Fossil SCM
Merge trunk into the merge-info-trunk branch.
Commit
1014ffb553117ca4113dcc5071d3389fe3c664eb5522dd634aab91d69b0e876e
Parent
c2b1c2c9fa3ef69…
109 files changed
+2
+1
+587
-525
+30
+379
+44
-18
+7
-6
+1147
-1059
+770
-596
+378
-147
+1614
-799
+49
-2
+2
-1
+1
-1
+27
-6
+2
-1
+2
-2
+4
-4
+1
-1
+9
-8
+16
+1
-1
+25
-5
+224
-71
+11
-5
+1
+333
+165
-103
+3
-1
+207
-85
+1
+1
+18
-3
+7
-1
+36
-7
+69
-30
+69
-30
+372
-55
+2
-1
+3
-3
+15
-8
+7
+4
-2
+13
-5
+9
-8
+12
-1
+30
-12
+6
-6
+10
-1
+28
-14
+144
-112
+3
-3
-1
+57
-23
+4
-3
+295
+57
+107
-18
+98
-5
+157
-97
+147
-27
+25
-9
+1
-1
+39
+3
-2
+1
-1
+240
-112
+126
-150
+36
-37
+124
-20
+1
+1
+1
-1
+5
-3
+33
+1
-1
+64
-3
+22
+216
-138
+6
-3
+7
-1
+4
-2
+2
-1
+2
-3
+1
-1
+63
-17
+54
-13
+367
-181
+2
-2
+1
-1
-346
+4
-3
+19
-15
+36
+3
+3
+5
+10
-20
+5
-1
+98
-24
+18
-3
+12
-5
+33
-5
+3
-3
+12
-2
+12
+14
~
.fossil-settings/crlf-glob
~
.fossil-settings/ignore-glob
~
Makefile.in
~
auto.def
~
autosetup/local.tcl
~
extsrc/linenoise-win32.c
~
extsrc/linenoise.c
~
extsrc/pikchr-worker.js
~
extsrc/pikchr.c
~
extsrc/pikchr.js
~
extsrc/pikchr.wasm
~
extsrc/shell.c
~
extsrc/sqlite3.c
~
extsrc/sqlite3.h
~
skins/default/css.txt
~
src/add.c
~
src/alerts.c
~
src/allrepo.c
~
src/backlink.c
~
src/bisect.c
~
src/blob.c
~
src/branch.c
~
src/builtin.c
~
src/cache.c
~
src/cgi.c
~
src/checkin.c
~
src/checkout.c
~
src/clone.c
~
src/color.c
~
src/comformat.c
~
src/configure.c
~
src/db.c
~
src/default.css
~
src/default.css
~
src/descendants.c
~
src/diff.c
~
src/diff.tcl
~
src/diffcmd.c
~
src/diffcmd.c
~
src/dispatch.c
~
src/doc.c
~
src/encode.c
~
src/export.c
~
src/file.c
~
src/finfo.c
~
src/fossil.page.pikchrshowasm.js
~
src/fossil.page.wikiedit.js
~
src/fuzz.c
~
src/graph.c
~
src/hook.c
~
src/http.c
~
src/http_ssl.c
~
src/info.c
~
src/interwiki.c
~
src/json_config.c
~
src/main.c
~
src/main.mk
~
src/manifest.c
~
src/markdown_html.c
~
src/name.c
~
src/patch.c
~
src/path.c
~
src/pqueue.c
~
src/printf.c
~
src/rebuild.c
~
src/regexp.c
~
src/repolist.c
~
src/schema.c
~
src/search.c
~
src/security_audit.c
~
src/setup.c
~
src/setupuser.c
~
src/sitemap.c
~
src/sqlcmd.c
~
src/stash.c
~
src/statrep.c
~
src/style.c
~
src/tar.c
~
src/terminal.c
~
src/th_main.c
~
src/timeline.c
~
src/tkt.c
~
src/tktsetup.c
~
src/unversioned.c
~
src/update.c
~
src/user.c
~
src/utf8.c
~
src/util.c
~
src/wiki.c
~
src/wikiformat.c
~
src/winhttp.c
~
src/zip.c
-
test/comment.test
~
tools/makemake.tcl
~
tools/mkindex.c
~
tools/randomize-js-names.tcl
~
win/build32.bat
~
win/build64.bat
~
www/alerts.md
~
www/build.wiki
~
www/cgi.wiki
~
www/changes.wiki
~
www/env-opts.md
~
www/fileformat.wiki
~
www/serverext.wiki
~
www/settings.wiki
~
www/th1.md
~
www/title-test.md
~
www/title-test.wiki
| --- .fossil-settings/crlf-glob | ||
| +++ .fossil-settings/crlf-glob | ||
| @@ -1,5 +1,7 @@ | ||
| 1 | 1 | compat/zlib/* |
| 2 | 2 | setup/fossil.iss |
| 3 | 3 | test/th1-docs-input.txt |
| 4 | 4 | test/th1-hooks-input.txt |
| 5 | +win/build32.bat | |
| 6 | +win/build64.bat | |
| 5 | 7 | win/buildmsvc.bat |
| 6 | 8 |
| --- .fossil-settings/crlf-glob | |
| +++ .fossil-settings/crlf-glob | |
| @@ -1,5 +1,7 @@ | |
| 1 | compat/zlib/* |
| 2 | setup/fossil.iss |
| 3 | test/th1-docs-input.txt |
| 4 | test/th1-hooks-input.txt |
| 5 | win/buildmsvc.bat |
| 6 |
| --- .fossil-settings/crlf-glob | |
| +++ .fossil-settings/crlf-glob | |
| @@ -1,5 +1,7 @@ | |
| 1 | compat/zlib/* |
| 2 | setup/fossil.iss |
| 3 | test/th1-docs-input.txt |
| 4 | test/th1-hooks-input.txt |
| 5 | win/build32.bat |
| 6 | win/build64.bat |
| 7 | win/buildmsvc.bat |
| 8 |
| --- .fossil-settings/ignore-glob | ||
| +++ .fossil-settings/ignore-glob | ||
| @@ -5,5 +5,6 @@ | ||
| 5 | 5 | fossil |
| 6 | 6 | fossil.exe |
| 7 | 7 | win/fossil.exe |
| 8 | 8 | *shell-see.* |
| 9 | 9 | *sqlite3-see.* |
| 10 | +bld | |
| 10 | 11 |
| --- .fossil-settings/ignore-glob | |
| +++ .fossil-settings/ignore-glob | |
| @@ -5,5 +5,6 @@ | |
| 5 | fossil |
| 6 | fossil.exe |
| 7 | win/fossil.exe |
| 8 | *shell-see.* |
| 9 | *sqlite3-see.* |
| 10 |
| --- .fossil-settings/ignore-glob | |
| +++ .fossil-settings/ignore-glob | |
| @@ -5,5 +5,6 @@ | |
| 5 | fossil |
| 6 | fossil.exe |
| 7 | win/fossil.exe |
| 8 | *shell-see.* |
| 9 | *sqlite3-see.* |
| 10 | bld |
| 11 |
No diff available
M
auto.def
+587
-525
| --- auto.def | ||
| +++ auto.def | ||
| @@ -36,17 +36,17 @@ | ||
| 36 | 36 | } |
| 37 | 37 | |
| 38 | 38 | # Update the minimum required SQLite version number here, and also |
| 39 | 39 | # in src/main.c near the sqlite3_libversion_number() call. Take care |
| 40 | 40 | # that both places agree! |
| 41 | -define MINIMUM_SQLITE_VERSION "3.46.0" | |
| 41 | +define MINIMUM_SQLITE_VERSION "3.49.0" | |
| 42 | 42 | |
| 43 | 43 | # This is useful for people wanting Fossil to use an external SQLite library |
| 44 | 44 | # to compare the one they have against the minimum required |
| 45 | 45 | if {[opt-bool print-minimum-sqlite-version]} { |
| 46 | - puts [get-define MINIMUM_SQLITE_VERSION] | |
| 47 | - exit 0 | |
| 46 | + puts [get-define MINIMUM_SQLITE_VERSION] | |
| 47 | + exit 0 | |
| 48 | 48 | } |
| 49 | 49 | |
| 50 | 50 | # Space characters have never been allowed in either the source |
| 51 | 51 | # tree nor the build directory. But the resulting error messages |
| 52 | 52 | # could be confusing. The following checks make the reason for the |
| @@ -67,21 +67,21 @@ | ||
| 67 | 67 | set outOfTreeBuild 1 |
| 68 | 68 | } |
| 69 | 69 | |
| 70 | 70 | # sqlite wants these types if possible |
| 71 | 71 | cc-with {-includes {stdint.h inttypes.h}} { |
| 72 | - cc-check-types uint32_t uint16_t int16_t uint8_t | |
| 72 | + cc-check-types uint32_t uint16_t int16_t uint8_t | |
| 73 | 73 | } |
| 74 | 74 | |
| 75 | 75 | # Use pread/pwrite system calls in place of seek + read/write if possible |
| 76 | 76 | define USE_PREAD [cc-check-functions pread] |
| 77 | 77 | |
| 78 | 78 | # If we have cscope here, we'll use it in the "tags" target |
| 79 | 79 | if {[cc-check-progs cscope]} { |
| 80 | - define COLLECT_CSCOPE_DATA "cscope -bR $::autosetup(srcdir)/src/*.\[ch\]" | |
| 80 | + define COLLECT_CSCOPE_DATA "cscope -bR $::autosetup(srcdir)/src/*.\[ch\]" | |
| 81 | 81 | } else { |
| 82 | - define COLLECT_CSCOPE_DATA "" | |
| 82 | + define COLLECT_CSCOPE_DATA "" | |
| 83 | 83 | } |
| 84 | 84 | |
| 85 | 85 | # Find tclsh for the test suite. |
| 86 | 86 | # |
| 87 | 87 | # We can't use jimsh for this: the test suite uses features of Tcl that |
| @@ -94,32 +94,32 @@ | ||
| 94 | 94 | # Ironically, this means we may right now be running under either jimsh0 |
| 95 | 95 | # or a version of tclsh that we find unsuitable below! |
| 96 | 96 | cc-check-progs tclsh |
| 97 | 97 | set hbtd /usr/local/Cellar/tcl-tk |
| 98 | 98 | if {[string equal false [get-define TCLSH]]} { |
| 99 | - msg-result "WARNING: 'make test' will not run here." | |
| 100 | -} else { | |
| 101 | - set v [exec sh -c "echo 'puts \$tcl_version' | tclsh"] | |
| 102 | - if {[expr {$v >= 8.6}]} { | |
| 103 | - msg-result "Found Tclsh version $v in the PATH." | |
| 104 | - define TCLSH tclsh | |
| 105 | - } elseif {[file isdirectory $hbtd]} { | |
| 106 | - # This is a macOS system with the Homebrew version of Tcl/Tk | |
| 107 | - # installed. Select the newest version. It won't normally be | |
| 108 | - # in the PATH to avoid shadowing /usr/bin/tclsh, and even if it | |
| 109 | - # were in the PATH, it's bad practice to put /usr/local/bin (the | |
| 110 | - # Homebrew default) ahead of /usr/bin, especially given that | |
| 111 | - # it's user-writeable by default with Homebrew. Thus, we can be | |
| 112 | - # pretty sure the only way to call it is with an absolute path. | |
| 113 | - set v [exec ls -tr $hbtd | tail -1] | |
| 114 | - set path "$hbtd/$v/bin/tclsh" | |
| 115 | - define TCLSH $path | |
| 116 | - msg-result "Using Homebrew Tcl/Tk version $path." | |
| 117 | - } else { | |
| 118 | - msg-result "WARNING: tclsh $v found; need >= 8.6 for 'make test'." | |
| 119 | - define TCLSH false ;# force "make test" failure via /usr/bin/false | |
| 120 | - } | |
| 99 | + msg-result "WARNING: 'make test' will not run here." | |
| 100 | +} else { | |
| 101 | + set v [exec sh -c "echo 'puts \$tcl_version' | tclsh"] | |
| 102 | + if {[expr {$v >= 8.6}]} { | |
| 103 | + msg-result "Found Tclsh version $v in the PATH." | |
| 104 | + define TCLSH tclsh | |
| 105 | + } elseif {[file isdirectory $hbtd]} { | |
| 106 | + # This is a macOS system with the Homebrew version of Tcl/Tk | |
| 107 | + # installed. Select the newest version. It won't normally be | |
| 108 | + # in the PATH to avoid shadowing /usr/bin/tclsh, and even if it | |
| 109 | + # were in the PATH, it's bad practice to put /usr/local/bin (the | |
| 110 | + # Homebrew default) ahead of /usr/bin, especially given that | |
| 111 | + # it's user-writeable by default with Homebrew. Thus, we can be | |
| 112 | + # pretty sure the only way to call it is with an absolute path. | |
| 113 | + set v [exec ls -tr $hbtd | tail -1] | |
| 114 | + set path "$hbtd/$v/bin/tclsh" | |
| 115 | + define TCLSH $path | |
| 116 | + msg-result "Using Homebrew Tcl/Tk version $path." | |
| 117 | + } else { | |
| 118 | + msg-result "WARNING: tclsh $v found; need >= 8.6 for 'make test'." | |
| 119 | + define TCLSH false ;# force "make test" failure via /usr/bin/false | |
| 120 | + } | |
| 121 | 121 | } |
| 122 | 122 | |
| 123 | 123 | define CFLAGS [get-env CFLAGS "-g -Os"] |
| 124 | 124 | define EXTRA_CFLAGS "-Wall" |
| 125 | 125 | define EXTRA_LDFLAGS "" |
| @@ -133,48 +133,48 @@ | ||
| 133 | 133 | # SQLITE_OPTIONS_EXT => build-dependent CFLAGS for sqlite3.c and shell.c |
| 134 | 134 | |
| 135 | 135 | # Maintain the C89/C90-style order of variable declarations before statements. |
| 136 | 136 | # Check if the compiler supports the respective warning flag. |
| 137 | 137 | if {[cctest -cflags -Wdeclaration-after-statement]} { |
| 138 | - define-append EXTRA_CFLAGS -Wdeclaration-after-statement | |
| 138 | + define-append EXTRA_CFLAGS -Wdeclaration-after-statement | |
| 139 | 139 | } |
| 140 | 140 | |
| 141 | 141 | |
| 142 | 142 | # This procedure is a customized version of "cc-check-function-in-lib", |
| 143 | 143 | # that does not modify the LIBS variable. Its use prevents prematurely |
| 144 | 144 | # pulling in libraries that will be added later anyhow (e.g. "-ldl"). |
| 145 | 145 | proc check-function-in-lib {function libs {otherlibs {}}} { |
| 146 | - if {[string length $otherlibs]} { | |
| 147 | - msg-checking "Checking for $function in $libs with $otherlibs..." | |
| 148 | - } else { | |
| 149 | - msg-checking "Checking for $function in $libs..." | |
| 150 | - } | |
| 151 | - set found 0 | |
| 152 | - cc-with [list -libs $otherlibs] { | |
| 153 | - if {[cctest_function $function]} { | |
| 154 | - msg-result "none needed" | |
| 155 | - define lib_$function "" | |
| 156 | - incr found | |
| 157 | - } else { | |
| 158 | - foreach lib $libs { | |
| 159 | - cc-with [list -libs -l$lib] { | |
| 160 | - if {[cctest_function $function]} { | |
| 161 | - msg-result -l$lib | |
| 162 | - define lib_$function -l$lib | |
| 163 | - incr found | |
| 164 | - break | |
| 165 | - } | |
| 166 | - } | |
| 167 | - } | |
| 168 | - } | |
| 169 | - } | |
| 170 | - if {$found} { | |
| 171 | - define [feature-define-name $function] | |
| 172 | - } else { | |
| 173 | - msg-result "no" | |
| 174 | - } | |
| 175 | - return $found | |
| 146 | + if {[string length $otherlibs]} { | |
| 147 | + msg-checking "Checking for $function in $libs with $otherlibs..." | |
| 148 | + } else { | |
| 149 | + msg-checking "Checking for $function in $libs..." | |
| 150 | + } | |
| 151 | + set found 0 | |
| 152 | + cc-with [list -libs $otherlibs] { | |
| 153 | + if {[cctest_function $function]} { | |
| 154 | + msg-result "none needed" | |
| 155 | + define lib_$function "" | |
| 156 | + incr found | |
| 157 | + } else { | |
| 158 | + foreach lib $libs { | |
| 159 | + cc-with [list -libs -l$lib] { | |
| 160 | + if {[cctest_function $function]} { | |
| 161 | + msg-result -l$lib | |
| 162 | + define lib_$function -l$lib | |
| 163 | + incr found | |
| 164 | + break | |
| 165 | + } | |
| 166 | + } | |
| 167 | + } | |
| 168 | + } | |
| 169 | + } | |
| 170 | + if {$found} { | |
| 171 | + define [feature-define-name $function] | |
| 172 | + } else { | |
| 173 | + msg-result "no" | |
| 174 | + } | |
| 175 | + return $found | |
| 176 | 176 | } |
| 177 | 177 | |
| 178 | 178 | if {![opt-bool internal-sqlite]} { |
| 179 | 179 | proc find_system_sqlite {} { |
| 180 | 180 | |
| @@ -219,17 +219,17 @@ | ||
| 219 | 219 | set sqlite-version [string cat "-D MINIMUM_SQLITE_VERSION=" [get-define MINIMUM_SQLITE_VERSION]] |
| 220 | 220 | lappend cmdline {*}[set sqlite-version] |
| 221 | 221 | set ok 1 |
| 222 | 222 | set err [catch {exec-with-stderr {*}$cmdline} result errinfo] |
| 223 | 223 | if {$err} { |
| 224 | - configlog "Failed: [join $cmdline]" | |
| 225 | - if {[string length $result]>0} {configlog $result} | |
| 226 | - configlog "============" | |
| 227 | - set ok 0 | |
| 224 | + configlog "Failed: [join $cmdline]" | |
| 225 | + if {[string length $result]>0} {configlog $result} | |
| 226 | + configlog "============" | |
| 227 | + set ok 0 | |
| 228 | 228 | } elseif {$::autosetup(debug)} { |
| 229 | - configlog "Compiled OK: [join $cmdline]" | |
| 230 | - configlog "============" | |
| 229 | + configlog "Compiled OK: [join $cmdline]" | |
| 230 | + configlog "============" | |
| 231 | 231 | } |
| 232 | 232 | if {!$ok} { |
| 233 | 233 | user-error "unable to compile SQLite compatibility test program" |
| 234 | 234 | } |
| 235 | 235 | set err [catch {exec-with-stderr ./conftest__} result errinfo] |
| @@ -251,475 +251,516 @@ | ||
| 251 | 251 | ![file exists "/dev/null"] |
| 252 | 252 | }] |
| 253 | 253 | } |
| 254 | 254 | |
| 255 | 255 | if {[is_mingw]} { |
| 256 | - define-append EXTRA_CFLAGS -DBROKEN_MINGW_CMDLINE | |
| 257 | - define-append LIBS -lkernel32 -lws2_32 | |
| 258 | -} else { | |
| 259 | - # | |
| 260 | - # NOTE: All platforms except MinGW should use the linenoise | |
| 261 | - # package. It is currently unsupported on Win32. | |
| 262 | - # | |
| 263 | - define USE_LINENOISE 1 | |
| 264 | -} | |
| 265 | - | |
| 266 | -if {[string match *-solaris* [get-define host]]} { | |
| 267 | - define-append EXTRA_CFLAGS {-D__EXTENSIONS__} | |
| 268 | -} | |
| 269 | - | |
| 270 | -if {[opt-bool fossil-debug]} { | |
| 271 | - define CFLAGS {-g -O0 -Wall} | |
| 272 | - define-append CFLAGS -DFOSSIL_DEBUG | |
| 273 | - msg-result "Debugging support enabled" | |
| 274 | -} | |
| 275 | - | |
| 276 | -if {[opt-bool no-opt]} { | |
| 277 | - define CFLAGS {-g -O0 -Wall} | |
| 278 | - msg-result "Builting without compiler optimization" | |
| 279 | - if {[opt-bool fossil-debug]} { | |
| 280 | - define-append CFLAGS -DFOSSIL_DEBUG | |
| 281 | - } | |
| 282 | -} | |
| 283 | - | |
| 284 | -if {[opt-bool with-mman]} { | |
| 285 | - define-append EXTRA_CFLAGS -DUSE_MMAN_H | |
| 286 | - define USE_MMAN_H 1 | |
| 287 | - msg-result "Enabling \"sys/mman.h\" support" | |
| 256 | + define-append EXTRA_CFLAGS -DBROKEN_MINGW_CMDLINE | |
| 257 | + define-append LIBS -lkernel32 -lws2_32 | |
| 258 | +} else { | |
| 259 | + # | |
| 260 | + # NOTE: All platforms except MinGW should use the linenoise | |
| 261 | + # package. It is currently unsupported on Win32. | |
| 262 | + # | |
| 263 | + define USE_LINENOISE 1 | |
| 264 | +} | |
| 265 | + | |
| 266 | +if {[string match *-solaris* [get-define host]]} { | |
| 267 | + define-append EXTRA_CFLAGS {-D__EXTENSIONS__} | |
| 268 | +} | |
| 269 | + | |
| 270 | +if {[opt-bool fossil-debug]} { | |
| 271 | + define CFLAGS {-g -O0 -Wall} | |
| 272 | + define-append CFLAGS -DFOSSIL_DEBUG | |
| 273 | + msg-result "Debugging support enabled" | |
| 274 | +} | |
| 275 | + | |
| 276 | +if {[opt-bool no-opt]} { | |
| 277 | + define CFLAGS {-g -O0 -Wall} | |
| 278 | + msg-result "Builting without compiler optimization" | |
| 279 | + if {[opt-bool fossil-debug]} { | |
| 280 | + define-append CFLAGS -DFOSSIL_DEBUG | |
| 281 | + } | |
| 282 | +} | |
| 283 | + | |
| 284 | +if {[opt-bool with-mman]} { | |
| 285 | + define-append EXTRA_CFLAGS -DUSE_MMAN_H | |
| 286 | + define USE_MMAN_H 1 | |
| 287 | + msg-result "Enabling \"sys/mman.h\" support" | |
| 288 | 288 | } |
| 289 | 289 | |
| 290 | 290 | if {[opt-bool with-see]} { |
| 291 | - define-append EXTRA_CFLAGS -DUSE_SEE | |
| 292 | - define USE_SEE 1 | |
| 293 | - define SQLITE3_ORIGIN 1 | |
| 294 | - msg-result "Enabling encryption support" | |
| 291 | + define-append EXTRA_CFLAGS -DUSE_SEE | |
| 292 | + define USE_SEE 1 | |
| 293 | + define SQLITE3_ORIGIN 1 | |
| 294 | + msg-result "Enabling encryption support" | |
| 295 | 295 | } |
| 296 | 296 | |
| 297 | 297 | if {[opt-bool json]} { |
| 298 | - # Reminder/FIXME (stephan): FOSSIL_ENABLE_JSON | |
| 299 | - # is required in the CFLAGS because json*.c | |
| 300 | - # have #ifdef guards around the whole file without | |
| 301 | - # reading config.h first. | |
| 302 | - define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_JSON | |
| 303 | - define FOSSIL_ENABLE_JSON | |
| 304 | - msg-result "JSON support enabled" | |
| 298 | + # Reminder/FIXME (stephan): FOSSIL_ENABLE_JSON | |
| 299 | + # is required in the CFLAGS because json*.c | |
| 300 | + # have #ifdef guards around the whole file without | |
| 301 | + # reading config.h first. | |
| 302 | + define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_JSON | |
| 303 | + define FOSSIL_ENABLE_JSON | |
| 304 | + msg-result "JSON support enabled" | |
| 305 | 305 | } |
| 306 | 306 | |
| 307 | 307 | if {[opt-bool with-exec-rel-paths]} { |
| 308 | - define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_EXEC_REL_PATHS | |
| 309 | - define FOSSIL_ENABLE_EXEC_REL_PATHS | |
| 310 | - msg-result "Relative paths in external diff/gdiff enabled" | |
| 308 | + define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_EXEC_REL_PATHS | |
| 309 | + define FOSSIL_ENABLE_EXEC_REL_PATHS | |
| 310 | + msg-result "Relative paths in external diff/gdiff enabled" | |
| 311 | 311 | } |
| 312 | 312 | |
| 313 | 313 | if {[opt-bool with-th1-docs]} { |
| 314 | - define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_TH1_DOCS | |
| 315 | - define FOSSIL_ENABLE_TH1_DOCS | |
| 316 | - msg-result "TH1 embedded documentation support enabled" | |
| 314 | + define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_TH1_DOCS | |
| 315 | + define FOSSIL_ENABLE_TH1_DOCS | |
| 316 | + msg-result "TH1 embedded documentation support enabled" | |
| 317 | 317 | } |
| 318 | 318 | |
| 319 | 319 | if {[opt-bool with-th1-hooks]} { |
| 320 | - define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_TH1_HOOKS | |
| 321 | - define FOSSIL_ENABLE_TH1_HOOKS | |
| 322 | - msg-result "TH1 hooks support enabled" | |
| 320 | + define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_TH1_HOOKS | |
| 321 | + define FOSSIL_ENABLE_TH1_HOOKS | |
| 322 | + msg-result "TH1 hooks support enabled" | |
| 323 | 323 | } |
| 324 | 324 | |
| 325 | 325 | #if {[opt-bool markdown]} { |
| 326 | 326 | # # no-op. Markdown is now enabled by default. |
| 327 | 327 | # msg-result "Markdown support enabled" |
| 328 | 328 | #} |
| 329 | 329 | |
| 330 | 330 | if {[opt-bool static]} { |
| 331 | - # XXX: This will not work on all systems. | |
| 332 | - define-append EXTRA_LDFLAGS -static | |
| 333 | - msg-result "Trying to link statically" | |
| 331 | + # XXX: This will not work on all systems. | |
| 332 | + define-append EXTRA_LDFLAGS -static | |
| 333 | + msg-result "Trying to link statically" | |
| 334 | 334 | } else { |
| 335 | - define-append EXTRA_CFLAGS -DFOSSIL_DYNAMIC_BUILD=1 | |
| 336 | - define FOSSIL_DYNAMIC_BUILD | |
| 335 | + define-append EXTRA_CFLAGS -DFOSSIL_DYNAMIC_BUILD=1 | |
| 336 | + define FOSSIL_DYNAMIC_BUILD | |
| 337 | 337 | } |
| 338 | 338 | |
| 339 | 339 | # Check for libraries that need to be sorted out early |
| 340 | 340 | cc-check-function-in-lib iconv iconv |
| 341 | 341 | |
| 342 | +cc-check-function-in-lib sin m | |
| 343 | +cc-check-function-in-lib dlopen dl | |
| 344 | + | |
| 342 | 345 | # Helper for OpenSSL checking |
| 343 | 346 | proc check-for-openssl {msg {cflags {}} {libs {-lssl -lcrypto -lpthread}}} { |
| 344 | - msg-checking "Checking for $msg..." | |
| 345 | - set rc 0 | |
| 346 | - if {[is_mingw]} { | |
| 347 | - lappend libs -lgdi32 -lwsock32 -lcrypt32 | |
| 348 | - } | |
| 349 | - if {[info exists ::zlib_lib]} { | |
| 350 | - lappend libs $::zlib_lib | |
| 351 | - } | |
| 352 | - msg-quiet cc-with [list -cflags $cflags -libs $libs] { | |
| 353 | - if {[cc-check-includes openssl/ssl.h] && \ | |
| 354 | - [cc-check-functions SSL_new]} { | |
| 355 | - incr rc | |
| 356 | - } | |
| 357 | - } | |
| 358 | - if {!$rc && ![is_mingw]} { | |
| 359 | - # On some systems, OpenSSL appears to require -ldl to link. | |
| 360 | - lappend libs -ldl | |
| 361 | - msg-quiet cc-with [list -cflags $cflags -libs $libs] { | |
| 362 | - if {[cc-check-includes openssl/ssl.h] && \ | |
| 363 | - [cc-check-functions SSL_new]} { | |
| 364 | - incr rc | |
| 365 | - } | |
| 366 | - } | |
| 367 | - } | |
| 368 | - if {$rc} { | |
| 369 | - msg-result "ok" | |
| 370 | - return 1 | |
| 371 | - } else { | |
| 372 | - msg-result "no" | |
| 373 | - return 0 | |
| 374 | - } | |
| 375 | -} | |
| 376 | - | |
| 347 | + msg-checking "Checking for $msg..." | |
| 348 | + set rc 0 | |
| 349 | + if {[is_mingw]} { | |
| 350 | + lappend libs -lgdi32 -lwsock32 -lcrypt32 | |
| 351 | + } | |
| 352 | + if {[info exists ::zlib_lib]} { | |
| 353 | + lappend libs $::zlib_lib | |
| 354 | + } | |
| 355 | + msg-quiet cc-with [list -cflags $cflags -libs $libs] { | |
| 356 | + if {[cc-check-includes openssl/ssl.h] && \ | |
| 357 | + [cc-check-functions SSL_new]} { | |
| 358 | + incr rc | |
| 359 | + } | |
| 360 | + } | |
| 361 | + if {!$rc && ![is_mingw]} { | |
| 362 | + # On some systems, OpenSSL appears to require -ldl to link. | |
| 363 | + lappend libs -ldl | |
| 364 | + msg-quiet cc-with [list -cflags $cflags -libs $libs] { | |
| 365 | + if {[cc-check-includes openssl/ssl.h] && \ | |
| 366 | + [cc-check-functions SSL_new]} { | |
| 367 | + incr rc | |
| 368 | + } | |
| 369 | + } | |
| 370 | + } | |
| 371 | + if {$rc} { | |
| 372 | + msg-result "ok" | |
| 373 | + return 1 | |
| 374 | + } else { | |
| 375 | + msg-result "no" | |
| 376 | + return 0 | |
| 377 | + } | |
| 378 | +} | |
| 379 | + | |
| 380 | +# | |
| 377 | 381 | # Check for zlib, using the given location if specified |
| 378 | -set zlibpath [opt-val with-zlib] | |
| 379 | -if {$zlibpath eq "tree"} { | |
| 380 | - set zlibdir [file dirname $autosetup(dir)]/compat/zlib | |
| 381 | - if {![file isdirectory $zlibdir]} { | |
| 382 | - user-error "The zlib in source tree directory does not exist" | |
| 383 | - } elseif { ([llength [glob -nocomplain -directory $zlibdir libz*]] == 0) } { | |
| 384 | - user-error "With --with-zlib=tree, $zlibdir must be configured and built first." | |
| 385 | - } | |
| 386 | - cc-with [list -cflags "-I$zlibdir -L$zlibdir"] | |
| 387 | - define-append EXTRA_CFLAGS -I$zlibdir | |
| 388 | - define-append LIBS $zlibdir/libz.a | |
| 389 | - set ::zlib_lib $zlibdir/libz.a | |
| 390 | - msg-result "Using zlib in source tree" | |
| 391 | -} else { | |
| 392 | - set cftry {""} | |
| 393 | - set ldtry {""} | |
| 394 | - if {$zlibpath ni {auto ""}} { | |
| 395 | - lappend cftry "-I$zlibpath" | |
| 396 | - lappend cftry "-I$zlibpath/include" | |
| 397 | - lappend ldtry "-L$zlibpath" | |
| 398 | - lappend ldtry "-L$zlibpath/lib" | |
| 399 | - } | |
| 400 | - | |
| 401 | - # Reverse the list of tests so we check most-specific to least, else | |
| 402 | - # platform devel files will shadow local --with-zlib overrides. | |
| 403 | - foreach c [lreverse $cftry] { | |
| 404 | - if {[cc-with [list -cflags $c] {cc-check-includes zlib.h}]} { | |
| 405 | - if {$c eq ""} { | |
| 406 | - msg-result "Found zlib.h in default include path" | |
| 407 | - } else { | |
| 408 | - define-append EXTRA_CFLAGS "$c" | |
| 409 | - msg-result "Found zlib.h via $c" | |
| 410 | - } | |
| 411 | - set cfound $c | |
| 412 | - break | |
| 413 | - } | |
| 414 | - } | |
| 415 | - if {![info exists cfound]} { | |
| 416 | - user-error "zlib.h not found; either install it or specify its location via --with-zlib" | |
| 417 | - } | |
| 418 | - foreach lcheck [lreverse $ldtry] { | |
| 419 | - if {[cc-with [list -cflags "$cfound $lcheck"] {check-function-in-lib inflateEnd z}]} { | |
| 420 | - if {$lcheck eq ""} { | |
| 421 | - msg-result "Linked to zlib via default library path" | |
| 422 | - } else { | |
| 423 | - define-append EXTRA_LDFLAGS "$lcheck" | |
| 424 | - msg-result "Linked to zlib via $lcheck" | |
| 425 | - } | |
| 426 | - if {![check-function-in-lib compressBound z]} { | |
| 427 | - puts "Notice: disabling zlib compression in the SQL shell" | |
| 428 | - define-append SQLITE_OPTIONS_EXT {-USQLITE_HAVE_ZLIB} | |
| 429 | - } | |
| 430 | - break | |
| 431 | - } | |
| 432 | - } | |
| 433 | - set ::zlib_lib -lz | |
| 434 | -} | |
| 435 | - | |
| 436 | -set ssldirs [opt-val with-openssl] | |
| 437 | -if {$ssldirs ne "none"} { | |
| 382 | +# | |
| 383 | +proc handle-zlib {} { | |
| 384 | + set ::zlibpath [opt-val with-zlib]; # used by downstream tcl tests | |
| 385 | + if {$::zlibpath eq "tree"} { | |
| 386 | + set ::zlibdir [file dirname $::autosetup(dir)]/compat/zlib | |
| 387 | + if {![file isdirectory $::zlibdir]} { | |
| 388 | + user-error "The zlib in source tree directory does not exist" | |
| 389 | + } elseif { ([llength [glob -nocomplain -directory $::zlibdir libz*]] == 0) } { | |
| 390 | + user-error "With --with-zlib=tree, $::zlibdir must be configured and built first." | |
| 391 | + } | |
| 392 | + cc-with [list -cflags "-I$::zlibdir -L$::zlibdir"] | |
| 393 | + define-append EXTRA_CFLAGS -I$::zlibdir | |
| 394 | + define-append LIBS $::zlibdir/libz.a | |
| 395 | + set ::zlib_lib $::zlibdir/libz.a | |
| 396 | + msg-result "Using zlib in source tree" | |
| 397 | + } else { | |
| 398 | + set cftry {""} | |
| 399 | + set ldtry {""} | |
| 400 | + if {$::zlibpath ni {auto ""}} { | |
| 401 | + lappend cftry "-I$::zlibpath" | |
| 402 | + lappend cftry "-I$::zlibpath/include" | |
| 403 | + lappend ldtry "-L$::zlibpath" | |
| 404 | + lappend ldtry "-L$::zlibpath/lib" | |
| 405 | + } | |
| 406 | + | |
| 407 | + # Reverse the list of tests so we check most-specific to least, else | |
| 408 | + # platform devel files will shadow local --with-zlib overrides. | |
| 409 | + foreach c [lreverse $cftry] { | |
| 410 | + if {[cc-with [list -cflags $c] {cc-check-includes zlib.h}]} { | |
| 411 | + if {$c eq ""} { | |
| 412 | + msg-result "Found zlib.h in default include path" | |
| 413 | + } else { | |
| 414 | + define-append EXTRA_CFLAGS "$c" | |
| 415 | + msg-result "Found zlib.h via $c" | |
| 416 | + } | |
| 417 | + set cfound $c | |
| 418 | + break | |
| 419 | + } | |
| 420 | + } | |
| 421 | + if {![info exists cfound]} { | |
| 422 | + user-error "zlib.h not found; either install it or specify its location via --with-zlib" | |
| 423 | + } | |
| 424 | + foreach lcheck [lreverse $ldtry] { | |
| 425 | + if {[cc-with [list -cflags "$cfound $lcheck"] {check-function-in-lib inflateEnd z}]} { | |
| 426 | + if {$lcheck eq ""} { | |
| 427 | + msg-result "Linked to zlib via default library path" | |
| 428 | + } else { | |
| 429 | + define-append EXTRA_LDFLAGS "$lcheck" | |
| 430 | + msg-result "Linked to zlib via $lcheck" | |
| 431 | + } | |
| 432 | + if {![check-function-in-lib compressBound z]} { | |
| 433 | + puts "Notice: disabling zlib compression in the SQL shell" | |
| 434 | + define-append SQLITE_OPTIONS_EXT {-USQLITE_HAVE_ZLIB} | |
| 435 | + } | |
| 436 | + break | |
| 437 | + } | |
| 438 | + } | |
| 439 | + set ::zlib_lib -lz | |
| 440 | + } | |
| 441 | +}; # handle-zlib | |
| 442 | +handle-zlib | |
| 443 | + | |
| 444 | +# | |
| 445 | +# Handle the --with-openssl flag and, incidentally, update @LIBS@ for | |
| 446 | +# zlib if openssl is _not_ used (if it is, we get zlib via libssl). | |
| 447 | +# | |
| 448 | +# This function should be called as late as possible in the configure | |
| 449 | +# script to avoid that its updates to @LIBS@ break tests which follow | |
| 450 | +# it when a custom local build of openssl is used, as discussed in | |
| 451 | +# <https://fossil-scm.org/forum/forumpost/15e3d9cdc137030c>. | |
| 452 | +# | |
| 453 | +proc handle-with-openssl {} { | |
| 454 | + set ssldirs [opt-val with-openssl] | |
| 455 | + if {$ssldirs ne "none"} { | |
| 438 | 456 | set found 0 |
| 439 | 457 | if {$ssldirs eq "tree"} { |
| 440 | - set ssldir [file dirname $autosetup(dir)]/compat/openssl | |
| 441 | - if {![file isdirectory $ssldir]} { | |
| 442 | - user-error "The OpenSSL in source tree directory does not exist" | |
| 443 | - } | |
| 444 | - set msg "openssl in $ssldir" | |
| 445 | - set cflags "-I$ssldir/include" | |
| 446 | - set ldflags "-L$ssldir" | |
| 447 | - set ssllibs "$ssldir/libssl.a $ssldir/libcrypto.a -lpthread" | |
| 448 | - set found [check-for-openssl "openssl in source tree" "$cflags $ldflags" $ssllibs] | |
| 458 | + set ssldir [file dirname $::autosetup(dir)]/compat/openssl | |
| 459 | + if {![file isdirectory $ssldir]} { | |
| 460 | + user-error "The OpenSSL in source tree directory does not exist" | |
| 461 | + } | |
| 462 | + set msg "openssl in $ssldir" | |
| 463 | + set cflags "-I$ssldir/include" | |
| 464 | + set ldflags "-L$ssldir" | |
| 465 | + set ssllibs "$ssldir/libssl.a $ssldir/libcrypto.a -lpthread" | |
| 466 | + set found [check-for-openssl "openssl in source tree" "$cflags $ldflags" $ssllibs] | |
| 449 | 467 | } else { |
| 450 | - if {$ssldirs in {auto ""}} { | |
| 451 | - catch { | |
| 452 | - set cflags [exec pkg-config openssl --cflags-only-I] | |
| 453 | - set ldflags [exec pkg-config openssl --libs-only-L] | |
| 454 | - set found [check-for-openssl "ssl via pkg-config" "$cflags $ldflags"] | |
| 455 | - } msg | |
| 456 | - if {!$found} { | |
| 457 | - set ssldirs "{} /usr/sfw /usr/local/ssl /usr/lib/ssl /usr/ssl \ | |
| 468 | + if {$ssldirs in {auto ""}} { | |
| 469 | + catch { | |
| 470 | + # TODO?: use autosetup's pkg-config support | |
| 471 | + set cflags [exec pkg-config openssl --cflags-only-I] | |
| 472 | + set ldflags [exec pkg-config openssl --libs-only-L] | |
| 473 | + set found [check-for-openssl "ssl via pkg-config" "$cflags $ldflags"] | |
| 474 | + } msg | |
| 475 | + if {!$found} { | |
| 476 | + set ssldirs "{} /usr/sfw /usr/local/ssl /usr/lib/ssl /usr/ssl \ | |
| 458 | 477 | /usr/pkg /usr/local /usr /usr/local/opt/openssl \ |
| 459 | 478 | /opt/homebrew/opt/openssl" |
| 460 | - } | |
| 461 | - } | |
| 462 | - if {!$found} { | |
| 463 | - foreach dir $ssldirs { | |
| 464 | - if {$dir eq ""} { | |
| 465 | - set msg "system openssl" | |
| 466 | - set cflags "" | |
| 467 | - set ldflags "" | |
| 468 | - } else { | |
| 469 | - set msg "openssl in $dir" | |
| 470 | - set cflags "-I$dir/include" | |
| 471 | - set ldflags "-L$dir/lib" | |
| 472 | - } | |
| 473 | - if {[check-for-openssl $msg "$cflags $ldflags"]} { | |
| 474 | - incr found | |
| 475 | - break | |
| 476 | - } | |
| 477 | - if {$dir ne ""} { | |
| 478 | - set ldflags "" | |
| 479 | - set msg "static build of openssl in $dir" | |
| 480 | - set ssllibs "$dir/libssl.a $dir/libcrypto.a -lpthread" | |
| 481 | - if {[check-for-openssl $msg "$cflags $ldflags" $ssllibs]} { | |
| 482 | - incr found | |
| 483 | - break | |
| 484 | - } | |
| 485 | - } | |
| 486 | - } | |
| 487 | - } | |
| 479 | + } | |
| 480 | + } | |
| 481 | + if {!$found} { | |
| 482 | + foreach dir $ssldirs { | |
| 483 | + if {$dir eq ""} { | |
| 484 | + set msg "system openssl" | |
| 485 | + set cflags "" | |
| 486 | + set ldflags "" | |
| 487 | + } else { | |
| 488 | + set msg "openssl in $dir" | |
| 489 | + set cflags "-I$dir/include" | |
| 490 | + if {[file readable $dir/libssl.a]} { | |
| 491 | + set ldflags -L$dir | |
| 492 | + } elseif {[file readable $dir/lib/libssl.a]} { | |
| 493 | + set ldflags -L$dir/lib | |
| 494 | + } elseif {[file isdir $dir/lib]} { | |
| 495 | + set ldflags "-L$dir -L$dir/lib" | |
| 496 | + } else { | |
| 497 | + set ldflags -L$dir | |
| 498 | + } | |
| 499 | + } | |
| 500 | + if {[check-for-openssl $msg "$cflags $ldflags"]} { | |
| 501 | + incr found | |
| 502 | + break | |
| 503 | + } | |
| 504 | + if {$dir ne ""} { | |
| 505 | + set ldflags "" | |
| 506 | + set msg "static build of openssl in $dir" | |
| 507 | + set ssllibs "$dir/libssl.a $dir/libcrypto.a -lpthread" | |
| 508 | + if {[check-for-openssl $msg "$cflags $ldflags" $ssllibs]} { | |
| 509 | + incr found | |
| 510 | + break | |
| 511 | + } | |
| 512 | + # This test should arguably fail here if --with-openssl=X | |
| 513 | + # points to an invalid X. | |
| 514 | + } | |
| 515 | + } | |
| 516 | + } | |
| 488 | 517 | } |
| 489 | 518 | if {$found} { |
| 490 | - define FOSSIL_ENABLE_SSL | |
| 491 | - define-append EXTRA_CFLAGS $cflags | |
| 492 | - define-append EXTRA_LDFLAGS $ldflags | |
| 493 | - define-append CFLAGS $cflags | |
| 494 | - define-append LDFLAGS $ldflags | |
| 495 | - if {[info exists ssllibs]} { | |
| 496 | - define-append LIBS $ssllibs | |
| 497 | - } else { | |
| 498 | - define-append LIBS -lssl -lcrypto | |
| 499 | - } | |
| 500 | - if {[info exists ::zlib_lib]} { | |
| 501 | - define-append LIBS $::zlib_lib | |
| 502 | - } | |
| 503 | - if {[is_mingw]} { | |
| 504 | - define-append LIBS -lgdi32 -lwsock32 -lcrypt32 | |
| 505 | - } | |
| 506 | - msg-result "HTTPS support enabled" | |
| 507 | - | |
| 508 | - # Silence OpenSSL deprecation warnings on Mac OS X 10.7. | |
| 509 | - if {[string match *-darwin* [get-define host]]} { | |
| 510 | - if {[cctest -cflags {-Wdeprecated-declarations}]} { | |
| 511 | - define-append EXTRA_CFLAGS -Wdeprecated-declarations | |
| 512 | - } | |
| 513 | - } | |
| 514 | - } else { | |
| 515 | - user-error "OpenSSL not found. Consider --with-openssl=none to disable HTTPS support" | |
| 516 | - } | |
| 517 | -} else { | |
| 518 | - if {[info exists ::zlib_lib]} { | |
| 519 | - define-append LIBS $::zlib_lib | |
| 520 | - } | |
| 521 | -} | |
| 522 | - | |
| 523 | - | |
| 519 | + define FOSSIL_ENABLE_SSL | |
| 520 | + define-append EXTRA_CFLAGS $cflags | |
| 521 | + define-append EXTRA_LDFLAGS $ldflags | |
| 522 | + if {[info exists ssllibs]} { | |
| 523 | + define-append LIBS $ssllibs | |
| 524 | + } else { | |
| 525 | + define-append LIBS -lssl -lcrypto | |
| 526 | + } | |
| 527 | + if {[info exists ::zlib_lib]} { | |
| 528 | + define-append LIBS $::zlib_lib | |
| 529 | + } | |
| 530 | + if {[is_mingw]} { | |
| 531 | + define-append LIBS -lgdi32 -lwsock32 -lcrypt32 | |
| 532 | + } | |
| 533 | + msg-result "HTTPS support enabled" | |
| 534 | + | |
| 535 | + # Silence OpenSSL deprecation warnings on Mac OS X 10.7. | |
| 536 | + if {[string match *-darwin* [get-define host]]} { | |
| 537 | + if {[cctest -cflags {-Wdeprecated-declarations}]} { | |
| 538 | + define-append EXTRA_CFLAGS -Wdeprecated-declarations | |
| 539 | + } | |
| 540 | + } | |
| 541 | + } else { | |
| 542 | + user-error "OpenSSL not found. Consider --with-openssl=none to disable HTTPS support" | |
| 543 | + } | |
| 544 | + } else { | |
| 545 | + if {[info exists ::zlib_lib]} { | |
| 546 | + define-append LIBS $::zlib_lib | |
| 547 | + } | |
| 548 | + } | |
| 549 | +}; # handle-with-openssl | |
| 550 | + | |
| 551 | +# | |
| 552 | +# CFLAGS_INCLUDE is ONLY for -I... flags and their order is | |
| 553 | +# significant so that --with-sqlite=PATH's header can shadow our | |
| 554 | +# own. <s>One caveat with this is that we cannot point | |
| 555 | +# --with-sqlite=PATH to the root of sqlite3's own build tree because | |
| 556 | +# that dir has a config.h which ends up shadowing src/config.h, | |
| 557 | +# breaking our build.</s> (That is no longer true: that that config.h | |
| 558 | +# was renamed to sqlite_cfg.h at some point.) | |
| 559 | +# | |
| 560 | +define CFLAGS_INCLUDE {} | |
| 524 | 561 | |
| 525 | 562 | ######################################################################## |
| 526 | 563 | # --with-sqlite=PATH checks for the first it finds of the following... |
| 527 | 564 | # - PATH/sqlite3.c and PATH/sqlite3.h |
| 528 | 565 | # - PATH/sqlite3.o (and assumes sqlite3.h is with it) |
| 529 | 566 | # - PATH/lib/libsqlite3* and PATH/include/sqlite3.h |
| 530 | -define CFLAGS_INCLUDE {} | |
| 531 | -# ^^^ CFLAGS_INCLUDE is ONLY for -I... flags and their order is | |
| 532 | -# significant so that --with-sqlite=PATH's header can shadow our | |
| 533 | -# own. One caveat with this is that we cannot point --with-sqlite=PATH | |
| 534 | -# to the root of sqlite3's own build tree because that dir has a | |
| 535 | -# config.h which ends up shadowing src/config.h, breaking our build. | |
| 536 | -set sq3path [opt-val with-sqlite] | |
| 537 | -define SQLITE3_SRC.2 {} | |
| 538 | -define SQLITE3_OBJ.2 {} | |
| 539 | -define SQLITE3_SHELL_SRC.2 {$(SQLITE3_SHELL_SRC.0)} | |
| 540 | -if {$sq3path in {tree ""}} { | |
| 541 | - msg-result "Using sqlite3.c from this source tree." | |
| 542 | -} else { | |
| 543 | - # SQLITE3_ORIGIN: | |
| 544 | - # 0 = local source tree | |
| 545 | - # 1 = use external lib or sqlite3.o | |
| 546 | - # 2 = use external sqlite3.c and (if found) shell.c | |
| 547 | - define USE_SYSTEM_SQLITE 1 | |
| 548 | - define SQLITE3_ORIGIN 2 | |
| 549 | - if {$sq3path != "auto"} { | |
| 550 | - if {([file exists $sq3path/sqlite3.c]) && \ | |
| 551 | - ([file exists $sq3path/sqlite3.h]) } { | |
| 552 | - # Prefer sqlite3.[ch] if found. | |
| 553 | - define SQLITE3_SRC.2 $sq3path/sqlite3.c | |
| 554 | - define SQLITE3_OBJ.2 {$(SQLITE3_OBJ.0)} | |
| 555 | - define USE_SYSTEM_SQLITE 2 | |
| 556 | - define SQLITE3_ORIGIN 2 | |
| 557 | - if {[file exists $sq3path/shell.c]} { | |
| 558 | - define SQLITE3_SHELL_SRC.2 $sq3path/shell.c | |
| 559 | - } | |
| 560 | - define-append CFLAGS_INCLUDE -I$sq3path | |
| 561 | - define-append EXTRA_LDFLAGS -lpthread | |
| 562 | - # ^^^ additional -lXXX flags are conservative estimates | |
| 563 | - msg-result "Using sqlite3.c and sqlite3.h from $sq3path" | |
| 564 | - } elseif {[file exists $sq3path/sqlite3.o]} { | |
| 565 | - # Use sqlite3.o if found. | |
| 566 | - define SQLITE3_OBJ.2 $sq3path/sqlite3.o | |
| 567 | - define-append CFLAGS_INCLUDE -I$sq3path | |
| 568 | - define-append EXTRA_LDFLAGS $sq3path/sqlite3.o -lpthread | |
| 569 | - # ^^^ additional -lXXX flags are conservative estimates | |
| 570 | - msg-result "Using sqlite3.o from $sq3path" | |
| 571 | - } elseif { ([llength [glob -nocomplain -directory $sq3path/lib libsqlite3*]] != 0) \ | |
| 572 | - && ([file exists $sq3path/include/sqlite3.h]) } { | |
| 573 | - # e.g. --with-sqlite=/usr/local. Try $sq3path/lib/libsqlite3* | |
| 574 | - # and $sq3path/include/sqlite3.h | |
| 575 | - define-append CFLAGS_INCLUDE -I$sq3path/include | |
| 576 | - define-append EXTRA_LDFLAGS -L$sq3path/lib -lsqlite3 -lpthread | |
| 577 | - # ^^^ additional -lXXX flags are conservative estimates | |
| 578 | - msg-result "Using -lsqlite3 from $sq3path" | |
| 579 | - } else { | |
| 580 | - # Assume $sq3path holds both the lib and header | |
| 581 | - cc-with [list -cflags "-I$sq3path -L$sq3path"] | |
| 582 | - define-append CFLAGS_INCLUDE -I$sq3path | |
| 583 | - define-append EXTRA_LDFLAGS -L$sq3path -lsqlite3 -lpthread | |
| 584 | - # ^^^ additional -lXXX flags are conservative estimates | |
| 585 | - msg-result "Using -lsqlite3 from $sq3path" | |
| 586 | - } | |
| 587 | - } elseif {![cc-check-includes sqlite3.h] || ![check-function-in-lib sqlite3_open_v2 sqlite3]} { | |
| 588 | - user-error "libsqlite3 not found please install it or specify the location with --with-sqlite" | |
| 589 | - } | |
| 590 | -} | |
| 591 | -define-append CFLAGS_INCLUDE {-I. -I$(SRCDIR) -I$(SRCDIR_extsrc)} | |
| 592 | - | |
| 593 | -set tclpath [opt-val with-tcl] | |
| 594 | -if {$tclpath ne ""} { | |
| 595 | - set tclprivatestubs [opt-bool with-tcl-private-stubs] | |
| 596 | - # Note parse-tclconfig-sh is in autosetup/local.tcl | |
| 597 | - if {$tclpath eq "1"} { | |
| 598 | - set tcldir [file dirname $autosetup(dir)]/compat/tcl-8.6 | |
| 599 | - if {$tclprivatestubs} { | |
| 600 | - set tclconfig(TCL_INCLUDE_SPEC) -I$tcldir/generic | |
| 601 | - set tclconfig(TCL_VERSION) {Private Stubs} | |
| 602 | - set tclconfig(TCL_PATCH_LEVEL) {} | |
| 603 | - set tclconfig(TCL_PREFIX) $tcldir | |
| 604 | - set tclconfig(TCL_LD_FLAGS) { } | |
| 605 | - } else { | |
| 606 | - # Use the system Tcl. Look in some likely places. | |
| 607 | - array set tclconfig [parse-tclconfig-sh \ | |
| 608 | - $tcldir/unix $tcldir/win \ | |
| 609 | - /usr /usr/local /usr/share /opt/local] | |
| 610 | - set msg "on your system" | |
| 611 | - } | |
| 612 | - } else { | |
| 613 | - array set tclconfig [parse-tclconfig-sh $tclpath] | |
| 614 | - set msg "at $tclpath" | |
| 615 | - } | |
| 616 | - if {[opt-bool static]} { | |
| 617 | - set tclconfig(TCL_LD_FLAGS) { } | |
| 618 | - } | |
| 619 | - if {![info exists tclconfig(TCL_INCLUDE_SPEC)]} { | |
| 620 | - user-error "Cannot find Tcl $msg" | |
| 621 | - } | |
| 622 | - set tclstubs [opt-bool with-tcl-stubs] | |
| 623 | - if {$tclprivatestubs} { | |
| 624 | - define FOSSIL_ENABLE_TCL_PRIVATE_STUBS | |
| 625 | - define USE_TCL_STUBS | |
| 626 | - } elseif {$tclstubs && $tclconfig(TCL_SUPPORTS_STUBS)} { | |
| 627 | - set libs "$tclconfig(TCL_STUB_LIB_SPEC)" | |
| 628 | - define FOSSIL_ENABLE_TCL_STUBS | |
| 629 | - define USE_TCL_STUBS | |
| 630 | - } else { | |
| 631 | - set libs "$tclconfig(TCL_LIB_SPEC) $tclconfig(TCL_LIBS)" | |
| 632 | - } | |
| 633 | - set cflags $tclconfig(TCL_INCLUDE_SPEC) | |
| 634 | - if {!$tclprivatestubs} { | |
| 635 | - set foundtcl 0; # Did we find a working Tcl library? | |
| 636 | - cc-with [list -cflags $cflags -libs $libs] { | |
| 637 | - if {$tclstubs} { | |
| 638 | - if {[cc-check-functions Tcl_InitStubs]} { | |
| 639 | - set foundtcl 1 | |
| 640 | - } | |
| 641 | - } else { | |
| 642 | - if {[cc-check-functions Tcl_CreateInterp]} { | |
| 643 | - set foundtcl 1 | |
| 644 | - } | |
| 645 | - } | |
| 646 | - } | |
| 647 | - if {!$foundtcl && [string match *-lieee* $libs]} { | |
| 648 | - # On some systems, using "-lieee" from TCL_LIB_SPEC appears | |
| 649 | - # to cause issues. | |
| 650 | - msg-result "Removing \"-lieee\" and retrying for Tcl..." | |
| 651 | - set libs [string map [list -lieee ""] $libs] | |
| 652 | - cc-with [list -cflags $cflags -libs $libs] { | |
| 653 | - if {$tclstubs} { | |
| 654 | - if {[cc-check-functions Tcl_InitStubs]} { | |
| 655 | - set foundtcl 1 | |
| 656 | - } | |
| 657 | - } else { | |
| 658 | - if {[cc-check-functions Tcl_CreateInterp]} { | |
| 659 | - set foundtcl 1 | |
| 660 | - } | |
| 661 | - } | |
| 662 | - } | |
| 663 | - } | |
| 664 | - if {!$foundtcl && ![string match *-lpthread* $libs]} { | |
| 665 | - # On some systems, TCL_LIB_SPEC appears to be missing | |
| 666 | - # "-lpthread". Try adding it. | |
| 667 | - msg-result "Adding \"-lpthread\" and retrying for Tcl..." | |
| 668 | - set libs "$libs -lpthread" | |
| 669 | - cc-with [list -cflags $cflags -libs $libs] { | |
| 670 | - if {$tclstubs} { | |
| 671 | - if {[cc-check-functions Tcl_InitStubs]} { | |
| 672 | - set foundtcl 1 | |
| 673 | - } | |
| 674 | - } else { | |
| 675 | - if {[cc-check-functions Tcl_CreateInterp]} { | |
| 676 | - set foundtcl 1 | |
| 677 | - } | |
| 678 | - } | |
| 679 | - } | |
| 680 | - } | |
| 681 | - if {!$foundtcl} { | |
| 682 | - if {$tclstubs} { | |
| 683 | - user-error "Cannot find a usable Tcl stubs library $msg" | |
| 684 | - } else { | |
| 685 | - user-error "Cannot find a usable Tcl library $msg" | |
| 686 | - } | |
| 687 | - } | |
| 688 | - } | |
| 689 | - set version $tclconfig(TCL_VERSION)$tclconfig(TCL_PATCH_LEVEL) | |
| 690 | - msg-result "Found Tcl $version at $tclconfig(TCL_PREFIX)" | |
| 691 | - if {!$tclprivatestubs} { | |
| 692 | - define-append LIBS $libs | |
| 693 | - } | |
| 694 | - define-append EXTRA_CFLAGS $cflags | |
| 695 | - define-append CFLAGS $cflags | |
| 696 | - if {[info exists zlibpath] && $zlibpath eq "tree"} { | |
| 697 | - # | |
| 698 | - # NOTE: When using zlib in the source tree, prevent Tcl from | |
| 699 | - # pulling in the system one. | |
| 700 | - # | |
| 701 | - set tclconfig(TCL_LD_FLAGS) [string map [list -lz ""] \ | |
| 702 | - $tclconfig(TCL_LD_FLAGS)] | |
| 703 | - } | |
| 704 | - # | |
| 705 | - # NOTE: Remove "-ldl" from the TCL_LD_FLAGS because it will be | |
| 706 | - # be checked for near the bottom of this file. | |
| 707 | - # | |
| 708 | - set tclconfig(TCL_LD_FLAGS) [string map [list -ldl ""] \ | |
| 709 | - $tclconfig(TCL_LD_FLAGS)] | |
| 710 | - define-append EXTRA_LDFLAGS $tclconfig(TCL_LD_FLAGS) | |
| 711 | - define FOSSIL_ENABLE_TCL | |
| 712 | -} | |
| 567 | +proc handle-with-sqlite {} { | |
| 568 | + set sq3path [opt-val with-sqlite] | |
| 569 | + define SQLITE3_SRC.2 {} | |
| 570 | + define SQLITE3_OBJ.2 {} | |
| 571 | + define SQLITE3_SHELL_SRC.2 {$(SQLITE3_SHELL_SRC.0)} | |
| 572 | + if {$sq3path in {tree ""}} { | |
| 573 | + msg-result "Using sqlite3.c from this source tree." | |
| 574 | + } else { | |
| 575 | + # SQLITE3_ORIGIN: | |
| 576 | + # 0 = local source tree | |
| 577 | + # 1 = use external lib or sqlite3.o | |
| 578 | + # 2 = use external sqlite3.c and (if found) shell.c | |
| 579 | + define USE_SYSTEM_SQLITE 1 | |
| 580 | + define SQLITE3_ORIGIN 2 | |
| 581 | + if {$sq3path != "auto"} { | |
| 582 | + if {([file exists $sq3path/sqlite3.c]) && | |
| 583 | + ([file exists $sq3path/sqlite3.h]) } { | |
| 584 | + # Prefer sqlite3.[ch] if found. | |
| 585 | + define SQLITE3_SRC.2 $sq3path/sqlite3.c | |
| 586 | + define SQLITE3_OBJ.2 {$(SQLITE3_OBJ.0)} | |
| 587 | + define USE_SYSTEM_SQLITE 2 | |
| 588 | + define SQLITE3_ORIGIN 2 | |
| 589 | + if {[file exists $sq3path/shell.c]} { | |
| 590 | + define SQLITE3_SHELL_SRC.2 $sq3path/shell.c | |
| 591 | + } | |
| 592 | + define-append CFLAGS_INCLUDE -I$sq3path | |
| 593 | + define-append EXTRA_LDFLAGS -lpthread | |
| 594 | + # ^^^ additional -lXXX flags are conservative estimates | |
| 595 | + msg-result "Using sqlite3.c and sqlite3.h from $sq3path" | |
| 596 | + } elseif {[file exists $sq3path/sqlite3.o]} { | |
| 597 | + # Use sqlite3.o if found. | |
| 598 | + define SQLITE3_OBJ.2 $sq3path/sqlite3.o | |
| 599 | + define-append CFLAGS_INCLUDE -I$sq3path | |
| 600 | + define-append EXTRA_LDFLAGS $sq3path/sqlite3.o -lpthread | |
| 601 | + # ^^^ additional -lXXX flags are conservative estimates | |
| 602 | + msg-result "Using sqlite3.o from $sq3path" | |
| 603 | + } elseif { ([llength [glob -nocomplain -directory $sq3path/lib libsqlite3*]] != 0) \ | |
| 604 | + && ([file exists $sq3path/include/sqlite3.h]) } { | |
| 605 | + # e.g. --with-sqlite=/usr/local. Try $sq3path/lib/libsqlite3* | |
| 606 | + # and $sq3path/include/sqlite3.h | |
| 607 | + define-append CFLAGS_INCLUDE -I$sq3path/include | |
| 608 | + define-append EXTRA_LDFLAGS -L$sq3path/lib -lsqlite3 -lpthread | |
| 609 | + # ^^^ additional -lXXX flags are conservative estimates | |
| 610 | + msg-result "Using -lsqlite3 from $sq3path" | |
| 611 | + } else { | |
| 612 | + # Assume $sq3path holds both the lib and header | |
| 613 | + cc-with [list -cflags "-I$sq3path -L$sq3path"] | |
| 614 | + define-append CFLAGS_INCLUDE -I$sq3path | |
| 615 | + define-append EXTRA_LDFLAGS -L$sq3path -lsqlite3 -lpthread | |
| 616 | + # ^^^ additional -lXXX flags are conservative estimates | |
| 617 | + msg-result "Using -lsqlite3 from $sq3path" | |
| 618 | + } | |
| 619 | + } elseif {![cc-check-includes sqlite3.h] || ![check-function-in-lib sqlite3_open_v2 sqlite3]} { | |
| 620 | + user-error "libsqlite3 not found please install it or specify the location with --with-sqlite" | |
| 621 | + } | |
| 622 | + } | |
| 623 | +}; # handle-with-sqlite | |
| 624 | +handle-with-sqlite | |
| 625 | +define-append CFLAGS_INCLUDE {-I. -I$(SRCDIR) -I$(SRCDIR_extsrc)}; # must be after handle-with-sqlite | |
| 626 | + | |
| 627 | +# | |
| 628 | +# Handle the --with-tcl flag. | |
| 629 | +# | |
| 630 | +proc handle-with-tcl {} { | |
| 631 | + set tclpath [opt-val with-tcl] | |
| 632 | + if {$tclpath eq ""} { | |
| 633 | + return | |
| 634 | + } | |
| 635 | + set tclprivatestubs [opt-bool with-tcl-private-stubs] | |
| 636 | + # Note parse-tclconfig-sh is in autosetup/local.tcl | |
| 637 | + if {$tclpath eq "1"} { | |
| 638 | + set tcldir [file dirname $::autosetup(dir)]/compat/tcl-8.6 | |
| 639 | + if {$tclprivatestubs} { | |
| 640 | + set tclconfig(TCL_INCLUDE_SPEC) -I$tcldir/generic | |
| 641 | + set tclconfig(TCL_VERSION) {Private Stubs} | |
| 642 | + set tclconfig(TCL_PATCH_LEVEL) {} | |
| 643 | + set tclconfig(TCL_PREFIX) $tcldir | |
| 644 | + set tclconfig(TCL_LD_FLAGS) { } | |
| 645 | + } else { | |
| 646 | + # Use the system Tcl. Look in some likely places. | |
| 647 | + array set tclconfig [parse-tclconfig-sh \ | |
| 648 | + $tcldir/unix $tcldir/win \ | |
| 649 | + /usr /usr/local /usr/share /opt/local] | |
| 650 | + set msg "on your system" | |
| 651 | + } | |
| 652 | + } else { | |
| 653 | + array set tclconfig [parse-tclconfig-sh $tclpath] | |
| 654 | + set msg "at $tclpath" | |
| 655 | + } | |
| 656 | + if {[opt-bool static]} { | |
| 657 | + set tclconfig(TCL_LD_FLAGS) { } | |
| 658 | + } | |
| 659 | + if {![info exists tclconfig(TCL_INCLUDE_SPEC)]} { | |
| 660 | + user-error "Cannot find Tcl $msg" | |
| 661 | + } | |
| 662 | + set tclstubs [opt-bool with-tcl-stubs] | |
| 663 | + if {$tclprivatestubs} { | |
| 664 | + define FOSSIL_ENABLE_TCL_PRIVATE_STUBS | |
| 665 | + define USE_TCL_STUBS | |
| 666 | + } elseif {$tclstubs && $tclconfig(TCL_SUPPORTS_STUBS)} { | |
| 667 | + set libs "$tclconfig(TCL_STUB_LIB_SPEC)" | |
| 668 | + define FOSSIL_ENABLE_TCL_STUBS | |
| 669 | + define USE_TCL_STUBS | |
| 670 | + } else { | |
| 671 | + set libs "$tclconfig(TCL_LIB_SPEC) $tclconfig(TCL_LIBS)" | |
| 672 | + } | |
| 673 | + set cflags $tclconfig(TCL_INCLUDE_SPEC) | |
| 674 | + if {!$tclprivatestubs} { | |
| 675 | + set foundtcl 0; # Did we find a working Tcl library? | |
| 676 | + cc-with [list -cflags $cflags -libs $libs] { | |
| 677 | + if {$tclstubs} { | |
| 678 | + if {[cc-check-functions Tcl_InitStubs]} { | |
| 679 | + set foundtcl 1 | |
| 680 | + } | |
| 681 | + } else { | |
| 682 | + if {[cc-check-functions Tcl_CreateInterp]} { | |
| 683 | + set foundtcl 1 | |
| 684 | + } | |
| 685 | + } | |
| 686 | + } | |
| 687 | + if {!$foundtcl && [string match *-lieee* $libs]} { | |
| 688 | + # On some systems, using "-lieee" from TCL_LIB_SPEC appears | |
| 689 | + # to cause issues. | |
| 690 | + msg-result "Removing \"-lieee\" and retrying for Tcl..." | |
| 691 | + set libs [string map [list -lieee ""] $libs] | |
| 692 | + cc-with [list -cflags $cflags -libs $libs] { | |
| 693 | + if {$tclstubs} { | |
| 694 | + if {[cc-check-functions Tcl_InitStubs]} { | |
| 695 | + set foundtcl 1 | |
| 696 | + } | |
| 697 | + } else { | |
| 698 | + if {[cc-check-functions Tcl_CreateInterp]} { | |
| 699 | + set foundtcl 1 | |
| 700 | + } | |
| 701 | + } | |
| 702 | + } | |
| 703 | + } | |
| 704 | + if {!$foundtcl && ![string match *-lpthread* $libs]} { | |
| 705 | + # On some systems, TCL_LIB_SPEC appears to be missing | |
| 706 | + # "-lpthread". Try adding it. | |
| 707 | + msg-result "Adding \"-lpthread\" and retrying for Tcl..." | |
| 708 | + set libs "$libs -lpthread" | |
| 709 | + cc-with [list -cflags $cflags -libs $libs] { | |
| 710 | + if {$tclstubs} { | |
| 711 | + if {[cc-check-functions Tcl_InitStubs]} { | |
| 712 | + set foundtcl 1 | |
| 713 | + } | |
| 714 | + } else { | |
| 715 | + if {[cc-check-functions Tcl_CreateInterp]} { | |
| 716 | + set foundtcl 1 | |
| 717 | + } | |
| 718 | + } | |
| 719 | + } | |
| 720 | + } | |
| 721 | + if {!$foundtcl} { | |
| 722 | + if {$tclstubs} { | |
| 723 | + user-error "Cannot find a usable Tcl stubs library $msg" | |
| 724 | + } else { | |
| 725 | + user-error "Cannot find a usable Tcl library $msg" | |
| 726 | + } | |
| 727 | + } | |
| 728 | + } | |
| 729 | + set version $tclconfig(TCL_VERSION)$tclconfig(TCL_PATCH_LEVEL) | |
| 730 | + msg-result "Found Tcl $version at $tclconfig(TCL_PREFIX)" | |
| 731 | + if {!$tclprivatestubs} { | |
| 732 | + define-append LIBS $libs | |
| 733 | + } | |
| 734 | + define-append EXTRA_CFLAGS $cflags | |
| 735 | + define-append CFLAGS $cflags | |
| 736 | + if {[info exists ::zlibpath] && $::zlibpath eq "tree"} { | |
| 737 | + # | |
| 738 | + # NOTE: When using zlib in the source tree, prevent Tcl from | |
| 739 | + # pulling in the system one. | |
| 740 | + # | |
| 741 | + set tclconfig(TCL_LD_FLAGS) [string map [list -lz ""] \ | |
| 742 | + $tclconfig(TCL_LD_FLAGS)] | |
| 743 | + } | |
| 744 | + # | |
| 745 | + # NOTE: Remove "-ldl" from the TCL_LD_FLAGS because it will be | |
| 746 | + # be checked for near the bottom of this file. | |
| 747 | + # | |
| 748 | + set tclconfig(TCL_LD_FLAGS) [string map [list -ldl ""] \ | |
| 749 | + $tclconfig(TCL_LD_FLAGS)] | |
| 750 | + define-append EXTRA_LDFLAGS $tclconfig(TCL_LD_FLAGS) | |
| 751 | + define FOSSIL_ENABLE_TCL | |
| 752 | +}; # handle-with-tcl | |
| 753 | +handle-with-tcl | |
| 713 | 754 | |
| 714 | 755 | # Network functions require libraries on some systems |
| 715 | 756 | cc-check-function-in-lib gethostbyname nsl |
| 716 | 757 | if {![cc-check-function-in-lib socket {socket network}]} { |
| 717 | - # Last resort, may be Windows | |
| 718 | - if {[is_mingw]} { | |
| 719 | - define-append LIBS -lwsock32 | |
| 720 | - } | |
| 758 | + # Last resort, may be Windows | |
| 759 | + if {[is_mingw]} { | |
| 760 | + define-append LIBS -lwsock32 | |
| 761 | + } | |
| 721 | 762 | } |
| 722 | 763 | |
| 723 | 764 | # Some systems (ex: SunOS) require -lrt in order to use nanosleep |
| 724 | 765 | cc-check-function-in-lib nanosleep rt |
| 725 | 766 | |
| @@ -734,11 +775,11 @@ | ||
| 734 | 775 | [cc-check-function-in-lib __ns_name_uncompress {bind resolv}]) && |
| 735 | 776 | ([cc-check-function-in-lib ns_parserr {bind resolv}] || |
| 736 | 777 | [cc-check-function-in-lib __ns_parserr {bind resolv}]) && |
| 737 | 778 | ([cc-check-function-in-lib res_query {bind resolv}] || |
| 738 | 779 | [cc-check-function-in-lib __res_query {bind resolv}]))} { |
| 739 | - msg-result "WARNING: SMTP feature will not be able to look up local MX." | |
| 780 | + msg-result "WARNING: SMTP feature will not be able to look up local MX." | |
| 740 | 781 | } |
| 741 | 782 | cc-check-function-in-lib res_9_ns_initparse resolv |
| 742 | 783 | |
| 743 | 784 | # Other nonstandard function checks |
| 744 | 785 | cc-check-functions utime |
| @@ -749,12 +790,12 @@ | ||
| 749 | 790 | |
| 750 | 791 | # Termux on Android adds "getpass(char *)" to unistd.h, so check this so we |
| 751 | 792 | # guard against including it again; use cctest as cc-check-functions and |
| 752 | 793 | # cctest_function check for "getpass()" with no args and fail |
| 753 | 794 | if {[cctest -link 1 -includes {unistd.h} -code "getpass(0);"]} { |
| 754 | - define FOSSIL_HAVE_GETPASS 1 | |
| 755 | - msg-result "Found getpass() with unistd.h" | |
| 795 | + define FOSSIL_HAVE_GETPASS 1 | |
| 796 | + msg-result "Found getpass() with unistd.h" | |
| 756 | 797 | } |
| 757 | 798 | |
| 758 | 799 | # Check for getloadavg(), and if it doesn't exist, define FOSSIL_OMIT_LOAD_AVERAGE |
| 759 | 800 | if {![cc-check-functions getloadavg] || |
| 760 | 801 | ![cctest -link 1 -includes {unistd.h} -code "double a\[3\]; getloadavg(a,3);"]} { |
| @@ -762,44 +803,43 @@ | ||
| 762 | 803 | msg-result "Load average support unavailable" |
| 763 | 804 | } |
| 764 | 805 | |
| 765 | 806 | # Check for getpassphrase() for Solaris 10 where getpass() truncates to 10 chars |
| 766 | 807 | if {![cc-check-functions getpassphrase]} { |
| 767 | - # Haiku needs this | |
| 768 | - cc-check-function-in-lib getpass bsd | |
| 808 | + # Haiku needs this | |
| 809 | + cc-check-function-in-lib getpass bsd | |
| 769 | 810 | } |
| 770 | -cc-check-function-in-lib sin m | |
| 771 | 811 | |
| 772 | 812 | # Check for the FuseFS library |
| 773 | 813 | if {[opt-bool fusefs]} { |
| 774 | 814 | if {[opt-bool static]} { |
| 775 | - msg-result "FuseFS support disabled due to -static" | |
| 815 | + msg-result "FuseFS support disabled due to -static" | |
| 776 | 816 | } elseif {[cc-check-function-in-lib fuse_mount fuse]} { |
| 777 | - define-append EXTRA_CFLAGS -DFOSSIL_HAVE_FUSEFS | |
| 778 | - define FOSSIL_HAVE_FUSEFS 1 | |
| 779 | - msg-result "FuseFS support enabled" | |
| 817 | + define-append EXTRA_CFLAGS -DFOSSIL_HAVE_FUSEFS | |
| 818 | + define FOSSIL_HAVE_FUSEFS 1 | |
| 819 | + msg-result "FuseFS support enabled" | |
| 780 | 820 | } |
| 781 | 821 | } |
| 782 | 822 | |
| 783 | 823 | ######################################################################## |
| 784 | 824 | # Checks the compiler for compile_commands.json support. |
| 785 | 825 | # |
| 786 | 826 | # Returns 1 if supported, else 0. Defines MAKE_COMPILATION_DB to "yes" |
| 787 | 827 | # if supported, "no" if not. |
| 788 | 828 | proc check-compile-commands {} { |
| 789 | - msg-checking "compile_commands.json support... " | |
| 790 | - if {[cctest -lang c -cflags {/dev/null -MJ} -source {}]} { | |
| 791 | - # This test reportedly incorrectly succeeds on one of | |
| 792 | - # Martin G.'s older systems. | |
| 793 | - msg-result "compiler supports compile_commands.json" | |
| 794 | - define MAKE_COMPILATION_DB yes | |
| 795 | - return 1 | |
| 796 | - } else { | |
| 797 | - msg-result "compiler does not support compile_commands.json" | |
| 798 | - define MAKE_COMPILATION_DB no | |
| 799 | - return 0 | |
| 800 | - } | |
| 829 | + msg-checking "compile_commands.json support... " | |
| 830 | + if {[cctest -lang c -cflags {/dev/null -MJ} -source {}]} { | |
| 831 | + # This test reportedly incorrectly succeeds on one of | |
| 832 | + # Martin G.'s older systems. | |
| 833 | + msg-result "compiler supports compile_commands.json" | |
| 834 | + define MAKE_COMPILATION_DB yes | |
| 835 | + return 1 | |
| 836 | + } else { | |
| 837 | + msg-result "compiler does not support compile_commands.json" | |
| 838 | + define MAKE_COMPILATION_DB no | |
| 839 | + return 0 | |
| 840 | + } | |
| 801 | 841 | } |
| 802 | 842 | |
| 803 | 843 | define MAKE_COMPILATION_DB no |
| 804 | 844 | if {!$outOfTreeBuild} { |
| 805 | 845 | if {[opt-bool compile-commands]} { |
| @@ -816,32 +856,18 @@ | ||
| 816 | 856 | # Add -fsanitize compile and link options late: we don't want the C |
| 817 | 857 | # checks above to run with those sanitizers enabled. It can not only |
| 818 | 858 | # be pointless, it can actually break correct tests. |
| 819 | 859 | set fsan [opt-val with-sanitizer] |
| 820 | 860 | if {[string length $fsan]} { |
| 821 | - define-append EXTRA_CFLAGS -fsanitize=$fsan | |
| 822 | - define-append EXTRA_LDFLAGS -fsanitize=$fsan | |
| 823 | - if {[string first "undefined" $fsan] != -1} { | |
| 824 | - # We need to link with libubsan if we're compiling under | |
| 825 | - # GCC with -fsanitize=undefined. | |
| 826 | - cc-check-function-in-lib __ubsan_handle_add_overflow ubsan | |
| 827 | - } | |
| 828 | -} | |
| 829 | - | |
| 830 | -# Finally, append libraries that must be last. This matters more on some | |
| 831 | -# OSes than others, but is most broadly required for static linking. | |
| 832 | -if {[check-function-in-lib dlopen dl]} { | |
| 833 | - # Some platforms (*BSD) have the dl functions already in libc and no libdl. | |
| 834 | - # In such case we can link directly without -ldl. | |
| 835 | - define-append LIBS [get-define lib_dlopen] | |
| 836 | -} | |
| 837 | -if {[opt-bool static]} { | |
| 838 | - # Linux can only infer the dependency on pthread from OpenSSL when | |
| 839 | - # doing dynamic linkage. | |
| 840 | - define-append LIBS -lpthread | |
| 841 | -} | |
| 842 | - | |
| 861 | + define-append EXTRA_CFLAGS -fsanitize=$fsan | |
| 862 | + define-append EXTRA_LDFLAGS -fsanitize=$fsan | |
| 863 | + if {[string first "undefined" $fsan] != -1} { | |
| 864 | + # We need to link with libubsan if we're compiling under | |
| 865 | + # GCC with -fsanitize=undefined. | |
| 866 | + cc-check-function-in-lib __ubsan_handle_add_overflow ubsan | |
| 867 | + } | |
| 868 | +} | |
| 843 | 869 | |
| 844 | 870 | ######################################################################## |
| 845 | 871 | # @proj-check-emsdk |
| 846 | 872 | # |
| 847 | 873 | # Emscripten is used for doing in-tree builds of web-based WASM stuff, |
| @@ -918,10 +944,46 @@ | ||
| 918 | 944 | } else { |
| 919 | 945 | define EMCC_WRAPPER "" |
| 920 | 946 | define EMCC_OPT "" |
| 921 | 947 | catch {exec rm -f tools/emcc.sh} |
| 922 | 948 | } |
| 949 | + | |
| 950 | +handle-with-openssl | |
| 951 | + | |
| 952 | +# Finally, append libraries that must be last. This matters more on some | |
| 953 | +# OSes than others, but is most broadly required for static linking. | |
| 954 | +if {[opt-bool static]} { | |
| 955 | + # Linux can only infer the dependency on pthread from OpenSSL when | |
| 956 | + # doing dynamic linkage. | |
| 957 | + define-append LIBS -lpthread | |
| 958 | +} | |
| 959 | + | |
| 960 | +apply {{} { | |
| 961 | + # This started out as a workaround for getting the ordering of -ldl | |
| 962 | + # correct in conjunction with openssl in some environments. Then it | |
| 963 | + # evolved into a more generic preemptive portability workaround to | |
| 964 | + # ensure that certain libraries are always appended to the global | |
| 965 | + # LIBS list if they exist on the system. Based on a /chat discussion | |
| 966 | + # on 2025-02-27 in the context of check-in [8d3b9bf4d4]. | |
| 967 | + # | |
| 968 | + # Note that [move-lib-to-end] and [lib-actually-exists] are in | |
| 969 | + # autosetup/local.tcl. | |
| 970 | + set libs [get-define LIBS] | |
| 971 | + #puts "**** 1 LIBS: $libs" | |
| 972 | + foreach ll {-ldl -lpthread -lm} { | |
| 973 | + if {![move-lib-to-end $ll $libs libs]} { | |
| 974 | + # $ll was not in the list | |
| 975 | + if {[lib-actually-exists $ll]} { | |
| 976 | + # Add it to the list "just in case." This will be a no-op on | |
| 977 | + # systems where the lib is not actually used. | |
| 978 | + lappend libs $ll | |
| 979 | + } | |
| 980 | + } | |
| 981 | + } | |
| 982 | + #puts "**** 2 LIBS: $libs" | |
| 983 | + define LIBS [join $libs " "] | |
| 984 | +}} | |
| 923 | 985 | |
| 924 | 986 | # Tag container builds with a prefix of the checkin ID of the version |
| 925 | 987 | # of Fossil each one contains. This not only allows multiple images |
| 926 | 988 | # to coexist and multiple containers to be created unamgiguosly from |
| 927 | 989 | # them, it also changes the URL we fetch the source tarball from, so |
| 928 | 990 |
| --- auto.def | |
| +++ auto.def | |
| @@ -36,17 +36,17 @@ | |
| 36 | } |
| 37 | |
| 38 | # Update the minimum required SQLite version number here, and also |
| 39 | # in src/main.c near the sqlite3_libversion_number() call. Take care |
| 40 | # that both places agree! |
| 41 | define MINIMUM_SQLITE_VERSION "3.46.0" |
| 42 | |
| 43 | # This is useful for people wanting Fossil to use an external SQLite library |
| 44 | # to compare the one they have against the minimum required |
| 45 | if {[opt-bool print-minimum-sqlite-version]} { |
| 46 | puts [get-define MINIMUM_SQLITE_VERSION] |
| 47 | exit 0 |
| 48 | } |
| 49 | |
| 50 | # Space characters have never been allowed in either the source |
| 51 | # tree nor the build directory. But the resulting error messages |
| 52 | # could be confusing. The following checks make the reason for the |
| @@ -67,21 +67,21 @@ | |
| 67 | set outOfTreeBuild 1 |
| 68 | } |
| 69 | |
| 70 | # sqlite wants these types if possible |
| 71 | cc-with {-includes {stdint.h inttypes.h}} { |
| 72 | cc-check-types uint32_t uint16_t int16_t uint8_t |
| 73 | } |
| 74 | |
| 75 | # Use pread/pwrite system calls in place of seek + read/write if possible |
| 76 | define USE_PREAD [cc-check-functions pread] |
| 77 | |
| 78 | # If we have cscope here, we'll use it in the "tags" target |
| 79 | if {[cc-check-progs cscope]} { |
| 80 | define COLLECT_CSCOPE_DATA "cscope -bR $::autosetup(srcdir)/src/*.\[ch\]" |
| 81 | } else { |
| 82 | define COLLECT_CSCOPE_DATA "" |
| 83 | } |
| 84 | |
| 85 | # Find tclsh for the test suite. |
| 86 | # |
| 87 | # We can't use jimsh for this: the test suite uses features of Tcl that |
| @@ -94,32 +94,32 @@ | |
| 94 | # Ironically, this means we may right now be running under either jimsh0 |
| 95 | # or a version of tclsh that we find unsuitable below! |
| 96 | cc-check-progs tclsh |
| 97 | set hbtd /usr/local/Cellar/tcl-tk |
| 98 | if {[string equal false [get-define TCLSH]]} { |
| 99 | msg-result "WARNING: 'make test' will not run here." |
| 100 | } else { |
| 101 | set v [exec sh -c "echo 'puts \$tcl_version' | tclsh"] |
| 102 | if {[expr {$v >= 8.6}]} { |
| 103 | msg-result "Found Tclsh version $v in the PATH." |
| 104 | define TCLSH tclsh |
| 105 | } elseif {[file isdirectory $hbtd]} { |
| 106 | # This is a macOS system with the Homebrew version of Tcl/Tk |
| 107 | # installed. Select the newest version. It won't normally be |
| 108 | # in the PATH to avoid shadowing /usr/bin/tclsh, and even if it |
| 109 | # were in the PATH, it's bad practice to put /usr/local/bin (the |
| 110 | # Homebrew default) ahead of /usr/bin, especially given that |
| 111 | # it's user-writeable by default with Homebrew. Thus, we can be |
| 112 | # pretty sure the only way to call it is with an absolute path. |
| 113 | set v [exec ls -tr $hbtd | tail -1] |
| 114 | set path "$hbtd/$v/bin/tclsh" |
| 115 | define TCLSH $path |
| 116 | msg-result "Using Homebrew Tcl/Tk version $path." |
| 117 | } else { |
| 118 | msg-result "WARNING: tclsh $v found; need >= 8.6 for 'make test'." |
| 119 | define TCLSH false ;# force "make test" failure via /usr/bin/false |
| 120 | } |
| 121 | } |
| 122 | |
| 123 | define CFLAGS [get-env CFLAGS "-g -Os"] |
| 124 | define EXTRA_CFLAGS "-Wall" |
| 125 | define EXTRA_LDFLAGS "" |
| @@ -133,48 +133,48 @@ | |
| 133 | # SQLITE_OPTIONS_EXT => build-dependent CFLAGS for sqlite3.c and shell.c |
| 134 | |
| 135 | # Maintain the C89/C90-style order of variable declarations before statements. |
| 136 | # Check if the compiler supports the respective warning flag. |
| 137 | if {[cctest -cflags -Wdeclaration-after-statement]} { |
| 138 | define-append EXTRA_CFLAGS -Wdeclaration-after-statement |
| 139 | } |
| 140 | |
| 141 | |
| 142 | # This procedure is a customized version of "cc-check-function-in-lib", |
| 143 | # that does not modify the LIBS variable. Its use prevents prematurely |
| 144 | # pulling in libraries that will be added later anyhow (e.g. "-ldl"). |
| 145 | proc check-function-in-lib {function libs {otherlibs {}}} { |
| 146 | if {[string length $otherlibs]} { |
| 147 | msg-checking "Checking for $function in $libs with $otherlibs..." |
| 148 | } else { |
| 149 | msg-checking "Checking for $function in $libs..." |
| 150 | } |
| 151 | set found 0 |
| 152 | cc-with [list -libs $otherlibs] { |
| 153 | if {[cctest_function $function]} { |
| 154 | msg-result "none needed" |
| 155 | define lib_$function "" |
| 156 | incr found |
| 157 | } else { |
| 158 | foreach lib $libs { |
| 159 | cc-with [list -libs -l$lib] { |
| 160 | if {[cctest_function $function]} { |
| 161 | msg-result -l$lib |
| 162 | define lib_$function -l$lib |
| 163 | incr found |
| 164 | break |
| 165 | } |
| 166 | } |
| 167 | } |
| 168 | } |
| 169 | } |
| 170 | if {$found} { |
| 171 | define [feature-define-name $function] |
| 172 | } else { |
| 173 | msg-result "no" |
| 174 | } |
| 175 | return $found |
| 176 | } |
| 177 | |
| 178 | if {![opt-bool internal-sqlite]} { |
| 179 | proc find_system_sqlite {} { |
| 180 | |
| @@ -219,17 +219,17 @@ | |
| 219 | set sqlite-version [string cat "-D MINIMUM_SQLITE_VERSION=" [get-define MINIMUM_SQLITE_VERSION]] |
| 220 | lappend cmdline {*}[set sqlite-version] |
| 221 | set ok 1 |
| 222 | set err [catch {exec-with-stderr {*}$cmdline} result errinfo] |
| 223 | if {$err} { |
| 224 | configlog "Failed: [join $cmdline]" |
| 225 | if {[string length $result]>0} {configlog $result} |
| 226 | configlog "============" |
| 227 | set ok 0 |
| 228 | } elseif {$::autosetup(debug)} { |
| 229 | configlog "Compiled OK: [join $cmdline]" |
| 230 | configlog "============" |
| 231 | } |
| 232 | if {!$ok} { |
| 233 | user-error "unable to compile SQLite compatibility test program" |
| 234 | } |
| 235 | set err [catch {exec-with-stderr ./conftest__} result errinfo] |
| @@ -251,475 +251,516 @@ | |
| 251 | ![file exists "/dev/null"] |
| 252 | }] |
| 253 | } |
| 254 | |
| 255 | if {[is_mingw]} { |
| 256 | define-append EXTRA_CFLAGS -DBROKEN_MINGW_CMDLINE |
| 257 | define-append LIBS -lkernel32 -lws2_32 |
| 258 | } else { |
| 259 | # |
| 260 | # NOTE: All platforms except MinGW should use the linenoise |
| 261 | # package. It is currently unsupported on Win32. |
| 262 | # |
| 263 | define USE_LINENOISE 1 |
| 264 | } |
| 265 | |
| 266 | if {[string match *-solaris* [get-define host]]} { |
| 267 | define-append EXTRA_CFLAGS {-D__EXTENSIONS__} |
| 268 | } |
| 269 | |
| 270 | if {[opt-bool fossil-debug]} { |
| 271 | define CFLAGS {-g -O0 -Wall} |
| 272 | define-append CFLAGS -DFOSSIL_DEBUG |
| 273 | msg-result "Debugging support enabled" |
| 274 | } |
| 275 | |
| 276 | if {[opt-bool no-opt]} { |
| 277 | define CFLAGS {-g -O0 -Wall} |
| 278 | msg-result "Builting without compiler optimization" |
| 279 | if {[opt-bool fossil-debug]} { |
| 280 | define-append CFLAGS -DFOSSIL_DEBUG |
| 281 | } |
| 282 | } |
| 283 | |
| 284 | if {[opt-bool with-mman]} { |
| 285 | define-append EXTRA_CFLAGS -DUSE_MMAN_H |
| 286 | define USE_MMAN_H 1 |
| 287 | msg-result "Enabling \"sys/mman.h\" support" |
| 288 | } |
| 289 | |
| 290 | if {[opt-bool with-see]} { |
| 291 | define-append EXTRA_CFLAGS -DUSE_SEE |
| 292 | define USE_SEE 1 |
| 293 | define SQLITE3_ORIGIN 1 |
| 294 | msg-result "Enabling encryption support" |
| 295 | } |
| 296 | |
| 297 | if {[opt-bool json]} { |
| 298 | # Reminder/FIXME (stephan): FOSSIL_ENABLE_JSON |
| 299 | # is required in the CFLAGS because json*.c |
| 300 | # have #ifdef guards around the whole file without |
| 301 | # reading config.h first. |
| 302 | define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_JSON |
| 303 | define FOSSIL_ENABLE_JSON |
| 304 | msg-result "JSON support enabled" |
| 305 | } |
| 306 | |
| 307 | if {[opt-bool with-exec-rel-paths]} { |
| 308 | define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_EXEC_REL_PATHS |
| 309 | define FOSSIL_ENABLE_EXEC_REL_PATHS |
| 310 | msg-result "Relative paths in external diff/gdiff enabled" |
| 311 | } |
| 312 | |
| 313 | if {[opt-bool with-th1-docs]} { |
| 314 | define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_TH1_DOCS |
| 315 | define FOSSIL_ENABLE_TH1_DOCS |
| 316 | msg-result "TH1 embedded documentation support enabled" |
| 317 | } |
| 318 | |
| 319 | if {[opt-bool with-th1-hooks]} { |
| 320 | define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_TH1_HOOKS |
| 321 | define FOSSIL_ENABLE_TH1_HOOKS |
| 322 | msg-result "TH1 hooks support enabled" |
| 323 | } |
| 324 | |
| 325 | #if {[opt-bool markdown]} { |
| 326 | # # no-op. Markdown is now enabled by default. |
| 327 | # msg-result "Markdown support enabled" |
| 328 | #} |
| 329 | |
| 330 | if {[opt-bool static]} { |
| 331 | # XXX: This will not work on all systems. |
| 332 | define-append EXTRA_LDFLAGS -static |
| 333 | msg-result "Trying to link statically" |
| 334 | } else { |
| 335 | define-append EXTRA_CFLAGS -DFOSSIL_DYNAMIC_BUILD=1 |
| 336 | define FOSSIL_DYNAMIC_BUILD |
| 337 | } |
| 338 | |
| 339 | # Check for libraries that need to be sorted out early |
| 340 | cc-check-function-in-lib iconv iconv |
| 341 | |
| 342 | # Helper for OpenSSL checking |
| 343 | proc check-for-openssl {msg {cflags {}} {libs {-lssl -lcrypto -lpthread}}} { |
| 344 | msg-checking "Checking for $msg..." |
| 345 | set rc 0 |
| 346 | if {[is_mingw]} { |
| 347 | lappend libs -lgdi32 -lwsock32 -lcrypt32 |
| 348 | } |
| 349 | if {[info exists ::zlib_lib]} { |
| 350 | lappend libs $::zlib_lib |
| 351 | } |
| 352 | msg-quiet cc-with [list -cflags $cflags -libs $libs] { |
| 353 | if {[cc-check-includes openssl/ssl.h] && \ |
| 354 | [cc-check-functions SSL_new]} { |
| 355 | incr rc |
| 356 | } |
| 357 | } |
| 358 | if {!$rc && ![is_mingw]} { |
| 359 | # On some systems, OpenSSL appears to require -ldl to link. |
| 360 | lappend libs -ldl |
| 361 | msg-quiet cc-with [list -cflags $cflags -libs $libs] { |
| 362 | if {[cc-check-includes openssl/ssl.h] && \ |
| 363 | [cc-check-functions SSL_new]} { |
| 364 | incr rc |
| 365 | } |
| 366 | } |
| 367 | } |
| 368 | if {$rc} { |
| 369 | msg-result "ok" |
| 370 | return 1 |
| 371 | } else { |
| 372 | msg-result "no" |
| 373 | return 0 |
| 374 | } |
| 375 | } |
| 376 | |
| 377 | # Check for zlib, using the given location if specified |
| 378 | set zlibpath [opt-val with-zlib] |
| 379 | if {$zlibpath eq "tree"} { |
| 380 | set zlibdir [file dirname $autosetup(dir)]/compat/zlib |
| 381 | if {![file isdirectory $zlibdir]} { |
| 382 | user-error "The zlib in source tree directory does not exist" |
| 383 | } elseif { ([llength [glob -nocomplain -directory $zlibdir libz*]] == 0) } { |
| 384 | user-error "With --with-zlib=tree, $zlibdir must be configured and built first." |
| 385 | } |
| 386 | cc-with [list -cflags "-I$zlibdir -L$zlibdir"] |
| 387 | define-append EXTRA_CFLAGS -I$zlibdir |
| 388 | define-append LIBS $zlibdir/libz.a |
| 389 | set ::zlib_lib $zlibdir/libz.a |
| 390 | msg-result "Using zlib in source tree" |
| 391 | } else { |
| 392 | set cftry {""} |
| 393 | set ldtry {""} |
| 394 | if {$zlibpath ni {auto ""}} { |
| 395 | lappend cftry "-I$zlibpath" |
| 396 | lappend cftry "-I$zlibpath/include" |
| 397 | lappend ldtry "-L$zlibpath" |
| 398 | lappend ldtry "-L$zlibpath/lib" |
| 399 | } |
| 400 | |
| 401 | # Reverse the list of tests so we check most-specific to least, else |
| 402 | # platform devel files will shadow local --with-zlib overrides. |
| 403 | foreach c [lreverse $cftry] { |
| 404 | if {[cc-with [list -cflags $c] {cc-check-includes zlib.h}]} { |
| 405 | if {$c eq ""} { |
| 406 | msg-result "Found zlib.h in default include path" |
| 407 | } else { |
| 408 | define-append EXTRA_CFLAGS "$c" |
| 409 | msg-result "Found zlib.h via $c" |
| 410 | } |
| 411 | set cfound $c |
| 412 | break |
| 413 | } |
| 414 | } |
| 415 | if {![info exists cfound]} { |
| 416 | user-error "zlib.h not found; either install it or specify its location via --with-zlib" |
| 417 | } |
| 418 | foreach lcheck [lreverse $ldtry] { |
| 419 | if {[cc-with [list -cflags "$cfound $lcheck"] {check-function-in-lib inflateEnd z}]} { |
| 420 | if {$lcheck eq ""} { |
| 421 | msg-result "Linked to zlib via default library path" |
| 422 | } else { |
| 423 | define-append EXTRA_LDFLAGS "$lcheck" |
| 424 | msg-result "Linked to zlib via $lcheck" |
| 425 | } |
| 426 | if {![check-function-in-lib compressBound z]} { |
| 427 | puts "Notice: disabling zlib compression in the SQL shell" |
| 428 | define-append SQLITE_OPTIONS_EXT {-USQLITE_HAVE_ZLIB} |
| 429 | } |
| 430 | break |
| 431 | } |
| 432 | } |
| 433 | set ::zlib_lib -lz |
| 434 | } |
| 435 | |
| 436 | set ssldirs [opt-val with-openssl] |
| 437 | if {$ssldirs ne "none"} { |
| 438 | set found 0 |
| 439 | if {$ssldirs eq "tree"} { |
| 440 | set ssldir [file dirname $autosetup(dir)]/compat/openssl |
| 441 | if {![file isdirectory $ssldir]} { |
| 442 | user-error "The OpenSSL in source tree directory does not exist" |
| 443 | } |
| 444 | set msg "openssl in $ssldir" |
| 445 | set cflags "-I$ssldir/include" |
| 446 | set ldflags "-L$ssldir" |
| 447 | set ssllibs "$ssldir/libssl.a $ssldir/libcrypto.a -lpthread" |
| 448 | set found [check-for-openssl "openssl in source tree" "$cflags $ldflags" $ssllibs] |
| 449 | } else { |
| 450 | if {$ssldirs in {auto ""}} { |
| 451 | catch { |
| 452 | set cflags [exec pkg-config openssl --cflags-only-I] |
| 453 | set ldflags [exec pkg-config openssl --libs-only-L] |
| 454 | set found [check-for-openssl "ssl via pkg-config" "$cflags $ldflags"] |
| 455 | } msg |
| 456 | if {!$found} { |
| 457 | set ssldirs "{} /usr/sfw /usr/local/ssl /usr/lib/ssl /usr/ssl \ |
| 458 | /usr/pkg /usr/local /usr /usr/local/opt/openssl \ |
| 459 | /opt/homebrew/opt/openssl" |
| 460 | } |
| 461 | } |
| 462 | if {!$found} { |
| 463 | foreach dir $ssldirs { |
| 464 | if {$dir eq ""} { |
| 465 | set msg "system openssl" |
| 466 | set cflags "" |
| 467 | set ldflags "" |
| 468 | } else { |
| 469 | set msg "openssl in $dir" |
| 470 | set cflags "-I$dir/include" |
| 471 | set ldflags "-L$dir/lib" |
| 472 | } |
| 473 | if {[check-for-openssl $msg "$cflags $ldflags"]} { |
| 474 | incr found |
| 475 | break |
| 476 | } |
| 477 | if {$dir ne ""} { |
| 478 | set ldflags "" |
| 479 | set msg "static build of openssl in $dir" |
| 480 | set ssllibs "$dir/libssl.a $dir/libcrypto.a -lpthread" |
| 481 | if {[check-for-openssl $msg "$cflags $ldflags" $ssllibs]} { |
| 482 | incr found |
| 483 | break |
| 484 | } |
| 485 | } |
| 486 | } |
| 487 | } |
| 488 | } |
| 489 | if {$found} { |
| 490 | define FOSSIL_ENABLE_SSL |
| 491 | define-append EXTRA_CFLAGS $cflags |
| 492 | define-append EXTRA_LDFLAGS $ldflags |
| 493 | define-append CFLAGS $cflags |
| 494 | define-append LDFLAGS $ldflags |
| 495 | if {[info exists ssllibs]} { |
| 496 | define-append LIBS $ssllibs |
| 497 | } else { |
| 498 | define-append LIBS -lssl -lcrypto |
| 499 | } |
| 500 | if {[info exists ::zlib_lib]} { |
| 501 | define-append LIBS $::zlib_lib |
| 502 | } |
| 503 | if {[is_mingw]} { |
| 504 | define-append LIBS -lgdi32 -lwsock32 -lcrypt32 |
| 505 | } |
| 506 | msg-result "HTTPS support enabled" |
| 507 | |
| 508 | # Silence OpenSSL deprecation warnings on Mac OS X 10.7. |
| 509 | if {[string match *-darwin* [get-define host]]} { |
| 510 | if {[cctest -cflags {-Wdeprecated-declarations}]} { |
| 511 | define-append EXTRA_CFLAGS -Wdeprecated-declarations |
| 512 | } |
| 513 | } |
| 514 | } else { |
| 515 | user-error "OpenSSL not found. Consider --with-openssl=none to disable HTTPS support" |
| 516 | } |
| 517 | } else { |
| 518 | if {[info exists ::zlib_lib]} { |
| 519 | define-append LIBS $::zlib_lib |
| 520 | } |
| 521 | } |
| 522 | |
| 523 | |
| 524 | |
| 525 | ######################################################################## |
| 526 | # --with-sqlite=PATH checks for the first it finds of the following... |
| 527 | # - PATH/sqlite3.c and PATH/sqlite3.h |
| 528 | # - PATH/sqlite3.o (and assumes sqlite3.h is with it) |
| 529 | # - PATH/lib/libsqlite3* and PATH/include/sqlite3.h |
| 530 | define CFLAGS_INCLUDE {} |
| 531 | # ^^^ CFLAGS_INCLUDE is ONLY for -I... flags and their order is |
| 532 | # significant so that --with-sqlite=PATH's header can shadow our |
| 533 | # own. One caveat with this is that we cannot point --with-sqlite=PATH |
| 534 | # to the root of sqlite3's own build tree because that dir has a |
| 535 | # config.h which ends up shadowing src/config.h, breaking our build. |
| 536 | set sq3path [opt-val with-sqlite] |
| 537 | define SQLITE3_SRC.2 {} |
| 538 | define SQLITE3_OBJ.2 {} |
| 539 | define SQLITE3_SHELL_SRC.2 {$(SQLITE3_SHELL_SRC.0)} |
| 540 | if {$sq3path in {tree ""}} { |
| 541 | msg-result "Using sqlite3.c from this source tree." |
| 542 | } else { |
| 543 | # SQLITE3_ORIGIN: |
| 544 | # 0 = local source tree |
| 545 | # 1 = use external lib or sqlite3.o |
| 546 | # 2 = use external sqlite3.c and (if found) shell.c |
| 547 | define USE_SYSTEM_SQLITE 1 |
| 548 | define SQLITE3_ORIGIN 2 |
| 549 | if {$sq3path != "auto"} { |
| 550 | if {([file exists $sq3path/sqlite3.c]) && \ |
| 551 | ([file exists $sq3path/sqlite3.h]) } { |
| 552 | # Prefer sqlite3.[ch] if found. |
| 553 | define SQLITE3_SRC.2 $sq3path/sqlite3.c |
| 554 | define SQLITE3_OBJ.2 {$(SQLITE3_OBJ.0)} |
| 555 | define USE_SYSTEM_SQLITE 2 |
| 556 | define SQLITE3_ORIGIN 2 |
| 557 | if {[file exists $sq3path/shell.c]} { |
| 558 | define SQLITE3_SHELL_SRC.2 $sq3path/shell.c |
| 559 | } |
| 560 | define-append CFLAGS_INCLUDE -I$sq3path |
| 561 | define-append EXTRA_LDFLAGS -lpthread |
| 562 | # ^^^ additional -lXXX flags are conservative estimates |
| 563 | msg-result "Using sqlite3.c and sqlite3.h from $sq3path" |
| 564 | } elseif {[file exists $sq3path/sqlite3.o]} { |
| 565 | # Use sqlite3.o if found. |
| 566 | define SQLITE3_OBJ.2 $sq3path/sqlite3.o |
| 567 | define-append CFLAGS_INCLUDE -I$sq3path |
| 568 | define-append EXTRA_LDFLAGS $sq3path/sqlite3.o -lpthread |
| 569 | # ^^^ additional -lXXX flags are conservative estimates |
| 570 | msg-result "Using sqlite3.o from $sq3path" |
| 571 | } elseif { ([llength [glob -nocomplain -directory $sq3path/lib libsqlite3*]] != 0) \ |
| 572 | && ([file exists $sq3path/include/sqlite3.h]) } { |
| 573 | # e.g. --with-sqlite=/usr/local. Try $sq3path/lib/libsqlite3* |
| 574 | # and $sq3path/include/sqlite3.h |
| 575 | define-append CFLAGS_INCLUDE -I$sq3path/include |
| 576 | define-append EXTRA_LDFLAGS -L$sq3path/lib -lsqlite3 -lpthread |
| 577 | # ^^^ additional -lXXX flags are conservative estimates |
| 578 | msg-result "Using -lsqlite3 from $sq3path" |
| 579 | } else { |
| 580 | # Assume $sq3path holds both the lib and header |
| 581 | cc-with [list -cflags "-I$sq3path -L$sq3path"] |
| 582 | define-append CFLAGS_INCLUDE -I$sq3path |
| 583 | define-append EXTRA_LDFLAGS -L$sq3path -lsqlite3 -lpthread |
| 584 | # ^^^ additional -lXXX flags are conservative estimates |
| 585 | msg-result "Using -lsqlite3 from $sq3path" |
| 586 | } |
| 587 | } elseif {![cc-check-includes sqlite3.h] || ![check-function-in-lib sqlite3_open_v2 sqlite3]} { |
| 588 | user-error "libsqlite3 not found please install it or specify the location with --with-sqlite" |
| 589 | } |
| 590 | } |
| 591 | define-append CFLAGS_INCLUDE {-I. -I$(SRCDIR) -I$(SRCDIR_extsrc)} |
| 592 | |
| 593 | set tclpath [opt-val with-tcl] |
| 594 | if {$tclpath ne ""} { |
| 595 | set tclprivatestubs [opt-bool with-tcl-private-stubs] |
| 596 | # Note parse-tclconfig-sh is in autosetup/local.tcl |
| 597 | if {$tclpath eq "1"} { |
| 598 | set tcldir [file dirname $autosetup(dir)]/compat/tcl-8.6 |
| 599 | if {$tclprivatestubs} { |
| 600 | set tclconfig(TCL_INCLUDE_SPEC) -I$tcldir/generic |
| 601 | set tclconfig(TCL_VERSION) {Private Stubs} |
| 602 | set tclconfig(TCL_PATCH_LEVEL) {} |
| 603 | set tclconfig(TCL_PREFIX) $tcldir |
| 604 | set tclconfig(TCL_LD_FLAGS) { } |
| 605 | } else { |
| 606 | # Use the system Tcl. Look in some likely places. |
| 607 | array set tclconfig [parse-tclconfig-sh \ |
| 608 | $tcldir/unix $tcldir/win \ |
| 609 | /usr /usr/local /usr/share /opt/local] |
| 610 | set msg "on your system" |
| 611 | } |
| 612 | } else { |
| 613 | array set tclconfig [parse-tclconfig-sh $tclpath] |
| 614 | set msg "at $tclpath" |
| 615 | } |
| 616 | if {[opt-bool static]} { |
| 617 | set tclconfig(TCL_LD_FLAGS) { } |
| 618 | } |
| 619 | if {![info exists tclconfig(TCL_INCLUDE_SPEC)]} { |
| 620 | user-error "Cannot find Tcl $msg" |
| 621 | } |
| 622 | set tclstubs [opt-bool with-tcl-stubs] |
| 623 | if {$tclprivatestubs} { |
| 624 | define FOSSIL_ENABLE_TCL_PRIVATE_STUBS |
| 625 | define USE_TCL_STUBS |
| 626 | } elseif {$tclstubs && $tclconfig(TCL_SUPPORTS_STUBS)} { |
| 627 | set libs "$tclconfig(TCL_STUB_LIB_SPEC)" |
| 628 | define FOSSIL_ENABLE_TCL_STUBS |
| 629 | define USE_TCL_STUBS |
| 630 | } else { |
| 631 | set libs "$tclconfig(TCL_LIB_SPEC) $tclconfig(TCL_LIBS)" |
| 632 | } |
| 633 | set cflags $tclconfig(TCL_INCLUDE_SPEC) |
| 634 | if {!$tclprivatestubs} { |
| 635 | set foundtcl 0; # Did we find a working Tcl library? |
| 636 | cc-with [list -cflags $cflags -libs $libs] { |
| 637 | if {$tclstubs} { |
| 638 | if {[cc-check-functions Tcl_InitStubs]} { |
| 639 | set foundtcl 1 |
| 640 | } |
| 641 | } else { |
| 642 | if {[cc-check-functions Tcl_CreateInterp]} { |
| 643 | set foundtcl 1 |
| 644 | } |
| 645 | } |
| 646 | } |
| 647 | if {!$foundtcl && [string match *-lieee* $libs]} { |
| 648 | # On some systems, using "-lieee" from TCL_LIB_SPEC appears |
| 649 | # to cause issues. |
| 650 | msg-result "Removing \"-lieee\" and retrying for Tcl..." |
| 651 | set libs [string map [list -lieee ""] $libs] |
| 652 | cc-with [list -cflags $cflags -libs $libs] { |
| 653 | if {$tclstubs} { |
| 654 | if {[cc-check-functions Tcl_InitStubs]} { |
| 655 | set foundtcl 1 |
| 656 | } |
| 657 | } else { |
| 658 | if {[cc-check-functions Tcl_CreateInterp]} { |
| 659 | set foundtcl 1 |
| 660 | } |
| 661 | } |
| 662 | } |
| 663 | } |
| 664 | if {!$foundtcl && ![string match *-lpthread* $libs]} { |
| 665 | # On some systems, TCL_LIB_SPEC appears to be missing |
| 666 | # "-lpthread". Try adding it. |
| 667 | msg-result "Adding \"-lpthread\" and retrying for Tcl..." |
| 668 | set libs "$libs -lpthread" |
| 669 | cc-with [list -cflags $cflags -libs $libs] { |
| 670 | if {$tclstubs} { |
| 671 | if {[cc-check-functions Tcl_InitStubs]} { |
| 672 | set foundtcl 1 |
| 673 | } |
| 674 | } else { |
| 675 | if {[cc-check-functions Tcl_CreateInterp]} { |
| 676 | set foundtcl 1 |
| 677 | } |
| 678 | } |
| 679 | } |
| 680 | } |
| 681 | if {!$foundtcl} { |
| 682 | if {$tclstubs} { |
| 683 | user-error "Cannot find a usable Tcl stubs library $msg" |
| 684 | } else { |
| 685 | user-error "Cannot find a usable Tcl library $msg" |
| 686 | } |
| 687 | } |
| 688 | } |
| 689 | set version $tclconfig(TCL_VERSION)$tclconfig(TCL_PATCH_LEVEL) |
| 690 | msg-result "Found Tcl $version at $tclconfig(TCL_PREFIX)" |
| 691 | if {!$tclprivatestubs} { |
| 692 | define-append LIBS $libs |
| 693 | } |
| 694 | define-append EXTRA_CFLAGS $cflags |
| 695 | define-append CFLAGS $cflags |
| 696 | if {[info exists zlibpath] && $zlibpath eq "tree"} { |
| 697 | # |
| 698 | # NOTE: When using zlib in the source tree, prevent Tcl from |
| 699 | # pulling in the system one. |
| 700 | # |
| 701 | set tclconfig(TCL_LD_FLAGS) [string map [list -lz ""] \ |
| 702 | $tclconfig(TCL_LD_FLAGS)] |
| 703 | } |
| 704 | # |
| 705 | # NOTE: Remove "-ldl" from the TCL_LD_FLAGS because it will be |
| 706 | # be checked for near the bottom of this file. |
| 707 | # |
| 708 | set tclconfig(TCL_LD_FLAGS) [string map [list -ldl ""] \ |
| 709 | $tclconfig(TCL_LD_FLAGS)] |
| 710 | define-append EXTRA_LDFLAGS $tclconfig(TCL_LD_FLAGS) |
| 711 | define FOSSIL_ENABLE_TCL |
| 712 | } |
| 713 | |
| 714 | # Network functions require libraries on some systems |
| 715 | cc-check-function-in-lib gethostbyname nsl |
| 716 | if {![cc-check-function-in-lib socket {socket network}]} { |
| 717 | # Last resort, may be Windows |
| 718 | if {[is_mingw]} { |
| 719 | define-append LIBS -lwsock32 |
| 720 | } |
| 721 | } |
| 722 | |
| 723 | # Some systems (ex: SunOS) require -lrt in order to use nanosleep |
| 724 | cc-check-function-in-lib nanosleep rt |
| 725 | |
| @@ -734,11 +775,11 @@ | |
| 734 | [cc-check-function-in-lib __ns_name_uncompress {bind resolv}]) && |
| 735 | ([cc-check-function-in-lib ns_parserr {bind resolv}] || |
| 736 | [cc-check-function-in-lib __ns_parserr {bind resolv}]) && |
| 737 | ([cc-check-function-in-lib res_query {bind resolv}] || |
| 738 | [cc-check-function-in-lib __res_query {bind resolv}]))} { |
| 739 | msg-result "WARNING: SMTP feature will not be able to look up local MX." |
| 740 | } |
| 741 | cc-check-function-in-lib res_9_ns_initparse resolv |
| 742 | |
| 743 | # Other nonstandard function checks |
| 744 | cc-check-functions utime |
| @@ -749,12 +790,12 @@ | |
| 749 | |
| 750 | # Termux on Android adds "getpass(char *)" to unistd.h, so check this so we |
| 751 | # guard against including it again; use cctest as cc-check-functions and |
| 752 | # cctest_function check for "getpass()" with no args and fail |
| 753 | if {[cctest -link 1 -includes {unistd.h} -code "getpass(0);"]} { |
| 754 | define FOSSIL_HAVE_GETPASS 1 |
| 755 | msg-result "Found getpass() with unistd.h" |
| 756 | } |
| 757 | |
| 758 | # Check for getloadavg(), and if it doesn't exist, define FOSSIL_OMIT_LOAD_AVERAGE |
| 759 | if {![cc-check-functions getloadavg] || |
| 760 | ![cctest -link 1 -includes {unistd.h} -code "double a\[3\]; getloadavg(a,3);"]} { |
| @@ -762,44 +803,43 @@ | |
| 762 | msg-result "Load average support unavailable" |
| 763 | } |
| 764 | |
| 765 | # Check for getpassphrase() for Solaris 10 where getpass() truncates to 10 chars |
| 766 | if {![cc-check-functions getpassphrase]} { |
| 767 | # Haiku needs this |
| 768 | cc-check-function-in-lib getpass bsd |
| 769 | } |
| 770 | cc-check-function-in-lib sin m |
| 771 | |
| 772 | # Check for the FuseFS library |
| 773 | if {[opt-bool fusefs]} { |
| 774 | if {[opt-bool static]} { |
| 775 | msg-result "FuseFS support disabled due to -static" |
| 776 | } elseif {[cc-check-function-in-lib fuse_mount fuse]} { |
| 777 | define-append EXTRA_CFLAGS -DFOSSIL_HAVE_FUSEFS |
| 778 | define FOSSIL_HAVE_FUSEFS 1 |
| 779 | msg-result "FuseFS support enabled" |
| 780 | } |
| 781 | } |
| 782 | |
| 783 | ######################################################################## |
| 784 | # Checks the compiler for compile_commands.json support. |
| 785 | # |
| 786 | # Returns 1 if supported, else 0. Defines MAKE_COMPILATION_DB to "yes" |
| 787 | # if supported, "no" if not. |
| 788 | proc check-compile-commands {} { |
| 789 | msg-checking "compile_commands.json support... " |
| 790 | if {[cctest -lang c -cflags {/dev/null -MJ} -source {}]} { |
| 791 | # This test reportedly incorrectly succeeds on one of |
| 792 | # Martin G.'s older systems. |
| 793 | msg-result "compiler supports compile_commands.json" |
| 794 | define MAKE_COMPILATION_DB yes |
| 795 | return 1 |
| 796 | } else { |
| 797 | msg-result "compiler does not support compile_commands.json" |
| 798 | define MAKE_COMPILATION_DB no |
| 799 | return 0 |
| 800 | } |
| 801 | } |
| 802 | |
| 803 | define MAKE_COMPILATION_DB no |
| 804 | if {!$outOfTreeBuild} { |
| 805 | if {[opt-bool compile-commands]} { |
| @@ -816,32 +856,18 @@ | |
| 816 | # Add -fsanitize compile and link options late: we don't want the C |
| 817 | # checks above to run with those sanitizers enabled. It can not only |
| 818 | # be pointless, it can actually break correct tests. |
| 819 | set fsan [opt-val with-sanitizer] |
| 820 | if {[string length $fsan]} { |
| 821 | define-append EXTRA_CFLAGS -fsanitize=$fsan |
| 822 | define-append EXTRA_LDFLAGS -fsanitize=$fsan |
| 823 | if {[string first "undefined" $fsan] != -1} { |
| 824 | # We need to link with libubsan if we're compiling under |
| 825 | # GCC with -fsanitize=undefined. |
| 826 | cc-check-function-in-lib __ubsan_handle_add_overflow ubsan |
| 827 | } |
| 828 | } |
| 829 | |
| 830 | # Finally, append libraries that must be last. This matters more on some |
| 831 | # OSes than others, but is most broadly required for static linking. |
| 832 | if {[check-function-in-lib dlopen dl]} { |
| 833 | # Some platforms (*BSD) have the dl functions already in libc and no libdl. |
| 834 | # In such case we can link directly without -ldl. |
| 835 | define-append LIBS [get-define lib_dlopen] |
| 836 | } |
| 837 | if {[opt-bool static]} { |
| 838 | # Linux can only infer the dependency on pthread from OpenSSL when |
| 839 | # doing dynamic linkage. |
| 840 | define-append LIBS -lpthread |
| 841 | } |
| 842 | |
| 843 | |
| 844 | ######################################################################## |
| 845 | # @proj-check-emsdk |
| 846 | # |
| 847 | # Emscripten is used for doing in-tree builds of web-based WASM stuff, |
| @@ -918,10 +944,46 @@ | |
| 918 | } else { |
| 919 | define EMCC_WRAPPER "" |
| 920 | define EMCC_OPT "" |
| 921 | catch {exec rm -f tools/emcc.sh} |
| 922 | } |
| 923 | |
| 924 | # Tag container builds with a prefix of the checkin ID of the version |
| 925 | # of Fossil each one contains. This not only allows multiple images |
| 926 | # to coexist and multiple containers to be created unamgiguosly from |
| 927 | # them, it also changes the URL we fetch the source tarball from, so |
| 928 |
| --- auto.def | |
| +++ auto.def | |
| @@ -36,17 +36,17 @@ | |
| 36 | } |
| 37 | |
| 38 | # Update the minimum required SQLite version number here, and also |
| 39 | # in src/main.c near the sqlite3_libversion_number() call. Take care |
| 40 | # that both places agree! |
| 41 | define MINIMUM_SQLITE_VERSION "3.49.0" |
| 42 | |
| 43 | # This is useful for people wanting Fossil to use an external SQLite library |
| 44 | # to compare the one they have against the minimum required |
| 45 | if {[opt-bool print-minimum-sqlite-version]} { |
| 46 | puts [get-define MINIMUM_SQLITE_VERSION] |
| 47 | exit 0 |
| 48 | } |
| 49 | |
| 50 | # Space characters have never been allowed in either the source |
| 51 | # tree nor the build directory. But the resulting error messages |
| 52 | # could be confusing. The following checks make the reason for the |
| @@ -67,21 +67,21 @@ | |
| 67 | set outOfTreeBuild 1 |
| 68 | } |
| 69 | |
| 70 | # sqlite wants these types if possible |
| 71 | cc-with {-includes {stdint.h inttypes.h}} { |
| 72 | cc-check-types uint32_t uint16_t int16_t uint8_t |
| 73 | } |
| 74 | |
| 75 | # Use pread/pwrite system calls in place of seek + read/write if possible |
| 76 | define USE_PREAD [cc-check-functions pread] |
| 77 | |
| 78 | # If we have cscope here, we'll use it in the "tags" target |
| 79 | if {[cc-check-progs cscope]} { |
| 80 | define COLLECT_CSCOPE_DATA "cscope -bR $::autosetup(srcdir)/src/*.\[ch\]" |
| 81 | } else { |
| 82 | define COLLECT_CSCOPE_DATA "" |
| 83 | } |
| 84 | |
| 85 | # Find tclsh for the test suite. |
| 86 | # |
| 87 | # We can't use jimsh for this: the test suite uses features of Tcl that |
| @@ -94,32 +94,32 @@ | |
| 94 | # Ironically, this means we may right now be running under either jimsh0 |
| 95 | # or a version of tclsh that we find unsuitable below! |
| 96 | cc-check-progs tclsh |
| 97 | set hbtd /usr/local/Cellar/tcl-tk |
| 98 | if {[string equal false [get-define TCLSH]]} { |
| 99 | msg-result "WARNING: 'make test' will not run here." |
| 100 | } else { |
| 101 | set v [exec sh -c "echo 'puts \$tcl_version' | tclsh"] |
| 102 | if {[expr {$v >= 8.6}]} { |
| 103 | msg-result "Found Tclsh version $v in the PATH." |
| 104 | define TCLSH tclsh |
| 105 | } elseif {[file isdirectory $hbtd]} { |
| 106 | # This is a macOS system with the Homebrew version of Tcl/Tk |
| 107 | # installed. Select the newest version. It won't normally be |
| 108 | # in the PATH to avoid shadowing /usr/bin/tclsh, and even if it |
| 109 | # were in the PATH, it's bad practice to put /usr/local/bin (the |
| 110 | # Homebrew default) ahead of /usr/bin, especially given that |
| 111 | # it's user-writeable by default with Homebrew. Thus, we can be |
| 112 | # pretty sure the only way to call it is with an absolute path. |
| 113 | set v [exec ls -tr $hbtd | tail -1] |
| 114 | set path "$hbtd/$v/bin/tclsh" |
| 115 | define TCLSH $path |
| 116 | msg-result "Using Homebrew Tcl/Tk version $path." |
| 117 | } else { |
| 118 | msg-result "WARNING: tclsh $v found; need >= 8.6 for 'make test'." |
| 119 | define TCLSH false ;# force "make test" failure via /usr/bin/false |
| 120 | } |
| 121 | } |
| 122 | |
| 123 | define CFLAGS [get-env CFLAGS "-g -Os"] |
| 124 | define EXTRA_CFLAGS "-Wall" |
| 125 | define EXTRA_LDFLAGS "" |
| @@ -133,48 +133,48 @@ | |
| 133 | # SQLITE_OPTIONS_EXT => build-dependent CFLAGS for sqlite3.c and shell.c |
| 134 | |
| 135 | # Maintain the C89/C90-style order of variable declarations before statements. |
| 136 | # Check if the compiler supports the respective warning flag. |
| 137 | if {[cctest -cflags -Wdeclaration-after-statement]} { |
| 138 | define-append EXTRA_CFLAGS -Wdeclaration-after-statement |
| 139 | } |
| 140 | |
| 141 | |
| 142 | # This procedure is a customized version of "cc-check-function-in-lib", |
| 143 | # that does not modify the LIBS variable. Its use prevents prematurely |
| 144 | # pulling in libraries that will be added later anyhow (e.g. "-ldl"). |
| 145 | proc check-function-in-lib {function libs {otherlibs {}}} { |
| 146 | if {[string length $otherlibs]} { |
| 147 | msg-checking "Checking for $function in $libs with $otherlibs..." |
| 148 | } else { |
| 149 | msg-checking "Checking for $function in $libs..." |
| 150 | } |
| 151 | set found 0 |
| 152 | cc-with [list -libs $otherlibs] { |
| 153 | if {[cctest_function $function]} { |
| 154 | msg-result "none needed" |
| 155 | define lib_$function "" |
| 156 | incr found |
| 157 | } else { |
| 158 | foreach lib $libs { |
| 159 | cc-with [list -libs -l$lib] { |
| 160 | if {[cctest_function $function]} { |
| 161 | msg-result -l$lib |
| 162 | define lib_$function -l$lib |
| 163 | incr found |
| 164 | break |
| 165 | } |
| 166 | } |
| 167 | } |
| 168 | } |
| 169 | } |
| 170 | if {$found} { |
| 171 | define [feature-define-name $function] |
| 172 | } else { |
| 173 | msg-result "no" |
| 174 | } |
| 175 | return $found |
| 176 | } |
| 177 | |
| 178 | if {![opt-bool internal-sqlite]} { |
| 179 | proc find_system_sqlite {} { |
| 180 | |
| @@ -219,17 +219,17 @@ | |
| 219 | set sqlite-version [string cat "-D MINIMUM_SQLITE_VERSION=" [get-define MINIMUM_SQLITE_VERSION]] |
| 220 | lappend cmdline {*}[set sqlite-version] |
| 221 | set ok 1 |
| 222 | set err [catch {exec-with-stderr {*}$cmdline} result errinfo] |
| 223 | if {$err} { |
| 224 | configlog "Failed: [join $cmdline]" |
| 225 | if {[string length $result]>0} {configlog $result} |
| 226 | configlog "============" |
| 227 | set ok 0 |
| 228 | } elseif {$::autosetup(debug)} { |
| 229 | configlog "Compiled OK: [join $cmdline]" |
| 230 | configlog "============" |
| 231 | } |
| 232 | if {!$ok} { |
| 233 | user-error "unable to compile SQLite compatibility test program" |
| 234 | } |
| 235 | set err [catch {exec-with-stderr ./conftest__} result errinfo] |
| @@ -251,475 +251,516 @@ | |
| 251 | ![file exists "/dev/null"] |
| 252 | }] |
| 253 | } |
| 254 | |
| 255 | if {[is_mingw]} { |
| 256 | define-append EXTRA_CFLAGS -DBROKEN_MINGW_CMDLINE |
| 257 | define-append LIBS -lkernel32 -lws2_32 |
| 258 | } else { |
| 259 | # |
| 260 | # NOTE: All platforms except MinGW should use the linenoise |
| 261 | # package. It is currently unsupported on Win32. |
| 262 | # |
| 263 | define USE_LINENOISE 1 |
| 264 | } |
| 265 | |
| 266 | if {[string match *-solaris* [get-define host]]} { |
| 267 | define-append EXTRA_CFLAGS {-D__EXTENSIONS__} |
| 268 | } |
| 269 | |
| 270 | if {[opt-bool fossil-debug]} { |
| 271 | define CFLAGS {-g -O0 -Wall} |
| 272 | define-append CFLAGS -DFOSSIL_DEBUG |
| 273 | msg-result "Debugging support enabled" |
| 274 | } |
| 275 | |
| 276 | if {[opt-bool no-opt]} { |
| 277 | define CFLAGS {-g -O0 -Wall} |
| 278 | msg-result "Builting without compiler optimization" |
| 279 | if {[opt-bool fossil-debug]} { |
| 280 | define-append CFLAGS -DFOSSIL_DEBUG |
| 281 | } |
| 282 | } |
| 283 | |
| 284 | if {[opt-bool with-mman]} { |
| 285 | define-append EXTRA_CFLAGS -DUSE_MMAN_H |
| 286 | define USE_MMAN_H 1 |
| 287 | msg-result "Enabling \"sys/mman.h\" support" |
| 288 | } |
| 289 | |
| 290 | if {[opt-bool with-see]} { |
| 291 | define-append EXTRA_CFLAGS -DUSE_SEE |
| 292 | define USE_SEE 1 |
| 293 | define SQLITE3_ORIGIN 1 |
| 294 | msg-result "Enabling encryption support" |
| 295 | } |
| 296 | |
| 297 | if {[opt-bool json]} { |
| 298 | # Reminder/FIXME (stephan): FOSSIL_ENABLE_JSON |
| 299 | # is required in the CFLAGS because json*.c |
| 300 | # have #ifdef guards around the whole file without |
| 301 | # reading config.h first. |
| 302 | define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_JSON |
| 303 | define FOSSIL_ENABLE_JSON |
| 304 | msg-result "JSON support enabled" |
| 305 | } |
| 306 | |
| 307 | if {[opt-bool with-exec-rel-paths]} { |
| 308 | define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_EXEC_REL_PATHS |
| 309 | define FOSSIL_ENABLE_EXEC_REL_PATHS |
| 310 | msg-result "Relative paths in external diff/gdiff enabled" |
| 311 | } |
| 312 | |
| 313 | if {[opt-bool with-th1-docs]} { |
| 314 | define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_TH1_DOCS |
| 315 | define FOSSIL_ENABLE_TH1_DOCS |
| 316 | msg-result "TH1 embedded documentation support enabled" |
| 317 | } |
| 318 | |
| 319 | if {[opt-bool with-th1-hooks]} { |
| 320 | define-append EXTRA_CFLAGS -DFOSSIL_ENABLE_TH1_HOOKS |
| 321 | define FOSSIL_ENABLE_TH1_HOOKS |
| 322 | msg-result "TH1 hooks support enabled" |
| 323 | } |
| 324 | |
| 325 | #if {[opt-bool markdown]} { |
| 326 | # # no-op. Markdown is now enabled by default. |
| 327 | # msg-result "Markdown support enabled" |
| 328 | #} |
| 329 | |
| 330 | if {[opt-bool static]} { |
| 331 | # XXX: This will not work on all systems. |
| 332 | define-append EXTRA_LDFLAGS -static |
| 333 | msg-result "Trying to link statically" |
| 334 | } else { |
| 335 | define-append EXTRA_CFLAGS -DFOSSIL_DYNAMIC_BUILD=1 |
| 336 | define FOSSIL_DYNAMIC_BUILD |
| 337 | } |
| 338 | |
| 339 | # Check for libraries that need to be sorted out early |
| 340 | cc-check-function-in-lib iconv iconv |
| 341 | |
| 342 | cc-check-function-in-lib sin m |
| 343 | cc-check-function-in-lib dlopen dl |
| 344 | |
| 345 | # Helper for OpenSSL checking |
| 346 | proc check-for-openssl {msg {cflags {}} {libs {-lssl -lcrypto -lpthread}}} { |
| 347 | msg-checking "Checking for $msg..." |
| 348 | set rc 0 |
| 349 | if {[is_mingw]} { |
| 350 | lappend libs -lgdi32 -lwsock32 -lcrypt32 |
| 351 | } |
| 352 | if {[info exists ::zlib_lib]} { |
| 353 | lappend libs $::zlib_lib |
| 354 | } |
| 355 | msg-quiet cc-with [list -cflags $cflags -libs $libs] { |
| 356 | if {[cc-check-includes openssl/ssl.h] && \ |
| 357 | [cc-check-functions SSL_new]} { |
| 358 | incr rc |
| 359 | } |
| 360 | } |
| 361 | if {!$rc && ![is_mingw]} { |
| 362 | # On some systems, OpenSSL appears to require -ldl to link. |
| 363 | lappend libs -ldl |
| 364 | msg-quiet cc-with [list -cflags $cflags -libs $libs] { |
| 365 | if {[cc-check-includes openssl/ssl.h] && \ |
| 366 | [cc-check-functions SSL_new]} { |
| 367 | incr rc |
| 368 | } |
| 369 | } |
| 370 | } |
| 371 | if {$rc} { |
| 372 | msg-result "ok" |
| 373 | return 1 |
| 374 | } else { |
| 375 | msg-result "no" |
| 376 | return 0 |
| 377 | } |
| 378 | } |
| 379 | |
| 380 | # |
| 381 | # Check for zlib, using the given location if specified |
| 382 | # |
| 383 | proc handle-zlib {} { |
| 384 | set ::zlibpath [opt-val with-zlib]; # used by downstream tcl tests |
| 385 | if {$::zlibpath eq "tree"} { |
| 386 | set ::zlibdir [file dirname $::autosetup(dir)]/compat/zlib |
| 387 | if {![file isdirectory $::zlibdir]} { |
| 388 | user-error "The zlib in source tree directory does not exist" |
| 389 | } elseif { ([llength [glob -nocomplain -directory $::zlibdir libz*]] == 0) } { |
| 390 | user-error "With --with-zlib=tree, $::zlibdir must be configured and built first." |
| 391 | } |
| 392 | cc-with [list -cflags "-I$::zlibdir -L$::zlibdir"] |
| 393 | define-append EXTRA_CFLAGS -I$::zlibdir |
| 394 | define-append LIBS $::zlibdir/libz.a |
| 395 | set ::zlib_lib $::zlibdir/libz.a |
| 396 | msg-result "Using zlib in source tree" |
| 397 | } else { |
| 398 | set cftry {""} |
| 399 | set ldtry {""} |
| 400 | if {$::zlibpath ni {auto ""}} { |
| 401 | lappend cftry "-I$::zlibpath" |
| 402 | lappend cftry "-I$::zlibpath/include" |
| 403 | lappend ldtry "-L$::zlibpath" |
| 404 | lappend ldtry "-L$::zlibpath/lib" |
| 405 | } |
| 406 | |
| 407 | # Reverse the list of tests so we check most-specific to least, else |
| 408 | # platform devel files will shadow local --with-zlib overrides. |
| 409 | foreach c [lreverse $cftry] { |
| 410 | if {[cc-with [list -cflags $c] {cc-check-includes zlib.h}]} { |
| 411 | if {$c eq ""} { |
| 412 | msg-result "Found zlib.h in default include path" |
| 413 | } else { |
| 414 | define-append EXTRA_CFLAGS "$c" |
| 415 | msg-result "Found zlib.h via $c" |
| 416 | } |
| 417 | set cfound $c |
| 418 | break |
| 419 | } |
| 420 | } |
| 421 | if {![info exists cfound]} { |
| 422 | user-error "zlib.h not found; either install it or specify its location via --with-zlib" |
| 423 | } |
| 424 | foreach lcheck [lreverse $ldtry] { |
| 425 | if {[cc-with [list -cflags "$cfound $lcheck"] {check-function-in-lib inflateEnd z}]} { |
| 426 | if {$lcheck eq ""} { |
| 427 | msg-result "Linked to zlib via default library path" |
| 428 | } else { |
| 429 | define-append EXTRA_LDFLAGS "$lcheck" |
| 430 | msg-result "Linked to zlib via $lcheck" |
| 431 | } |
| 432 | if {![check-function-in-lib compressBound z]} { |
| 433 | puts "Notice: disabling zlib compression in the SQL shell" |
| 434 | define-append SQLITE_OPTIONS_EXT {-USQLITE_HAVE_ZLIB} |
| 435 | } |
| 436 | break |
| 437 | } |
| 438 | } |
| 439 | set ::zlib_lib -lz |
| 440 | } |
| 441 | }; # handle-zlib |
| 442 | handle-zlib |
| 443 | |
| 444 | # |
| 445 | # Handle the --with-openssl flag and, incidentally, update @LIBS@ for |
| 446 | # zlib if openssl is _not_ used (if it is, we get zlib via libssl). |
| 447 | # |
| 448 | # This function should be called as late as possible in the configure |
| 449 | # script to avoid that its updates to @LIBS@ break tests which follow |
| 450 | # it when a custom local build of openssl is used, as discussed in |
| 451 | # <https://fossil-scm.org/forum/forumpost/15e3d9cdc137030c>. |
| 452 | # |
| 453 | proc handle-with-openssl {} { |
| 454 | set ssldirs [opt-val with-openssl] |
| 455 | if {$ssldirs ne "none"} { |
| 456 | set found 0 |
| 457 | if {$ssldirs eq "tree"} { |
| 458 | set ssldir [file dirname $::autosetup(dir)]/compat/openssl |
| 459 | if {![file isdirectory $ssldir]} { |
| 460 | user-error "The OpenSSL in source tree directory does not exist" |
| 461 | } |
| 462 | set msg "openssl in $ssldir" |
| 463 | set cflags "-I$ssldir/include" |
| 464 | set ldflags "-L$ssldir" |
| 465 | set ssllibs "$ssldir/libssl.a $ssldir/libcrypto.a -lpthread" |
| 466 | set found [check-for-openssl "openssl in source tree" "$cflags $ldflags" $ssllibs] |
| 467 | } else { |
| 468 | if {$ssldirs in {auto ""}} { |
| 469 | catch { |
| 470 | # TODO?: use autosetup's pkg-config support |
| 471 | set cflags [exec pkg-config openssl --cflags-only-I] |
| 472 | set ldflags [exec pkg-config openssl --libs-only-L] |
| 473 | set found [check-for-openssl "ssl via pkg-config" "$cflags $ldflags"] |
| 474 | } msg |
| 475 | if {!$found} { |
| 476 | set ssldirs "{} /usr/sfw /usr/local/ssl /usr/lib/ssl /usr/ssl \ |
| 477 | /usr/pkg /usr/local /usr /usr/local/opt/openssl \ |
| 478 | /opt/homebrew/opt/openssl" |
| 479 | } |
| 480 | } |
| 481 | if {!$found} { |
| 482 | foreach dir $ssldirs { |
| 483 | if {$dir eq ""} { |
| 484 | set msg "system openssl" |
| 485 | set cflags "" |
| 486 | set ldflags "" |
| 487 | } else { |
| 488 | set msg "openssl in $dir" |
| 489 | set cflags "-I$dir/include" |
| 490 | if {[file readable $dir/libssl.a]} { |
| 491 | set ldflags -L$dir |
| 492 | } elseif {[file readable $dir/lib/libssl.a]} { |
| 493 | set ldflags -L$dir/lib |
| 494 | } elseif {[file isdir $dir/lib]} { |
| 495 | set ldflags "-L$dir -L$dir/lib" |
| 496 | } else { |
| 497 | set ldflags -L$dir |
| 498 | } |
| 499 | } |
| 500 | if {[check-for-openssl $msg "$cflags $ldflags"]} { |
| 501 | incr found |
| 502 | break |
| 503 | } |
| 504 | if {$dir ne ""} { |
| 505 | set ldflags "" |
| 506 | set msg "static build of openssl in $dir" |
| 507 | set ssllibs "$dir/libssl.a $dir/libcrypto.a -lpthread" |
| 508 | if {[check-for-openssl $msg "$cflags $ldflags" $ssllibs]} { |
| 509 | incr found |
| 510 | break |
| 511 | } |
| 512 | # This test should arguably fail here if --with-openssl=X |
| 513 | # points to an invalid X. |
| 514 | } |
| 515 | } |
| 516 | } |
| 517 | } |
| 518 | if {$found} { |
| 519 | define FOSSIL_ENABLE_SSL |
| 520 | define-append EXTRA_CFLAGS $cflags |
| 521 | define-append EXTRA_LDFLAGS $ldflags |
| 522 | if {[info exists ssllibs]} { |
| 523 | define-append LIBS $ssllibs |
| 524 | } else { |
| 525 | define-append LIBS -lssl -lcrypto |
| 526 | } |
| 527 | if {[info exists ::zlib_lib]} { |
| 528 | define-append LIBS $::zlib_lib |
| 529 | } |
| 530 | if {[is_mingw]} { |
| 531 | define-append LIBS -lgdi32 -lwsock32 -lcrypt32 |
| 532 | } |
| 533 | msg-result "HTTPS support enabled" |
| 534 | |
| 535 | # Silence OpenSSL deprecation warnings on Mac OS X 10.7. |
| 536 | if {[string match *-darwin* [get-define host]]} { |
| 537 | if {[cctest -cflags {-Wdeprecated-declarations}]} { |
| 538 | define-append EXTRA_CFLAGS -Wdeprecated-declarations |
| 539 | } |
| 540 | } |
| 541 | } else { |
| 542 | user-error "OpenSSL not found. Consider --with-openssl=none to disable HTTPS support" |
| 543 | } |
| 544 | } else { |
| 545 | if {[info exists ::zlib_lib]} { |
| 546 | define-append LIBS $::zlib_lib |
| 547 | } |
| 548 | } |
| 549 | }; # handle-with-openssl |
| 550 | |
| 551 | # |
| 552 | # CFLAGS_INCLUDE is ONLY for -I... flags and their order is |
| 553 | # significant so that --with-sqlite=PATH's header can shadow our |
| 554 | # own. <s>One caveat with this is that we cannot point |
| 555 | # --with-sqlite=PATH to the root of sqlite3's own build tree because |
| 556 | # that dir has a config.h which ends up shadowing src/config.h, |
| 557 | # breaking our build.</s> (That is no longer true: that that config.h |
| 558 | # was renamed to sqlite_cfg.h at some point.) |
| 559 | # |
| 560 | define CFLAGS_INCLUDE {} |
| 561 | |
| 562 | ######################################################################## |
| 563 | # --with-sqlite=PATH checks for the first it finds of the following... |
| 564 | # - PATH/sqlite3.c and PATH/sqlite3.h |
| 565 | # - PATH/sqlite3.o (and assumes sqlite3.h is with it) |
| 566 | # - PATH/lib/libsqlite3* and PATH/include/sqlite3.h |
| 567 | proc handle-with-sqlite {} { |
| 568 | set sq3path [opt-val with-sqlite] |
| 569 | define SQLITE3_SRC.2 {} |
| 570 | define SQLITE3_OBJ.2 {} |
| 571 | define SQLITE3_SHELL_SRC.2 {$(SQLITE3_SHELL_SRC.0)} |
| 572 | if {$sq3path in {tree ""}} { |
| 573 | msg-result "Using sqlite3.c from this source tree." |
| 574 | } else { |
| 575 | # SQLITE3_ORIGIN: |
| 576 | # 0 = local source tree |
| 577 | # 1 = use external lib or sqlite3.o |
| 578 | # 2 = use external sqlite3.c and (if found) shell.c |
| 579 | define USE_SYSTEM_SQLITE 1 |
| 580 | define SQLITE3_ORIGIN 2 |
| 581 | if {$sq3path != "auto"} { |
| 582 | if {([file exists $sq3path/sqlite3.c]) && |
| 583 | ([file exists $sq3path/sqlite3.h]) } { |
| 584 | # Prefer sqlite3.[ch] if found. |
| 585 | define SQLITE3_SRC.2 $sq3path/sqlite3.c |
| 586 | define SQLITE3_OBJ.2 {$(SQLITE3_OBJ.0)} |
| 587 | define USE_SYSTEM_SQLITE 2 |
| 588 | define SQLITE3_ORIGIN 2 |
| 589 | if {[file exists $sq3path/shell.c]} { |
| 590 | define SQLITE3_SHELL_SRC.2 $sq3path/shell.c |
| 591 | } |
| 592 | define-append CFLAGS_INCLUDE -I$sq3path |
| 593 | define-append EXTRA_LDFLAGS -lpthread |
| 594 | # ^^^ additional -lXXX flags are conservative estimates |
| 595 | msg-result "Using sqlite3.c and sqlite3.h from $sq3path" |
| 596 | } elseif {[file exists $sq3path/sqlite3.o]} { |
| 597 | # Use sqlite3.o if found. |
| 598 | define SQLITE3_OBJ.2 $sq3path/sqlite3.o |
| 599 | define-append CFLAGS_INCLUDE -I$sq3path |
| 600 | define-append EXTRA_LDFLAGS $sq3path/sqlite3.o -lpthread |
| 601 | # ^^^ additional -lXXX flags are conservative estimates |
| 602 | msg-result "Using sqlite3.o from $sq3path" |
| 603 | } elseif { ([llength [glob -nocomplain -directory $sq3path/lib libsqlite3*]] != 0) \ |
| 604 | && ([file exists $sq3path/include/sqlite3.h]) } { |
| 605 | # e.g. --with-sqlite=/usr/local. Try $sq3path/lib/libsqlite3* |
| 606 | # and $sq3path/include/sqlite3.h |
| 607 | define-append CFLAGS_INCLUDE -I$sq3path/include |
| 608 | define-append EXTRA_LDFLAGS -L$sq3path/lib -lsqlite3 -lpthread |
| 609 | # ^^^ additional -lXXX flags are conservative estimates |
| 610 | msg-result "Using -lsqlite3 from $sq3path" |
| 611 | } else { |
| 612 | # Assume $sq3path holds both the lib and header |
| 613 | cc-with [list -cflags "-I$sq3path -L$sq3path"] |
| 614 | define-append CFLAGS_INCLUDE -I$sq3path |
| 615 | define-append EXTRA_LDFLAGS -L$sq3path -lsqlite3 -lpthread |
| 616 | # ^^^ additional -lXXX flags are conservative estimates |
| 617 | msg-result "Using -lsqlite3 from $sq3path" |
| 618 | } |
| 619 | } elseif {![cc-check-includes sqlite3.h] || ![check-function-in-lib sqlite3_open_v2 sqlite3]} { |
| 620 | user-error "libsqlite3 not found please install it or specify the location with --with-sqlite" |
| 621 | } |
| 622 | } |
| 623 | }; # handle-with-sqlite |
| 624 | handle-with-sqlite |
| 625 | define-append CFLAGS_INCLUDE {-I. -I$(SRCDIR) -I$(SRCDIR_extsrc)}; # must be after handle-with-sqlite |
| 626 | |
| 627 | # |
| 628 | # Handle the --with-tcl flag. |
| 629 | # |
| 630 | proc handle-with-tcl {} { |
| 631 | set tclpath [opt-val with-tcl] |
| 632 | if {$tclpath eq ""} { |
| 633 | return |
| 634 | } |
| 635 | set tclprivatestubs [opt-bool with-tcl-private-stubs] |
| 636 | # Note parse-tclconfig-sh is in autosetup/local.tcl |
| 637 | if {$tclpath eq "1"} { |
| 638 | set tcldir [file dirname $::autosetup(dir)]/compat/tcl-8.6 |
| 639 | if {$tclprivatestubs} { |
| 640 | set tclconfig(TCL_INCLUDE_SPEC) -I$tcldir/generic |
| 641 | set tclconfig(TCL_VERSION) {Private Stubs} |
| 642 | set tclconfig(TCL_PATCH_LEVEL) {} |
| 643 | set tclconfig(TCL_PREFIX) $tcldir |
| 644 | set tclconfig(TCL_LD_FLAGS) { } |
| 645 | } else { |
| 646 | # Use the system Tcl. Look in some likely places. |
| 647 | array set tclconfig [parse-tclconfig-sh \ |
| 648 | $tcldir/unix $tcldir/win \ |
| 649 | /usr /usr/local /usr/share /opt/local] |
| 650 | set msg "on your system" |
| 651 | } |
| 652 | } else { |
| 653 | array set tclconfig [parse-tclconfig-sh $tclpath] |
| 654 | set msg "at $tclpath" |
| 655 | } |
| 656 | if {[opt-bool static]} { |
| 657 | set tclconfig(TCL_LD_FLAGS) { } |
| 658 | } |
| 659 | if {![info exists tclconfig(TCL_INCLUDE_SPEC)]} { |
| 660 | user-error "Cannot find Tcl $msg" |
| 661 | } |
| 662 | set tclstubs [opt-bool with-tcl-stubs] |
| 663 | if {$tclprivatestubs} { |
| 664 | define FOSSIL_ENABLE_TCL_PRIVATE_STUBS |
| 665 | define USE_TCL_STUBS |
| 666 | } elseif {$tclstubs && $tclconfig(TCL_SUPPORTS_STUBS)} { |
| 667 | set libs "$tclconfig(TCL_STUB_LIB_SPEC)" |
| 668 | define FOSSIL_ENABLE_TCL_STUBS |
| 669 | define USE_TCL_STUBS |
| 670 | } else { |
| 671 | set libs "$tclconfig(TCL_LIB_SPEC) $tclconfig(TCL_LIBS)" |
| 672 | } |
| 673 | set cflags $tclconfig(TCL_INCLUDE_SPEC) |
| 674 | if {!$tclprivatestubs} { |
| 675 | set foundtcl 0; # Did we find a working Tcl library? |
| 676 | cc-with [list -cflags $cflags -libs $libs] { |
| 677 | if {$tclstubs} { |
| 678 | if {[cc-check-functions Tcl_InitStubs]} { |
| 679 | set foundtcl 1 |
| 680 | } |
| 681 | } else { |
| 682 | if {[cc-check-functions Tcl_CreateInterp]} { |
| 683 | set foundtcl 1 |
| 684 | } |
| 685 | } |
| 686 | } |
| 687 | if {!$foundtcl && [string match *-lieee* $libs]} { |
| 688 | # On some systems, using "-lieee" from TCL_LIB_SPEC appears |
| 689 | # to cause issues. |
| 690 | msg-result "Removing \"-lieee\" and retrying for Tcl..." |
| 691 | set libs [string map [list -lieee ""] $libs] |
| 692 | cc-with [list -cflags $cflags -libs $libs] { |
| 693 | if {$tclstubs} { |
| 694 | if {[cc-check-functions Tcl_InitStubs]} { |
| 695 | set foundtcl 1 |
| 696 | } |
| 697 | } else { |
| 698 | if {[cc-check-functions Tcl_CreateInterp]} { |
| 699 | set foundtcl 1 |
| 700 | } |
| 701 | } |
| 702 | } |
| 703 | } |
| 704 | if {!$foundtcl && ![string match *-lpthread* $libs]} { |
| 705 | # On some systems, TCL_LIB_SPEC appears to be missing |
| 706 | # "-lpthread". Try adding it. |
| 707 | msg-result "Adding \"-lpthread\" and retrying for Tcl..." |
| 708 | set libs "$libs -lpthread" |
| 709 | cc-with [list -cflags $cflags -libs $libs] { |
| 710 | if {$tclstubs} { |
| 711 | if {[cc-check-functions Tcl_InitStubs]} { |
| 712 | set foundtcl 1 |
| 713 | } |
| 714 | } else { |
| 715 | if {[cc-check-functions Tcl_CreateInterp]} { |
| 716 | set foundtcl 1 |
| 717 | } |
| 718 | } |
| 719 | } |
| 720 | } |
| 721 | if {!$foundtcl} { |
| 722 | if {$tclstubs} { |
| 723 | user-error "Cannot find a usable Tcl stubs library $msg" |
| 724 | } else { |
| 725 | user-error "Cannot find a usable Tcl library $msg" |
| 726 | } |
| 727 | } |
| 728 | } |
| 729 | set version $tclconfig(TCL_VERSION)$tclconfig(TCL_PATCH_LEVEL) |
| 730 | msg-result "Found Tcl $version at $tclconfig(TCL_PREFIX)" |
| 731 | if {!$tclprivatestubs} { |
| 732 | define-append LIBS $libs |
| 733 | } |
| 734 | define-append EXTRA_CFLAGS $cflags |
| 735 | define-append CFLAGS $cflags |
| 736 | if {[info exists ::zlibpath] && $::zlibpath eq "tree"} { |
| 737 | # |
| 738 | # NOTE: When using zlib in the source tree, prevent Tcl from |
| 739 | # pulling in the system one. |
| 740 | # |
| 741 | set tclconfig(TCL_LD_FLAGS) [string map [list -lz ""] \ |
| 742 | $tclconfig(TCL_LD_FLAGS)] |
| 743 | } |
| 744 | # |
| 745 | # NOTE: Remove "-ldl" from the TCL_LD_FLAGS because it will be |
| 746 | # be checked for near the bottom of this file. |
| 747 | # |
| 748 | set tclconfig(TCL_LD_FLAGS) [string map [list -ldl ""] \ |
| 749 | $tclconfig(TCL_LD_FLAGS)] |
| 750 | define-append EXTRA_LDFLAGS $tclconfig(TCL_LD_FLAGS) |
| 751 | define FOSSIL_ENABLE_TCL |
| 752 | }; # handle-with-tcl |
| 753 | handle-with-tcl |
| 754 | |
| 755 | # Network functions require libraries on some systems |
| 756 | cc-check-function-in-lib gethostbyname nsl |
| 757 | if {![cc-check-function-in-lib socket {socket network}]} { |
| 758 | # Last resort, may be Windows |
| 759 | if {[is_mingw]} { |
| 760 | define-append LIBS -lwsock32 |
| 761 | } |
| 762 | } |
| 763 | |
| 764 | # Some systems (ex: SunOS) require -lrt in order to use nanosleep |
| 765 | cc-check-function-in-lib nanosleep rt |
| 766 | |
| @@ -734,11 +775,11 @@ | |
| 775 | [cc-check-function-in-lib __ns_name_uncompress {bind resolv}]) && |
| 776 | ([cc-check-function-in-lib ns_parserr {bind resolv}] || |
| 777 | [cc-check-function-in-lib __ns_parserr {bind resolv}]) && |
| 778 | ([cc-check-function-in-lib res_query {bind resolv}] || |
| 779 | [cc-check-function-in-lib __res_query {bind resolv}]))} { |
| 780 | msg-result "WARNING: SMTP feature will not be able to look up local MX." |
| 781 | } |
| 782 | cc-check-function-in-lib res_9_ns_initparse resolv |
| 783 | |
| 784 | # Other nonstandard function checks |
| 785 | cc-check-functions utime |
| @@ -749,12 +790,12 @@ | |
| 790 | |
| 791 | # Termux on Android adds "getpass(char *)" to unistd.h, so check this so we |
| 792 | # guard against including it again; use cctest as cc-check-functions and |
| 793 | # cctest_function check for "getpass()" with no args and fail |
| 794 | if {[cctest -link 1 -includes {unistd.h} -code "getpass(0);"]} { |
| 795 | define FOSSIL_HAVE_GETPASS 1 |
| 796 | msg-result "Found getpass() with unistd.h" |
| 797 | } |
| 798 | |
| 799 | # Check for getloadavg(), and if it doesn't exist, define FOSSIL_OMIT_LOAD_AVERAGE |
| 800 | if {![cc-check-functions getloadavg] || |
| 801 | ![cctest -link 1 -includes {unistd.h} -code "double a\[3\]; getloadavg(a,3);"]} { |
| @@ -762,44 +803,43 @@ | |
| 803 | msg-result "Load average support unavailable" |
| 804 | } |
| 805 | |
| 806 | # Check for getpassphrase() for Solaris 10 where getpass() truncates to 10 chars |
| 807 | if {![cc-check-functions getpassphrase]} { |
| 808 | # Haiku needs this |
| 809 | cc-check-function-in-lib getpass bsd |
| 810 | } |
| 811 | |
| 812 | # Check for the FuseFS library |
| 813 | if {[opt-bool fusefs]} { |
| 814 | if {[opt-bool static]} { |
| 815 | msg-result "FuseFS support disabled due to -static" |
| 816 | } elseif {[cc-check-function-in-lib fuse_mount fuse]} { |
| 817 | define-append EXTRA_CFLAGS -DFOSSIL_HAVE_FUSEFS |
| 818 | define FOSSIL_HAVE_FUSEFS 1 |
| 819 | msg-result "FuseFS support enabled" |
| 820 | } |
| 821 | } |
| 822 | |
| 823 | ######################################################################## |
| 824 | # Checks the compiler for compile_commands.json support. |
| 825 | # |
| 826 | # Returns 1 if supported, else 0. Defines MAKE_COMPILATION_DB to "yes" |
| 827 | # if supported, "no" if not. |
| 828 | proc check-compile-commands {} { |
| 829 | msg-checking "compile_commands.json support... " |
| 830 | if {[cctest -lang c -cflags {/dev/null -MJ} -source {}]} { |
| 831 | # This test reportedly incorrectly succeeds on one of |
| 832 | # Martin G.'s older systems. |
| 833 | msg-result "compiler supports compile_commands.json" |
| 834 | define MAKE_COMPILATION_DB yes |
| 835 | return 1 |
| 836 | } else { |
| 837 | msg-result "compiler does not support compile_commands.json" |
| 838 | define MAKE_COMPILATION_DB no |
| 839 | return 0 |
| 840 | } |
| 841 | } |
| 842 | |
| 843 | define MAKE_COMPILATION_DB no |
| 844 | if {!$outOfTreeBuild} { |
| 845 | if {[opt-bool compile-commands]} { |
| @@ -816,32 +856,18 @@ | |
| 856 | # Add -fsanitize compile and link options late: we don't want the C |
| 857 | # checks above to run with those sanitizers enabled. It can not only |
| 858 | # be pointless, it can actually break correct tests. |
| 859 | set fsan [opt-val with-sanitizer] |
| 860 | if {[string length $fsan]} { |
| 861 | define-append EXTRA_CFLAGS -fsanitize=$fsan |
| 862 | define-append EXTRA_LDFLAGS -fsanitize=$fsan |
| 863 | if {[string first "undefined" $fsan] != -1} { |
| 864 | # We need to link with libubsan if we're compiling under |
| 865 | # GCC with -fsanitize=undefined. |
| 866 | cc-check-function-in-lib __ubsan_handle_add_overflow ubsan |
| 867 | } |
| 868 | } |
| 869 | |
| 870 | ######################################################################## |
| 871 | # @proj-check-emsdk |
| 872 | # |
| 873 | # Emscripten is used for doing in-tree builds of web-based WASM stuff, |
| @@ -918,10 +944,46 @@ | |
| 944 | } else { |
| 945 | define EMCC_WRAPPER "" |
| 946 | define EMCC_OPT "" |
| 947 | catch {exec rm -f tools/emcc.sh} |
| 948 | } |
| 949 | |
| 950 | handle-with-openssl |
| 951 | |
| 952 | # Finally, append libraries that must be last. This matters more on some |
| 953 | # OSes than others, but is most broadly required for static linking. |
| 954 | if {[opt-bool static]} { |
| 955 | # Linux can only infer the dependency on pthread from OpenSSL when |
| 956 | # doing dynamic linkage. |
| 957 | define-append LIBS -lpthread |
| 958 | } |
| 959 | |
| 960 | apply {{} { |
| 961 | # This started out as a workaround for getting the ordering of -ldl |
| 962 | # correct in conjunction with openssl in some environments. Then it |
| 963 | # evolved into a more generic preemptive portability workaround to |
| 964 | # ensure that certain libraries are always appended to the global |
| 965 | # LIBS list if they exist on the system. Based on a /chat discussion |
| 966 | # on 2025-02-27 in the context of check-in [8d3b9bf4d4]. |
| 967 | # |
| 968 | # Note that [move-lib-to-end] and [lib-actually-exists] are in |
| 969 | # autosetup/local.tcl. |
| 970 | set libs [get-define LIBS] |
| 971 | #puts "**** 1 LIBS: $libs" |
| 972 | foreach ll {-ldl -lpthread -lm} { |
| 973 | if {![move-lib-to-end $ll $libs libs]} { |
| 974 | # $ll was not in the list |
| 975 | if {[lib-actually-exists $ll]} { |
| 976 | # Add it to the list "just in case." This will be a no-op on |
| 977 | # systems where the lib is not actually used. |
| 978 | lappend libs $ll |
| 979 | } |
| 980 | } |
| 981 | } |
| 982 | #puts "**** 2 LIBS: $libs" |
| 983 | define LIBS [join $libs " "] |
| 984 | }} |
| 985 | |
| 986 | # Tag container builds with a prefix of the checkin ID of the version |
| 987 | # of Fossil each one contains. This not only allows multiple images |
| 988 | # to coexist and multiple containers to be created unamgiguosly from |
| 989 | # them, it also changes the URL we fetch the source tarball from, so |
| 990 |
+30
| --- autosetup/local.tcl | ||
| +++ autosetup/local.tcl | ||
| @@ -27,5 +27,35 @@ | ||
| 27 | 27 | set tclconfig($name) [string trim $value '] |
| 28 | 28 | } |
| 29 | 29 | } |
| 30 | 30 | return [array get tclconfig] |
| 31 | 31 | } |
| 32 | + | |
| 33 | +# | |
| 34 | +# Given a library link flag, e.g. -lfoo, returns 1 if that library can | |
| 35 | +# actually be linked to, else returns 0. | |
| 36 | +proc lib-actually-exists {linkFlag} { | |
| 37 | + cctest -link 1 -code "void libActuallyExists(void){}" -libs $linkFlag | |
| 38 | +} | |
| 39 | + | |
| 40 | +# | |
| 41 | +# Given a library flag, e.g. -lfoo, a list of libs, e.g. {-lfoo -lbar | |
| 42 | +# -lbaz}, and a target variable name, this function appends all | |
| 43 | +# entries of $libList which do not match $flag to $tgtVar, then | |
| 44 | +# appends $flag to the end of $tgtVar. Returns the number of matches | |
| 45 | +# found. | |
| 46 | +proc move-lib-to-end {flag libList tgtVar} { | |
| 47 | + upvar $tgtVar tgt | |
| 48 | + set tgt {} | |
| 49 | + set found 0 | |
| 50 | + foreach e $libList { | |
| 51 | + if {$flag eq $e} { | |
| 52 | + incr found | |
| 53 | + } else { | |
| 54 | + lappend tgt $e | |
| 55 | + } | |
| 56 | + } | |
| 57 | + if {$found} { | |
| 58 | + lappend tgt $flag | |
| 59 | + } | |
| 60 | + return $found | |
| 61 | +} | |
| 32 | 62 | |
| 33 | 63 | ADDED extsrc/linenoise-win32.c |
| --- autosetup/local.tcl | |
| +++ autosetup/local.tcl | |
| @@ -27,5 +27,35 @@ | |
| 27 | set tclconfig($name) [string trim $value '] |
| 28 | } |
| 29 | } |
| 30 | return [array get tclconfig] |
| 31 | } |
| 32 | |
| 33 | DDED extsrc/linenoise-win32.c |
| --- autosetup/local.tcl | |
| +++ autosetup/local.tcl | |
| @@ -27,5 +27,35 @@ | |
| 27 | set tclconfig($name) [string trim $value '] |
| 28 | } |
| 29 | } |
| 30 | return [array get tclconfig] |
| 31 | } |
| 32 | |
| 33 | # |
| 34 | # Given a library link flag, e.g. -lfoo, returns 1 if that library can |
| 35 | # actually be linked to, else returns 0. |
| 36 | proc lib-actually-exists {linkFlag} { |
| 37 | cctest -link 1 -code "void libActuallyExists(void){}" -libs $linkFlag |
| 38 | } |
| 39 | |
| 40 | # |
| 41 | # Given a library flag, e.g. -lfoo, a list of libs, e.g. {-lfoo -lbar |
| 42 | # -lbaz}, and a target variable name, this function appends all |
| 43 | # entries of $libList which do not match $flag to $tgtVar, then |
| 44 | # appends $flag to the end of $tgtVar. Returns the number of matches |
| 45 | # found. |
| 46 | proc move-lib-to-end {flag libList tgtVar} { |
| 47 | upvar $tgtVar tgt |
| 48 | set tgt {} |
| 49 | set found 0 |
| 50 | foreach e $libList { |
| 51 | if {$flag eq $e} { |
| 52 | incr found |
| 53 | } else { |
| 54 | lappend tgt $e |
| 55 | } |
| 56 | } |
| 57 | if {$found} { |
| 58 | lappend tgt $flag |
| 59 | } |
| 60 | return $found |
| 61 | } |
| 62 | |
| 63 | DDED extsrc/linenoise-win32.c |
+379
| --- a/extsrc/linenoise-win32.c | ||
| +++ b/extsrc/linenoise-win32.c | ||
| @@ -0,0 +1,379 @@ | ||
| 1 | + | |
| 2 | +/* this code is not standalone | |
| 3 | + * it is included into linenoise.c | |
| 4 | + * for windows. | |
| 5 | + * It is deliberately kept separate so that | |
| 6 | + * applications that have no need for windows | |
| 7 | + * support can omit this | |
| 8 | + */ | |
| 9 | +static DWORD orig_consolemode = 0; | |
| 10 | + | |
| 11 | +static int flushOutput(struct current *current); | |
| 12 | +static void outputNewline(struct current *current); | |
| 13 | + | |
| 14 | +static void refreshStart(struct current *current) | |
| 15 | +{ | |
| 16 | + (void)current; | |
| 17 | +} | |
| 18 | + | |
| 19 | +static void refreshEnd(struct current *current) | |
| 20 | +{ | |
| 21 | + (void)current; | |
| 22 | +} | |
| 23 | + | |
| 24 | +static void refreshStartChars(struct current *current) | |
| 25 | +{ | |
| 26 | + assert(current->output == NULL); | |
| 27 | + /* We accumulate all output here */ | |
| 28 | + current->output = sb_alloc(); | |
| 29 | +#ifdef USE_UTF8 | |
| 30 | + current->ubuflen = 0; | |
| 31 | +#endif | |
| 32 | +} | |
| 33 | + | |
| 34 | +static void refreshNewline(struct current *current) | |
| 35 | +{ | |
| 36 | + DRL("<nl>"); | |
| 37 | + outputNewline(current); | |
| 38 | +} | |
| 39 | + | |
| 40 | +static void refreshEndChars(struct current *current) | |
| 41 | +{ | |
| 42 | + assert(current->output); | |
| 43 | + flushOutput(current); | |
| 44 | + sb_free(current->output); | |
| 45 | + current->output = NULL; | |
| 46 | +} | |
| 47 | + | |
| 48 | +static int enableRawMode(struct current *current) { | |
| 49 | + DWORD n; | |
| 50 | + INPUT_RECORD irec; | |
| 51 | + | |
| 52 | + current->outh = GetStdHandle(STD_OUTPUT_HANDLE); | |
| 53 | + current->inh = GetStdHandle(STD_INPUT_HANDLE); | |
| 54 | + | |
| 55 | + if (!PeekConsoleInput(current->inh, &irec, 1, &n)) { | |
| 56 | + return -1; | |
| 57 | + } | |
| 58 | + if (getWindowSize(current) != 0) { | |
| 59 | + return -1; | |
| 60 | + } | |
| 61 | + if (GetConsoleMode(current->inh, &orig_consolemode)) { | |
| 62 | + SetConsoleMode(current->inh, ENABLE_PROCESSED_INPUT); | |
| 63 | + } | |
| 64 | +#ifdef USE_UTF8 | |
| 65 | + /* XXX is this the right thing to do? */ | |
| 66 | + SetConsoleCP(65001); | |
| 67 | +#endif | |
| 68 | + return 0; | |
| 69 | +} | |
| 70 | + | |
| 71 | +static void disableRawMode(struct current *current) | |
| 72 | +{ | |
| 73 | + SetConsoleMode(current->inh, orig_consolemode); | |
| 74 | +} | |
| 75 | + | |
| 76 | +void linenoiseClearScreen(void) | |
| 77 | +{ | |
| 78 | + /* XXX: This is ugly. Should just have the caller pass a handle */ | |
| 79 | + struct current current; | |
| 80 | + | |
| 81 | + current.outh = GetStdHandle(STD_OUTPUT_HANDLE); | |
| 82 | + | |
| 83 | + if (getWindowSize(¤t) == 0) { | |
| 84 | + COORD topleft = { 0, 0 }; | |
| 85 | + DWORD n; | |
| 86 | + | |
| 87 | + FillConsoleOutputCharacter(current.outh, ' ', | |
| 88 | + current.cols * current.rows, topleft, &n); | |
| 89 | + FillConsoleOutputAttribute(current.outh, | |
| 90 | + FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN, | |
| 91 | + current.cols * current.rows, topleft, &n); | |
| 92 | + SetConsoleCursorPosition(current.outh, topleft); | |
| 93 | + } | |
| 94 | +} | |
| 95 | + | |
| 96 | +static void cursorToLeft(struct current *current) | |
| 97 | +{ | |
| 98 | + COORD pos; | |
| 99 | + DWORD n; | |
| 100 | + | |
| 101 | + pos.X = 0; | |
| 102 | + pos.Y = (SHORT)current->y; | |
| 103 | + | |
| 104 | + FillConsoleOutputAttribute(current->outh, | |
| 105 | + FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN, current->cols, pos, &n); | |
| 106 | + current->x = 0; | |
| 107 | +} | |
| 108 | + | |
| 109 | +#ifdef USE_UTF8 | |
| 110 | +static void flush_ubuf(struct current *current) | |
| 111 | +{ | |
| 112 | + COORD pos; | |
| 113 | + DWORD nwritten; | |
| 114 | + pos.Y = (SHORT)current->y; | |
| 115 | + pos.X = (SHORT)current->x; | |
| 116 | + SetConsoleCursorPosition(current->outh, pos); | |
| 117 | + WriteConsoleW(current->outh, current->ubuf, current->ubuflen, &nwritten, 0); | |
| 118 | + current->x += current->ubufcols; | |
| 119 | + current->ubuflen = 0; | |
| 120 | + current->ubufcols = 0; | |
| 121 | +} | |
| 122 | + | |
| 123 | +static void add_ubuf(struct current *current, int ch) | |
| 124 | +{ | |
| 125 | + /* This code originally by: Author: Mark E. Davis, 1994. */ | |
| 126 | + static const int halfShift = 10; /* used for shifting by 10 bits */ | |
| 127 | + | |
| 128 | + static const DWORD halfBase = 0x0010000UL; | |
| 129 | + static const DWORD halfMask = 0x3FFUL; | |
| 130 | + | |
| 131 | + #define UNI_SUR_HIGH_START 0xD800 | |
| 132 | + #define UNI_SUR_HIGH_END 0xDBFF | |
| 133 | + #define UNI_SUR_LOW_START 0xDC00 | |
| 134 | + #define UNI_SUR_LOW_END 0xDFFF | |
| 135 | + | |
| 136 | + #define UNI_MAX_BMP 0x0000FFFF | |
| 137 | + | |
| 138 | + if (ch > UNI_MAX_BMP) { | |
| 139 | + /* convert from unicode to utf16 surrogate pairs | |
| 140 | + * There is always space for one extra word in ubuf | |
| 141 | + */ | |
| 142 | + ch -= halfBase; | |
| 143 | + current->ubuf[current->ubuflen++] = (WORD)((ch >> halfShift) + UNI_SUR_HIGH_START); | |
| 144 | + current->ubuf[current->ubuflen++] = (WORD)((ch & halfMask) + UNI_SUR_LOW_START); | |
| 145 | + } | |
| 146 | + else { | |
| 147 | + current->ubuf[current->ubuflen++] = ch; | |
| 148 | + } | |
| 149 | + current->ubufcols += utf8_width(ch); | |
| 150 | + if (current->ubuflen >= UBUF_MAX_CHARS) { | |
| 151 | + flush_ubuf(current); | |
| 152 | + } | |
| 153 | +} | |
| 154 | +#endif | |
| 155 | + | |
| 156 | +static int flushOutput(struct current *current) | |
| 157 | +{ | |
| 158 | + const char *pt = sb_str(current->output); | |
| 159 | + int len = sb_len(current->output); | |
| 160 | + | |
| 161 | +#ifdef USE_UTF8 | |
| 162 | + /* convert utf8 in current->output into utf16 in current->ubuf | |
| 163 | + */ | |
| 164 | + while (len) { | |
| 165 | + int ch; | |
| 166 | + int n = utf8_tounicode(pt, &ch); | |
| 167 | + | |
| 168 | + pt += n; | |
| 169 | + len -= n; | |
| 170 | + | |
| 171 | + add_ubuf(current, ch); | |
| 172 | + } | |
| 173 | + flush_ubuf(current); | |
| 174 | +#else | |
| 175 | + DWORD nwritten; | |
| 176 | + COORD pos; | |
| 177 | + | |
| 178 | + pos.Y = (SHORT)current->y; | |
| 179 | + pos.X = (SHORT)current->x; | |
| 180 | + | |
| 181 | + SetConsoleCursorPosition(current->outh, pos); | |
| 182 | + WriteConsoleA(current->outh, pt, len, &nwritten, 0); | |
| 183 | + | |
| 184 | + current->x += len; | |
| 185 | +#endif | |
| 186 | + | |
| 187 | + sb_clear(current->output); | |
| 188 | + | |
| 189 | + return 0; | |
| 190 | +} | |
| 191 | + | |
| 192 | +static int outputChars(struct current *current, const char *buf, int len) | |
| 193 | +{ | |
| 194 | + if (len < 0) { | |
| 195 | + len = strlen(buf); | |
| 196 | + } | |
| 197 | + assert(current->output); | |
| 198 | + | |
| 199 | + sb_append_len(current->output, buf, len); | |
| 200 | + | |
| 201 | + return 0; | |
| 202 | +} | |
| 203 | + | |
| 204 | +static void outputNewline(struct current *current) | |
| 205 | +{ | |
| 206 | + /* On the last row output a newline to force a scroll */ | |
| 207 | + if (current->y + 1 == current->rows) { | |
| 208 | + outputChars(current, "\n", 1); | |
| 209 | + } | |
| 210 | + flushOutput(current); | |
| 211 | + current->x = 0; | |
| 212 | + current->y++; | |
| 213 | +} | |
| 214 | + | |
| 215 | +static void setOutputHighlight(struct current *current, const int *props, int nprops) | |
| 216 | +{ | |
| 217 | + int colour = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN; | |
| 218 | + int bold = 0; | |
| 219 | + int reverse = 0; | |
| 220 | + int i; | |
| 221 | + | |
| 222 | + for (i = 0; i < nprops; i++) { | |
| 223 | + switch (props[i]) { | |
| 224 | + case 0: | |
| 225 | + colour = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN; | |
| 226 | + bold = 0; | |
| 227 | + reverse = 0; | |
| 228 | + break; | |
| 229 | + case 1: | |
| 230 | + bold = FOREGROUND_INTENSITY; | |
| 231 | + break; | |
| 232 | + case 7: | |
| 233 | + reverse = 1; | |
| 234 | + break; | |
| 235 | + case 30: | |
| 236 | + colour = 0; | |
| 237 | + break; | |
| 238 | + case 31: | |
| 239 | + colour = FOREGROUND_RED; | |
| 240 | + break; | |
| 241 | + case 32: | |
| 242 | + colour = FOREGROUND_GREEN; | |
| 243 | + break; | |
| 244 | + case 33: | |
| 245 | + colour = FOREGROUND_RED | FOREGROUND_GREEN; | |
| 246 | + break; | |
| 247 | + case 34: | |
| 248 | + colour = FOREGROUND_BLUE; | |
| 249 | + break; | |
| 250 | + case 35: | |
| 251 | + colour = FOREGROUND_RED | FOREGROUND_BLUE; | |
| 252 | + break; | |
| 253 | + case 36: | |
| 254 | + colour = FOREGROUND_BLUE | FOREGROUND_GREEN; | |
| 255 | + break; | |
| 256 | + case 37: | |
| 257 | + colour = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN; | |
| 258 | + break; | |
| 259 | + } | |
| 260 | + } | |
| 261 | + | |
| 262 | + flushOutput(current); | |
| 263 | + | |
| 264 | + if (reverse) { | |
| 265 | + SetConsoleTextAttribute(current->outh, BACKGROUND_INTENSITY); | |
| 266 | + } | |
| 267 | + else { | |
| 268 | + SetConsoleTextAttribute(current->outh, colour | bold); | |
| 269 | + } | |
| 270 | +} | |
| 271 | + | |
| 272 | +static void eraseEol(struct current *current) | |
| 273 | +{ | |
| 274 | + COORD pos; | |
| 275 | + DWORD n; | |
| 276 | + | |
| 277 | + pos.X = (SHORT) current->x; | |
| 278 | + pos.Y = (SHORT) current->y; | |
| 279 | + | |
| 280 | + FillConsoleOutputCharacter(current->outh, ' ', current->cols - current->x, pos, &n); | |
| 281 | +} | |
| 282 | + | |
| 283 | +static void setCursorXY(struct current *current) | |
| 284 | +{ | |
| 285 | + COORD pos; | |
| 286 | + | |
| 287 | + pos.X = (SHORT) current->x; | |
| 288 | + pos.Y = (SHORT) current->y; | |
| 289 | + | |
| 290 | + SetConsoleCursorPosition(current->outh, pos); | |
| 291 | +} | |
| 292 | + | |
| 293 | + | |
| 294 | +static void setCursorPos(struct current *current, int x) | |
| 295 | +{ | |
| 296 | + current->x = x; | |
| 297 | + setCursorXY(current); | |
| 298 | +} | |
| 299 | + | |
| 300 | +static void cursorUp(struct current *current, int n) | |
| 301 | +{ | |
| 302 | + current->y -= n; | |
| 303 | + setCursorXY(current); | |
| 304 | +} | |
| 305 | + | |
| 306 | +static void cursorDown(struct current *current, int n) | |
| 307 | +{ | |
| 308 | + current->y += n; | |
| 309 | + setCursorXY(current); | |
| 310 | +} | |
| 311 | + | |
| 312 | +static int fd_read(struct current *current) | |
| 313 | +{ | |
| 314 | + while (1) { | |
| 315 | + INPUT_RECORD irec; | |
| 316 | + DWORD n; | |
| 317 | + if (WaitForSingleObject(current->inh, INFINITE) != WAIT_OBJECT_0) { | |
| 318 | + break; | |
| 319 | + } | |
| 320 | + if (!ReadConsoleInputW(current->inh, &irec, 1, &n)) { | |
| 321 | + break; | |
| 322 | + } | |
| 323 | + if (irec.EventType == KEY_EVENT) { | |
| 324 | + KEY_EVENT_RECORD *k = &irec.Event.KeyEvent; | |
| 325 | + if (k->bKeyDown || k->wVirtualKeyCode == VK_MENU) { | |
| 326 | + if (k->dwControlKeyState & ENHANCED_KEY) { | |
| 327 | + switch (k->wVirtualKeyCode) { | |
| 328 | + case VK_LEFT: | |
| 329 | + return SPECIAL_LEFT; | |
| 330 | + case VK_RIGHT: | |
| 331 | + return SPECIAL_RIGHT; | |
| 332 | + case VK_UP: | |
| 333 | + return SPECIAL_UP; | |
| 334 | + case VK_DOWN: | |
| 335 | + return SPECIAL_DOWN; | |
| 336 | + case VK_INSERT: | |
| 337 | + return SPECIAL_INSERT; | |
| 338 | + case VK_DELETE: | |
| 339 | + return SPECIAL_DELETE; | |
| 340 | + case VK_HOME: | |
| 341 | + return SPECIAL_HOME; | |
| 342 | + case VK_END: | |
| 343 | + return SPECIAL_END; | |
| 344 | + case VK_PRIOR: | |
| 345 | + return SPECIAL_PAGE_UP; | |
| 346 | + case VK_NEXT: | |
| 347 | + return SPECIAL_PAGE_DOWN; | |
| 348 | + case VK_RETURN: | |
| 349 | + return k->uChar.UnicodeChar; | |
| 350 | + } | |
| 351 | + } | |
| 352 | + /* Note that control characters are already translated in AsciiChar */ | |
| 353 | + else if (k->wVirtualKeyCode == VK_CONTROL) | |
| 354 | + continue; | |
| 355 | + else { | |
| 356 | + return k->uChar.UnicodeChar; | |
| 357 | + } | |
| 358 | + } | |
| 359 | + } | |
| 360 | + } | |
| 361 | + return -1; | |
| 362 | +} | |
| 363 | + | |
| 364 | +static int getWindowSize(struct current *current) | |
| 365 | +{ | |
| 366 | + CONSOLE_SCREEN_BUFFER_INFO info; | |
| 367 | + if (!GetConsoleScreenBufferInfo(current->outh, &info)) { | |
| 368 | + return -1; | |
| 369 | + } | |
| 370 | + current->cols = info.dwSize.X; | |
| 371 | + current->rows = info.dwSize.Y; | |
| 372 | + if (current->cols <= 0 || current->rows <= 0) { | |
| 373 | + current->cols = 80; | |
| 374 | + return -1; | |
| 375 | + } | |
| 376 | + current->y = info.dwCursorPosition.Y; | |
| 377 | + current->x = info.dwCursorPosition.X; | |
| 378 | + return 0; | |
| 379 | +} |
| --- a/extsrc/linenoise-win32.c | |
| +++ b/extsrc/linenoise-win32.c | |
| @@ -0,0 +1,379 @@ | |
| --- a/extsrc/linenoise-win32.c | |
| +++ b/extsrc/linenoise-win32.c | |
| @@ -0,0 +1,379 @@ | |
| 1 | |
| 2 | /* this code is not standalone |
| 3 | * it is included into linenoise.c |
| 4 | * for windows. |
| 5 | * It is deliberately kept separate so that |
| 6 | * applications that have no need for windows |
| 7 | * support can omit this |
| 8 | */ |
| 9 | static DWORD orig_consolemode = 0; |
| 10 | |
| 11 | static int flushOutput(struct current *current); |
| 12 | static void outputNewline(struct current *current); |
| 13 | |
| 14 | static void refreshStart(struct current *current) |
| 15 | { |
| 16 | (void)current; |
| 17 | } |
| 18 | |
| 19 | static void refreshEnd(struct current *current) |
| 20 | { |
| 21 | (void)current; |
| 22 | } |
| 23 | |
| 24 | static void refreshStartChars(struct current *current) |
| 25 | { |
| 26 | assert(current->output == NULL); |
| 27 | /* We accumulate all output here */ |
| 28 | current->output = sb_alloc(); |
| 29 | #ifdef USE_UTF8 |
| 30 | current->ubuflen = 0; |
| 31 | #endif |
| 32 | } |
| 33 | |
| 34 | static void refreshNewline(struct current *current) |
| 35 | { |
| 36 | DRL("<nl>"); |
| 37 | outputNewline(current); |
| 38 | } |
| 39 | |
| 40 | static void refreshEndChars(struct current *current) |
| 41 | { |
| 42 | assert(current->output); |
| 43 | flushOutput(current); |
| 44 | sb_free(current->output); |
| 45 | current->output = NULL; |
| 46 | } |
| 47 | |
| 48 | static int enableRawMode(struct current *current) { |
| 49 | DWORD n; |
| 50 | INPUT_RECORD irec; |
| 51 | |
| 52 | current->outh = GetStdHandle(STD_OUTPUT_HANDLE); |
| 53 | current->inh = GetStdHandle(STD_INPUT_HANDLE); |
| 54 | |
| 55 | if (!PeekConsoleInput(current->inh, &irec, 1, &n)) { |
| 56 | return -1; |
| 57 | } |
| 58 | if (getWindowSize(current) != 0) { |
| 59 | return -1; |
| 60 | } |
| 61 | if (GetConsoleMode(current->inh, &orig_consolemode)) { |
| 62 | SetConsoleMode(current->inh, ENABLE_PROCESSED_INPUT); |
| 63 | } |
| 64 | #ifdef USE_UTF8 |
| 65 | /* XXX is this the right thing to do? */ |
| 66 | SetConsoleCP(65001); |
| 67 | #endif |
| 68 | return 0; |
| 69 | } |
| 70 | |
| 71 | static void disableRawMode(struct current *current) |
| 72 | { |
| 73 | SetConsoleMode(current->inh, orig_consolemode); |
| 74 | } |
| 75 | |
| 76 | void linenoiseClearScreen(void) |
| 77 | { |
| 78 | /* XXX: This is ugly. Should just have the caller pass a handle */ |
| 79 | struct current current; |
| 80 | |
| 81 | current.outh = GetStdHandle(STD_OUTPUT_HANDLE); |
| 82 | |
| 83 | if (getWindowSize(¤t) == 0) { |
| 84 | COORD topleft = { 0, 0 }; |
| 85 | DWORD n; |
| 86 | |
| 87 | FillConsoleOutputCharacter(current.outh, ' ', |
| 88 | current.cols * current.rows, topleft, &n); |
| 89 | FillConsoleOutputAttribute(current.outh, |
| 90 | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN, |
| 91 | current.cols * current.rows, topleft, &n); |
| 92 | SetConsoleCursorPosition(current.outh, topleft); |
| 93 | } |
| 94 | } |
| 95 | |
| 96 | static void cursorToLeft(struct current *current) |
| 97 | { |
| 98 | COORD pos; |
| 99 | DWORD n; |
| 100 | |
| 101 | pos.X = 0; |
| 102 | pos.Y = (SHORT)current->y; |
| 103 | |
| 104 | FillConsoleOutputAttribute(current->outh, |
| 105 | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN, current->cols, pos, &n); |
| 106 | current->x = 0; |
| 107 | } |
| 108 | |
| 109 | #ifdef USE_UTF8 |
| 110 | static void flush_ubuf(struct current *current) |
| 111 | { |
| 112 | COORD pos; |
| 113 | DWORD nwritten; |
| 114 | pos.Y = (SHORT)current->y; |
| 115 | pos.X = (SHORT)current->x; |
| 116 | SetConsoleCursorPosition(current->outh, pos); |
| 117 | WriteConsoleW(current->outh, current->ubuf, current->ubuflen, &nwritten, 0); |
| 118 | current->x += current->ubufcols; |
| 119 | current->ubuflen = 0; |
| 120 | current->ubufcols = 0; |
| 121 | } |
| 122 | |
| 123 | static void add_ubuf(struct current *current, int ch) |
| 124 | { |
| 125 | /* This code originally by: Author: Mark E. Davis, 1994. */ |
| 126 | static const int halfShift = 10; /* used for shifting by 10 bits */ |
| 127 | |
| 128 | static const DWORD halfBase = 0x0010000UL; |
| 129 | static const DWORD halfMask = 0x3FFUL; |
| 130 | |
| 131 | #define UNI_SUR_HIGH_START 0xD800 |
| 132 | #define UNI_SUR_HIGH_END 0xDBFF |
| 133 | #define UNI_SUR_LOW_START 0xDC00 |
| 134 | #define UNI_SUR_LOW_END 0xDFFF |
| 135 | |
| 136 | #define UNI_MAX_BMP 0x0000FFFF |
| 137 | |
| 138 | if (ch > UNI_MAX_BMP) { |
| 139 | /* convert from unicode to utf16 surrogate pairs |
| 140 | * There is always space for one extra word in ubuf |
| 141 | */ |
| 142 | ch -= halfBase; |
| 143 | current->ubuf[current->ubuflen++] = (WORD)((ch >> halfShift) + UNI_SUR_HIGH_START); |
| 144 | current->ubuf[current->ubuflen++] = (WORD)((ch & halfMask) + UNI_SUR_LOW_START); |
| 145 | } |
| 146 | else { |
| 147 | current->ubuf[current->ubuflen++] = ch; |
| 148 | } |
| 149 | current->ubufcols += utf8_width(ch); |
| 150 | if (current->ubuflen >= UBUF_MAX_CHARS) { |
| 151 | flush_ubuf(current); |
| 152 | } |
| 153 | } |
| 154 | #endif |
| 155 | |
| 156 | static int flushOutput(struct current *current) |
| 157 | { |
| 158 | const char *pt = sb_str(current->output); |
| 159 | int len = sb_len(current->output); |
| 160 | |
| 161 | #ifdef USE_UTF8 |
| 162 | /* convert utf8 in current->output into utf16 in current->ubuf |
| 163 | */ |
| 164 | while (len) { |
| 165 | int ch; |
| 166 | int n = utf8_tounicode(pt, &ch); |
| 167 | |
| 168 | pt += n; |
| 169 | len -= n; |
| 170 | |
| 171 | add_ubuf(current, ch); |
| 172 | } |
| 173 | flush_ubuf(current); |
| 174 | #else |
| 175 | DWORD nwritten; |
| 176 | COORD pos; |
| 177 | |
| 178 | pos.Y = (SHORT)current->y; |
| 179 | pos.X = (SHORT)current->x; |
| 180 | |
| 181 | SetConsoleCursorPosition(current->outh, pos); |
| 182 | WriteConsoleA(current->outh, pt, len, &nwritten, 0); |
| 183 | |
| 184 | current->x += len; |
| 185 | #endif |
| 186 | |
| 187 | sb_clear(current->output); |
| 188 | |
| 189 | return 0; |
| 190 | } |
| 191 | |
| 192 | static int outputChars(struct current *current, const char *buf, int len) |
| 193 | { |
| 194 | if (len < 0) { |
| 195 | len = strlen(buf); |
| 196 | } |
| 197 | assert(current->output); |
| 198 | |
| 199 | sb_append_len(current->output, buf, len); |
| 200 | |
| 201 | return 0; |
| 202 | } |
| 203 | |
| 204 | static void outputNewline(struct current *current) |
| 205 | { |
| 206 | /* On the last row output a newline to force a scroll */ |
| 207 | if (current->y + 1 == current->rows) { |
| 208 | outputChars(current, "\n", 1); |
| 209 | } |
| 210 | flushOutput(current); |
| 211 | current->x = 0; |
| 212 | current->y++; |
| 213 | } |
| 214 | |
| 215 | static void setOutputHighlight(struct current *current, const int *props, int nprops) |
| 216 | { |
| 217 | int colour = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN; |
| 218 | int bold = 0; |
| 219 | int reverse = 0; |
| 220 | int i; |
| 221 | |
| 222 | for (i = 0; i < nprops; i++) { |
| 223 | switch (props[i]) { |
| 224 | case 0: |
| 225 | colour = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN; |
| 226 | bold = 0; |
| 227 | reverse = 0; |
| 228 | break; |
| 229 | case 1: |
| 230 | bold = FOREGROUND_INTENSITY; |
| 231 | break; |
| 232 | case 7: |
| 233 | reverse = 1; |
| 234 | break; |
| 235 | case 30: |
| 236 | colour = 0; |
| 237 | break; |
| 238 | case 31: |
| 239 | colour = FOREGROUND_RED; |
| 240 | break; |
| 241 | case 32: |
| 242 | colour = FOREGROUND_GREEN; |
| 243 | break; |
| 244 | case 33: |
| 245 | colour = FOREGROUND_RED | FOREGROUND_GREEN; |
| 246 | break; |
| 247 | case 34: |
| 248 | colour = FOREGROUND_BLUE; |
| 249 | break; |
| 250 | case 35: |
| 251 | colour = FOREGROUND_RED | FOREGROUND_BLUE; |
| 252 | break; |
| 253 | case 36: |
| 254 | colour = FOREGROUND_BLUE | FOREGROUND_GREEN; |
| 255 | break; |
| 256 | case 37: |
| 257 | colour = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN; |
| 258 | break; |
| 259 | } |
| 260 | } |
| 261 | |
| 262 | flushOutput(current); |
| 263 | |
| 264 | if (reverse) { |
| 265 | SetConsoleTextAttribute(current->outh, BACKGROUND_INTENSITY); |
| 266 | } |
| 267 | else { |
| 268 | SetConsoleTextAttribute(current->outh, colour | bold); |
| 269 | } |
| 270 | } |
| 271 | |
| 272 | static void eraseEol(struct current *current) |
| 273 | { |
| 274 | COORD pos; |
| 275 | DWORD n; |
| 276 | |
| 277 | pos.X = (SHORT) current->x; |
| 278 | pos.Y = (SHORT) current->y; |
| 279 | |
| 280 | FillConsoleOutputCharacter(current->outh, ' ', current->cols - current->x, pos, &n); |
| 281 | } |
| 282 | |
| 283 | static void setCursorXY(struct current *current) |
| 284 | { |
| 285 | COORD pos; |
| 286 | |
| 287 | pos.X = (SHORT) current->x; |
| 288 | pos.Y = (SHORT) current->y; |
| 289 | |
| 290 | SetConsoleCursorPosition(current->outh, pos); |
| 291 | } |
| 292 | |
| 293 | |
| 294 | static void setCursorPos(struct current *current, int x) |
| 295 | { |
| 296 | current->x = x; |
| 297 | setCursorXY(current); |
| 298 | } |
| 299 | |
| 300 | static void cursorUp(struct current *current, int n) |
| 301 | { |
| 302 | current->y -= n; |
| 303 | setCursorXY(current); |
| 304 | } |
| 305 | |
| 306 | static void cursorDown(struct current *current, int n) |
| 307 | { |
| 308 | current->y += n; |
| 309 | setCursorXY(current); |
| 310 | } |
| 311 | |
| 312 | static int fd_read(struct current *current) |
| 313 | { |
| 314 | while (1) { |
| 315 | INPUT_RECORD irec; |
| 316 | DWORD n; |
| 317 | if (WaitForSingleObject(current->inh, INFINITE) != WAIT_OBJECT_0) { |
| 318 | break; |
| 319 | } |
| 320 | if (!ReadConsoleInputW(current->inh, &irec, 1, &n)) { |
| 321 | break; |
| 322 | } |
| 323 | if (irec.EventType == KEY_EVENT) { |
| 324 | KEY_EVENT_RECORD *k = &irec.Event.KeyEvent; |
| 325 | if (k->bKeyDown || k->wVirtualKeyCode == VK_MENU) { |
| 326 | if (k->dwControlKeyState & ENHANCED_KEY) { |
| 327 | switch (k->wVirtualKeyCode) { |
| 328 | case VK_LEFT: |
| 329 | return SPECIAL_LEFT; |
| 330 | case VK_RIGHT: |
| 331 | return SPECIAL_RIGHT; |
| 332 | case VK_UP: |
| 333 | return SPECIAL_UP; |
| 334 | case VK_DOWN: |
| 335 | return SPECIAL_DOWN; |
| 336 | case VK_INSERT: |
| 337 | return SPECIAL_INSERT; |
| 338 | case VK_DELETE: |
| 339 | return SPECIAL_DELETE; |
| 340 | case VK_HOME: |
| 341 | return SPECIAL_HOME; |
| 342 | case VK_END: |
| 343 | return SPECIAL_END; |
| 344 | case VK_PRIOR: |
| 345 | return SPECIAL_PAGE_UP; |
| 346 | case VK_NEXT: |
| 347 | return SPECIAL_PAGE_DOWN; |
| 348 | case VK_RETURN: |
| 349 | return k->uChar.UnicodeChar; |
| 350 | } |
| 351 | } |
| 352 | /* Note that control characters are already translated in AsciiChar */ |
| 353 | else if (k->wVirtualKeyCode == VK_CONTROL) |
| 354 | continue; |
| 355 | else { |
| 356 | return k->uChar.UnicodeChar; |
| 357 | } |
| 358 | } |
| 359 | } |
| 360 | } |
| 361 | return -1; |
| 362 | } |
| 363 | |
| 364 | static int getWindowSize(struct current *current) |
| 365 | { |
| 366 | CONSOLE_SCREEN_BUFFER_INFO info; |
| 367 | if (!GetConsoleScreenBufferInfo(current->outh, &info)) { |
| 368 | return -1; |
| 369 | } |
| 370 | current->cols = info.dwSize.X; |
| 371 | current->rows = info.dwSize.Y; |
| 372 | if (current->cols <= 0 || current->rows <= 0) { |
| 373 | current->cols = 80; |
| 374 | return -1; |
| 375 | } |
| 376 | current->y = info.dwCursorPosition.Y; |
| 377 | current->x = info.dwCursorPosition.X; |
| 378 | return 0; |
| 379 | } |
+44
-18
| --- extsrc/linenoise.c | ||
| +++ extsrc/linenoise.c | ||
| @@ -892,10 +892,11 @@ | ||
| 892 | 892 | const char *prompt; |
| 893 | 893 | stringbuf *capture; /* capture buffer, or NULL for none. Always null terminated */ |
| 894 | 894 | stringbuf *output; /* used only during refreshLine() - output accumulator */ |
| 895 | 895 | #if defined(USE_TERMIOS) |
| 896 | 896 | int fd; /* Terminal fd */ |
| 897 | + int pending; /* pending char fd_read_char() */ | |
| 897 | 898 | #elif defined(USE_WINCONSOLE) |
| 898 | 899 | HANDLE outh; /* Console output handle */ |
| 899 | 900 | HANDLE inh; /* Console input handle */ |
| 900 | 901 | int rows; /* Screen rows */ |
| 901 | 902 | int x; /* Current column during output */ |
| @@ -1121,28 +1122,31 @@ | ||
| 1121 | 1122 | raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); |
| 1122 | 1123 | /* control chars - set return condition: min number of bytes and timer. |
| 1123 | 1124 | * We want read to return every single byte, without timeout. */ |
| 1124 | 1125 | raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */ |
| 1125 | 1126 | |
| 1126 | - /* put terminal in raw mode after flushing */ | |
| 1127 | - if (tcsetattr(current->fd,TCSADRAIN,&raw) < 0) { | |
| 1127 | + /* put terminal in raw mode. Because we aren't changing any output | |
| 1128 | + * settings we don't need to use TCSADRAIN and I have seen that hang on | |
| 1129 | + * OpenBSD when running under a pty | |
| 1130 | + */ | |
| 1131 | + if (tcsetattr(current->fd,TCSANOW,&raw) < 0) { | |
| 1128 | 1132 | goto fatal; |
| 1129 | 1133 | } |
| 1130 | 1134 | rawmode = 1; |
| 1131 | 1135 | return 0; |
| 1132 | 1136 | } |
| 1133 | 1137 | |
| 1134 | 1138 | static void disableRawMode(struct current *current) { |
| 1135 | 1139 | /* Don't even check the return value as it's too late. */ |
| 1136 | - if (rawmode && tcsetattr(current->fd,TCSADRAIN,&orig_termios) != -1) | |
| 1140 | + if (rawmode && tcsetattr(current->fd,TCSANOW,&orig_termios) != -1) | |
| 1137 | 1141 | rawmode = 0; |
| 1138 | 1142 | } |
| 1139 | 1143 | |
| 1140 | 1144 | /* At exit we'll try to fix the terminal to the initial conditions. */ |
| 1141 | 1145 | static void linenoiseAtExit(void) { |
| 1142 | 1146 | if (rawmode) { |
| 1143 | - tcsetattr(STDIN_FILENO, TCSADRAIN, &orig_termios); | |
| 1147 | + tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios); | |
| 1144 | 1148 | } |
| 1145 | 1149 | linenoiseHistoryFree(); |
| 1146 | 1150 | } |
| 1147 | 1151 | |
| 1148 | 1152 | /* gcc/glibc insists that we care about the return code of write! |
| @@ -1230,29 +1234,35 @@ | ||
| 1230 | 1234 | { |
| 1231 | 1235 | IGNORE_RC(write(STDOUT_FILENO, "\x1b[H\x1b[2J", 7)); |
| 1232 | 1236 | } |
| 1233 | 1237 | |
| 1234 | 1238 | /** |
| 1235 | - * Reads a char from 'fd', waiting at most 'timeout' milliseconds. | |
| 1239 | + * Reads a char from 'current->fd', waiting at most 'timeout' milliseconds. | |
| 1236 | 1240 | * |
| 1237 | 1241 | * A timeout of -1 means to wait forever. |
| 1238 | 1242 | * |
| 1239 | 1243 | * Returns -1 if no char is received within the time or an error occurs. |
| 1240 | 1244 | */ |
| 1241 | -static int fd_read_char(int fd, int timeout) | |
| 1245 | +static int fd_read_char(struct current *current, int timeout) | |
| 1242 | 1246 | { |
| 1243 | 1247 | struct pollfd p; |
| 1244 | 1248 | unsigned char c; |
| 1245 | 1249 | |
| 1246 | - p.fd = fd; | |
| 1250 | + if (current->pending) { | |
| 1251 | + c = current->pending; | |
| 1252 | + current->pending = 0; | |
| 1253 | + return c; | |
| 1254 | + } | |
| 1255 | + | |
| 1256 | + p.fd = current->fd; | |
| 1247 | 1257 | p.events = POLLIN; |
| 1248 | 1258 | |
| 1249 | 1259 | if (poll(&p, 1, timeout) == 0) { |
| 1250 | 1260 | /* timeout */ |
| 1251 | 1261 | return -1; |
| 1252 | 1262 | } |
| 1253 | - if (read(fd, &c, 1) != 1) { | |
| 1263 | + if (read(current->fd, &c, 1) != 1) { | |
| 1254 | 1264 | return -1; |
| 1255 | 1265 | } |
| 1256 | 1266 | return c; |
| 1257 | 1267 | } |
| 1258 | 1268 | |
| @@ -1266,11 +1276,15 @@ | ||
| 1266 | 1276 | char buf[MAX_UTF8_LEN]; |
| 1267 | 1277 | int n; |
| 1268 | 1278 | int i; |
| 1269 | 1279 | int c; |
| 1270 | 1280 | |
| 1271 | - if (read(current->fd, &buf[0], 1) != 1) { | |
| 1281 | + if (current->pending) { | |
| 1282 | + buf[0] = current->pending; | |
| 1283 | + current->pending = 0; | |
| 1284 | + } | |
| 1285 | + else if (read(current->fd, &buf[0], 1) != 1) { | |
| 1272 | 1286 | return -1; |
| 1273 | 1287 | } |
| 1274 | 1288 | n = utf8_charlen(buf[0]); |
| 1275 | 1289 | if (n < 1) { |
| 1276 | 1290 | return -1; |
| @@ -1282,11 +1296,11 @@ | ||
| 1282 | 1296 | } |
| 1283 | 1297 | /* decode and return the character */ |
| 1284 | 1298 | utf8_tounicode(buf, &c); |
| 1285 | 1299 | return c; |
| 1286 | 1300 | #else |
| 1287 | - return fd_read_char(current->fd, -1); | |
| 1301 | + return fd_read_char(current, -1); | |
| 1288 | 1302 | #endif |
| 1289 | 1303 | } |
| 1290 | 1304 | |
| 1291 | 1305 | |
| 1292 | 1306 | /** |
| @@ -1295,20 +1309,29 @@ | ||
| 1295 | 1309 | */ |
| 1296 | 1310 | static int queryCursor(struct current *current, int* cols) |
| 1297 | 1311 | { |
| 1298 | 1312 | struct esc_parser parser; |
| 1299 | 1313 | int ch; |
| 1314 | + /* Unfortunately we don't have any persistent state, so assume | |
| 1315 | + * a process will only ever interact with one terminal at a time. | |
| 1316 | + */ | |
| 1317 | + static int query_cursor_failed; | |
| 1318 | + | |
| 1319 | + if (query_cursor_failed) { | |
| 1320 | + /* If it ever fails, don't try again */ | |
| 1321 | + return 0; | |
| 1322 | + } | |
| 1300 | 1323 | |
| 1301 | 1324 | /* Should not be buffering this output, it needs to go immediately */ |
| 1302 | 1325 | assert(current->output == NULL); |
| 1303 | 1326 | |
| 1304 | 1327 | /* control sequence - report cursor location */ |
| 1305 | 1328 | outputChars(current, "\x1b[6n", -1); |
| 1306 | 1329 | |
| 1307 | 1330 | /* Parse the response: ESC [ rows ; cols R */ |
| 1308 | 1331 | initParseEscapeSeq(&parser, 'R'); |
| 1309 | - while ((ch = fd_read_char(current->fd, 100)) > 0) { | |
| 1332 | + while ((ch = fd_read_char(current, 100)) > 0) { | |
| 1310 | 1333 | switch (parseEscapeSequence(&parser, ch)) { |
| 1311 | 1334 | default: |
| 1312 | 1335 | continue; |
| 1313 | 1336 | case EP_END: |
| 1314 | 1337 | if (parser.numprops == 2 && parser.props[1] < 1000) { |
| @@ -1315,15 +1338,18 @@ | ||
| 1315 | 1338 | *cols = parser.props[1]; |
| 1316 | 1339 | return 1; |
| 1317 | 1340 | } |
| 1318 | 1341 | break; |
| 1319 | 1342 | case EP_ERROR: |
| 1343 | + /* Push back the character that caused the error */ | |
| 1344 | + current->pending = ch; | |
| 1320 | 1345 | break; |
| 1321 | 1346 | } |
| 1322 | 1347 | /* failed */ |
| 1323 | 1348 | break; |
| 1324 | 1349 | } |
| 1350 | + query_cursor_failed = 1; | |
| 1325 | 1351 | return 0; |
| 1326 | 1352 | } |
| 1327 | 1353 | |
| 1328 | 1354 | /** |
| 1329 | 1355 | * Updates current->cols with the current window size (width) |
| @@ -1386,13 +1412,13 @@ | ||
| 1386 | 1412 | * Returns SPECIAL_NONE if unrecognised, or -1 if EOF. |
| 1387 | 1413 | * |
| 1388 | 1414 | * If no additional char is received within a short time, |
| 1389 | 1415 | * CHAR_ESCAPE is returned. |
| 1390 | 1416 | */ |
| 1391 | -static int check_special(int fd) | |
| 1417 | +static int check_special(struct current *current) | |
| 1392 | 1418 | { |
| 1393 | - int c = fd_read_char(fd, 50); | |
| 1419 | + int c = fd_read_char(current, 50); | |
| 1394 | 1420 | int c2; |
| 1395 | 1421 | |
| 1396 | 1422 | if (c < 0) { |
| 1397 | 1423 | return CHAR_ESCAPE; |
| 1398 | 1424 | } |
| @@ -1399,11 +1425,11 @@ | ||
| 1399 | 1425 | else if (c >= 'a' && c <= 'z') { |
| 1400 | 1426 | /* esc-a => meta-a */ |
| 1401 | 1427 | return meta(c); |
| 1402 | 1428 | } |
| 1403 | 1429 | |
| 1404 | - c2 = fd_read_char(fd, 50); | |
| 1430 | + c2 = fd_read_char(current, 50); | |
| 1405 | 1431 | if (c2 < 0) { |
| 1406 | 1432 | return c2; |
| 1407 | 1433 | } |
| 1408 | 1434 | if (c == '[' || c == 'O') { |
| 1409 | 1435 | /* Potential arrow key */ |
| @@ -1422,11 +1448,11 @@ | ||
| 1422 | 1448 | return SPECIAL_HOME; |
| 1423 | 1449 | } |
| 1424 | 1450 | } |
| 1425 | 1451 | if (c == '[' && c2 >= '1' && c2 <= '8') { |
| 1426 | 1452 | /* extended escape */ |
| 1427 | - c = fd_read_char(fd, 50); | |
| 1453 | + c = fd_read_char(current, 50); | |
| 1428 | 1454 | if (c == '~') { |
| 1429 | 1455 | switch (c2) { |
| 1430 | 1456 | case '2': |
| 1431 | 1457 | return SPECIAL_INSERT; |
| 1432 | 1458 | case '3': |
| @@ -1441,11 +1467,11 @@ | ||
| 1441 | 1467 | return SPECIAL_END; |
| 1442 | 1468 | } |
| 1443 | 1469 | } |
| 1444 | 1470 | while (c != -1 && c != '~') { |
| 1445 | 1471 | /* .e.g \e[12~ or '\e[11;2~ discard the complete sequence */ |
| 1446 | - c = fd_read_char(fd, 50); | |
| 1472 | + c = fd_read_char(current, 50); | |
| 1447 | 1473 | } |
| 1448 | 1474 | } |
| 1449 | 1475 | |
| 1450 | 1476 | return SPECIAL_NONE; |
| 1451 | 1477 | } |
| @@ -2234,11 +2260,11 @@ | ||
| 2234 | 2260 | } |
| 2235 | 2261 | continue; |
| 2236 | 2262 | } |
| 2237 | 2263 | #ifdef USE_TERMIOS |
| 2238 | 2264 | if (c == CHAR_ESCAPE) { |
| 2239 | - c = check_special(current->fd); | |
| 2265 | + c = check_special(current); | |
| 2240 | 2266 | } |
| 2241 | 2267 | #endif |
| 2242 | 2268 | if (c == ctrl('R')) { |
| 2243 | 2269 | /* Search for the previous (earlier) match */ |
| 2244 | 2270 | if (searchpos > 0) { |
| @@ -2346,11 +2372,11 @@ | ||
| 2346 | 2372 | /* go on to process the returned char normally */ |
| 2347 | 2373 | } |
| 2348 | 2374 | |
| 2349 | 2375 | #ifdef USE_TERMIOS |
| 2350 | 2376 | if (c == CHAR_ESCAPE) { /* escape sequence */ |
| 2351 | - c = check_special(current->fd); | |
| 2377 | + c = check_special(current); | |
| 2352 | 2378 | } |
| 2353 | 2379 | #endif |
| 2354 | 2380 | if (c == -1) { |
| 2355 | 2381 | /* Return on errors */ |
| 2356 | 2382 | return sb_len(current->buf); |
| 2357 | 2383 |
| --- extsrc/linenoise.c | |
| +++ extsrc/linenoise.c | |
| @@ -892,10 +892,11 @@ | |
| 892 | const char *prompt; |
| 893 | stringbuf *capture; /* capture buffer, or NULL for none. Always null terminated */ |
| 894 | stringbuf *output; /* used only during refreshLine() - output accumulator */ |
| 895 | #if defined(USE_TERMIOS) |
| 896 | int fd; /* Terminal fd */ |
| 897 | #elif defined(USE_WINCONSOLE) |
| 898 | HANDLE outh; /* Console output handle */ |
| 899 | HANDLE inh; /* Console input handle */ |
| 900 | int rows; /* Screen rows */ |
| 901 | int x; /* Current column during output */ |
| @@ -1121,28 +1122,31 @@ | |
| 1121 | raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); |
| 1122 | /* control chars - set return condition: min number of bytes and timer. |
| 1123 | * We want read to return every single byte, without timeout. */ |
| 1124 | raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */ |
| 1125 | |
| 1126 | /* put terminal in raw mode after flushing */ |
| 1127 | if (tcsetattr(current->fd,TCSADRAIN,&raw) < 0) { |
| 1128 | goto fatal; |
| 1129 | } |
| 1130 | rawmode = 1; |
| 1131 | return 0; |
| 1132 | } |
| 1133 | |
| 1134 | static void disableRawMode(struct current *current) { |
| 1135 | /* Don't even check the return value as it's too late. */ |
| 1136 | if (rawmode && tcsetattr(current->fd,TCSADRAIN,&orig_termios) != -1) |
| 1137 | rawmode = 0; |
| 1138 | } |
| 1139 | |
| 1140 | /* At exit we'll try to fix the terminal to the initial conditions. */ |
| 1141 | static void linenoiseAtExit(void) { |
| 1142 | if (rawmode) { |
| 1143 | tcsetattr(STDIN_FILENO, TCSADRAIN, &orig_termios); |
| 1144 | } |
| 1145 | linenoiseHistoryFree(); |
| 1146 | } |
| 1147 | |
| 1148 | /* gcc/glibc insists that we care about the return code of write! |
| @@ -1230,29 +1234,35 @@ | |
| 1230 | { |
| 1231 | IGNORE_RC(write(STDOUT_FILENO, "\x1b[H\x1b[2J", 7)); |
| 1232 | } |
| 1233 | |
| 1234 | /** |
| 1235 | * Reads a char from 'fd', waiting at most 'timeout' milliseconds. |
| 1236 | * |
| 1237 | * A timeout of -1 means to wait forever. |
| 1238 | * |
| 1239 | * Returns -1 if no char is received within the time or an error occurs. |
| 1240 | */ |
| 1241 | static int fd_read_char(int fd, int timeout) |
| 1242 | { |
| 1243 | struct pollfd p; |
| 1244 | unsigned char c; |
| 1245 | |
| 1246 | p.fd = fd; |
| 1247 | p.events = POLLIN; |
| 1248 | |
| 1249 | if (poll(&p, 1, timeout) == 0) { |
| 1250 | /* timeout */ |
| 1251 | return -1; |
| 1252 | } |
| 1253 | if (read(fd, &c, 1) != 1) { |
| 1254 | return -1; |
| 1255 | } |
| 1256 | return c; |
| 1257 | } |
| 1258 | |
| @@ -1266,11 +1276,15 @@ | |
| 1266 | char buf[MAX_UTF8_LEN]; |
| 1267 | int n; |
| 1268 | int i; |
| 1269 | int c; |
| 1270 | |
| 1271 | if (read(current->fd, &buf[0], 1) != 1) { |
| 1272 | return -1; |
| 1273 | } |
| 1274 | n = utf8_charlen(buf[0]); |
| 1275 | if (n < 1) { |
| 1276 | return -1; |
| @@ -1282,11 +1296,11 @@ | |
| 1282 | } |
| 1283 | /* decode and return the character */ |
| 1284 | utf8_tounicode(buf, &c); |
| 1285 | return c; |
| 1286 | #else |
| 1287 | return fd_read_char(current->fd, -1); |
| 1288 | #endif |
| 1289 | } |
| 1290 | |
| 1291 | |
| 1292 | /** |
| @@ -1295,20 +1309,29 @@ | |
| 1295 | */ |
| 1296 | static int queryCursor(struct current *current, int* cols) |
| 1297 | { |
| 1298 | struct esc_parser parser; |
| 1299 | int ch; |
| 1300 | |
| 1301 | /* Should not be buffering this output, it needs to go immediately */ |
| 1302 | assert(current->output == NULL); |
| 1303 | |
| 1304 | /* control sequence - report cursor location */ |
| 1305 | outputChars(current, "\x1b[6n", -1); |
| 1306 | |
| 1307 | /* Parse the response: ESC [ rows ; cols R */ |
| 1308 | initParseEscapeSeq(&parser, 'R'); |
| 1309 | while ((ch = fd_read_char(current->fd, 100)) > 0) { |
| 1310 | switch (parseEscapeSequence(&parser, ch)) { |
| 1311 | default: |
| 1312 | continue; |
| 1313 | case EP_END: |
| 1314 | if (parser.numprops == 2 && parser.props[1] < 1000) { |
| @@ -1315,15 +1338,18 @@ | |
| 1315 | *cols = parser.props[1]; |
| 1316 | return 1; |
| 1317 | } |
| 1318 | break; |
| 1319 | case EP_ERROR: |
| 1320 | break; |
| 1321 | } |
| 1322 | /* failed */ |
| 1323 | break; |
| 1324 | } |
| 1325 | return 0; |
| 1326 | } |
| 1327 | |
| 1328 | /** |
| 1329 | * Updates current->cols with the current window size (width) |
| @@ -1386,13 +1412,13 @@ | |
| 1386 | * Returns SPECIAL_NONE if unrecognised, or -1 if EOF. |
| 1387 | * |
| 1388 | * If no additional char is received within a short time, |
| 1389 | * CHAR_ESCAPE is returned. |
| 1390 | */ |
| 1391 | static int check_special(int fd) |
| 1392 | { |
| 1393 | int c = fd_read_char(fd, 50); |
| 1394 | int c2; |
| 1395 | |
| 1396 | if (c < 0) { |
| 1397 | return CHAR_ESCAPE; |
| 1398 | } |
| @@ -1399,11 +1425,11 @@ | |
| 1399 | else if (c >= 'a' && c <= 'z') { |
| 1400 | /* esc-a => meta-a */ |
| 1401 | return meta(c); |
| 1402 | } |
| 1403 | |
| 1404 | c2 = fd_read_char(fd, 50); |
| 1405 | if (c2 < 0) { |
| 1406 | return c2; |
| 1407 | } |
| 1408 | if (c == '[' || c == 'O') { |
| 1409 | /* Potential arrow key */ |
| @@ -1422,11 +1448,11 @@ | |
| 1422 | return SPECIAL_HOME; |
| 1423 | } |
| 1424 | } |
| 1425 | if (c == '[' && c2 >= '1' && c2 <= '8') { |
| 1426 | /* extended escape */ |
| 1427 | c = fd_read_char(fd, 50); |
| 1428 | if (c == '~') { |
| 1429 | switch (c2) { |
| 1430 | case '2': |
| 1431 | return SPECIAL_INSERT; |
| 1432 | case '3': |
| @@ -1441,11 +1467,11 @@ | |
| 1441 | return SPECIAL_END; |
| 1442 | } |
| 1443 | } |
| 1444 | while (c != -1 && c != '~') { |
| 1445 | /* .e.g \e[12~ or '\e[11;2~ discard the complete sequence */ |
| 1446 | c = fd_read_char(fd, 50); |
| 1447 | } |
| 1448 | } |
| 1449 | |
| 1450 | return SPECIAL_NONE; |
| 1451 | } |
| @@ -2234,11 +2260,11 @@ | |
| 2234 | } |
| 2235 | continue; |
| 2236 | } |
| 2237 | #ifdef USE_TERMIOS |
| 2238 | if (c == CHAR_ESCAPE) { |
| 2239 | c = check_special(current->fd); |
| 2240 | } |
| 2241 | #endif |
| 2242 | if (c == ctrl('R')) { |
| 2243 | /* Search for the previous (earlier) match */ |
| 2244 | if (searchpos > 0) { |
| @@ -2346,11 +2372,11 @@ | |
| 2346 | /* go on to process the returned char normally */ |
| 2347 | } |
| 2348 | |
| 2349 | #ifdef USE_TERMIOS |
| 2350 | if (c == CHAR_ESCAPE) { /* escape sequence */ |
| 2351 | c = check_special(current->fd); |
| 2352 | } |
| 2353 | #endif |
| 2354 | if (c == -1) { |
| 2355 | /* Return on errors */ |
| 2356 | return sb_len(current->buf); |
| 2357 |
| --- extsrc/linenoise.c | |
| +++ extsrc/linenoise.c | |
| @@ -892,10 +892,11 @@ | |
| 892 | const char *prompt; |
| 893 | stringbuf *capture; /* capture buffer, or NULL for none. Always null terminated */ |
| 894 | stringbuf *output; /* used only during refreshLine() - output accumulator */ |
| 895 | #if defined(USE_TERMIOS) |
| 896 | int fd; /* Terminal fd */ |
| 897 | int pending; /* pending char fd_read_char() */ |
| 898 | #elif defined(USE_WINCONSOLE) |
| 899 | HANDLE outh; /* Console output handle */ |
| 900 | HANDLE inh; /* Console input handle */ |
| 901 | int rows; /* Screen rows */ |
| 902 | int x; /* Current column during output */ |
| @@ -1121,28 +1122,31 @@ | |
| 1122 | raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); |
| 1123 | /* control chars - set return condition: min number of bytes and timer. |
| 1124 | * We want read to return every single byte, without timeout. */ |
| 1125 | raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */ |
| 1126 | |
| 1127 | /* put terminal in raw mode. Because we aren't changing any output |
| 1128 | * settings we don't need to use TCSADRAIN and I have seen that hang on |
| 1129 | * OpenBSD when running under a pty |
| 1130 | */ |
| 1131 | if (tcsetattr(current->fd,TCSANOW,&raw) < 0) { |
| 1132 | goto fatal; |
| 1133 | } |
| 1134 | rawmode = 1; |
| 1135 | return 0; |
| 1136 | } |
| 1137 | |
| 1138 | static void disableRawMode(struct current *current) { |
| 1139 | /* Don't even check the return value as it's too late. */ |
| 1140 | if (rawmode && tcsetattr(current->fd,TCSANOW,&orig_termios) != -1) |
| 1141 | rawmode = 0; |
| 1142 | } |
| 1143 | |
| 1144 | /* At exit we'll try to fix the terminal to the initial conditions. */ |
| 1145 | static void linenoiseAtExit(void) { |
| 1146 | if (rawmode) { |
| 1147 | tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios); |
| 1148 | } |
| 1149 | linenoiseHistoryFree(); |
| 1150 | } |
| 1151 | |
| 1152 | /* gcc/glibc insists that we care about the return code of write! |
| @@ -1230,29 +1234,35 @@ | |
| 1234 | { |
| 1235 | IGNORE_RC(write(STDOUT_FILENO, "\x1b[H\x1b[2J", 7)); |
| 1236 | } |
| 1237 | |
| 1238 | /** |
| 1239 | * Reads a char from 'current->fd', waiting at most 'timeout' milliseconds. |
| 1240 | * |
| 1241 | * A timeout of -1 means to wait forever. |
| 1242 | * |
| 1243 | * Returns -1 if no char is received within the time or an error occurs. |
| 1244 | */ |
| 1245 | static int fd_read_char(struct current *current, int timeout) |
| 1246 | { |
| 1247 | struct pollfd p; |
| 1248 | unsigned char c; |
| 1249 | |
| 1250 | if (current->pending) { |
| 1251 | c = current->pending; |
| 1252 | current->pending = 0; |
| 1253 | return c; |
| 1254 | } |
| 1255 | |
| 1256 | p.fd = current->fd; |
| 1257 | p.events = POLLIN; |
| 1258 | |
| 1259 | if (poll(&p, 1, timeout) == 0) { |
| 1260 | /* timeout */ |
| 1261 | return -1; |
| 1262 | } |
| 1263 | if (read(current->fd, &c, 1) != 1) { |
| 1264 | return -1; |
| 1265 | } |
| 1266 | return c; |
| 1267 | } |
| 1268 | |
| @@ -1266,11 +1276,15 @@ | |
| 1276 | char buf[MAX_UTF8_LEN]; |
| 1277 | int n; |
| 1278 | int i; |
| 1279 | int c; |
| 1280 | |
| 1281 | if (current->pending) { |
| 1282 | buf[0] = current->pending; |
| 1283 | current->pending = 0; |
| 1284 | } |
| 1285 | else if (read(current->fd, &buf[0], 1) != 1) { |
| 1286 | return -1; |
| 1287 | } |
| 1288 | n = utf8_charlen(buf[0]); |
| 1289 | if (n < 1) { |
| 1290 | return -1; |
| @@ -1282,11 +1296,11 @@ | |
| 1296 | } |
| 1297 | /* decode and return the character */ |
| 1298 | utf8_tounicode(buf, &c); |
| 1299 | return c; |
| 1300 | #else |
| 1301 | return fd_read_char(current, -1); |
| 1302 | #endif |
| 1303 | } |
| 1304 | |
| 1305 | |
| 1306 | /** |
| @@ -1295,20 +1309,29 @@ | |
| 1309 | */ |
| 1310 | static int queryCursor(struct current *current, int* cols) |
| 1311 | { |
| 1312 | struct esc_parser parser; |
| 1313 | int ch; |
| 1314 | /* Unfortunately we don't have any persistent state, so assume |
| 1315 | * a process will only ever interact with one terminal at a time. |
| 1316 | */ |
| 1317 | static int query_cursor_failed; |
| 1318 | |
| 1319 | if (query_cursor_failed) { |
| 1320 | /* If it ever fails, don't try again */ |
| 1321 | return 0; |
| 1322 | } |
| 1323 | |
| 1324 | /* Should not be buffering this output, it needs to go immediately */ |
| 1325 | assert(current->output == NULL); |
| 1326 | |
| 1327 | /* control sequence - report cursor location */ |
| 1328 | outputChars(current, "\x1b[6n", -1); |
| 1329 | |
| 1330 | /* Parse the response: ESC [ rows ; cols R */ |
| 1331 | initParseEscapeSeq(&parser, 'R'); |
| 1332 | while ((ch = fd_read_char(current, 100)) > 0) { |
| 1333 | switch (parseEscapeSequence(&parser, ch)) { |
| 1334 | default: |
| 1335 | continue; |
| 1336 | case EP_END: |
| 1337 | if (parser.numprops == 2 && parser.props[1] < 1000) { |
| @@ -1315,15 +1338,18 @@ | |
| 1338 | *cols = parser.props[1]; |
| 1339 | return 1; |
| 1340 | } |
| 1341 | break; |
| 1342 | case EP_ERROR: |
| 1343 | /* Push back the character that caused the error */ |
| 1344 | current->pending = ch; |
| 1345 | break; |
| 1346 | } |
| 1347 | /* failed */ |
| 1348 | break; |
| 1349 | } |
| 1350 | query_cursor_failed = 1; |
| 1351 | return 0; |
| 1352 | } |
| 1353 | |
| 1354 | /** |
| 1355 | * Updates current->cols with the current window size (width) |
| @@ -1386,13 +1412,13 @@ | |
| 1412 | * Returns SPECIAL_NONE if unrecognised, or -1 if EOF. |
| 1413 | * |
| 1414 | * If no additional char is received within a short time, |
| 1415 | * CHAR_ESCAPE is returned. |
| 1416 | */ |
| 1417 | static int check_special(struct current *current) |
| 1418 | { |
| 1419 | int c = fd_read_char(current, 50); |
| 1420 | int c2; |
| 1421 | |
| 1422 | if (c < 0) { |
| 1423 | return CHAR_ESCAPE; |
| 1424 | } |
| @@ -1399,11 +1425,11 @@ | |
| 1425 | else if (c >= 'a' && c <= 'z') { |
| 1426 | /* esc-a => meta-a */ |
| 1427 | return meta(c); |
| 1428 | } |
| 1429 | |
| 1430 | c2 = fd_read_char(current, 50); |
| 1431 | if (c2 < 0) { |
| 1432 | return c2; |
| 1433 | } |
| 1434 | if (c == '[' || c == 'O') { |
| 1435 | /* Potential arrow key */ |
| @@ -1422,11 +1448,11 @@ | |
| 1448 | return SPECIAL_HOME; |
| 1449 | } |
| 1450 | } |
| 1451 | if (c == '[' && c2 >= '1' && c2 <= '8') { |
| 1452 | /* extended escape */ |
| 1453 | c = fd_read_char(current, 50); |
| 1454 | if (c == '~') { |
| 1455 | switch (c2) { |
| 1456 | case '2': |
| 1457 | return SPECIAL_INSERT; |
| 1458 | case '3': |
| @@ -1441,11 +1467,11 @@ | |
| 1467 | return SPECIAL_END; |
| 1468 | } |
| 1469 | } |
| 1470 | while (c != -1 && c != '~') { |
| 1471 | /* .e.g \e[12~ or '\e[11;2~ discard the complete sequence */ |
| 1472 | c = fd_read_char(current, 50); |
| 1473 | } |
| 1474 | } |
| 1475 | |
| 1476 | return SPECIAL_NONE; |
| 1477 | } |
| @@ -2234,11 +2260,11 @@ | |
| 2260 | } |
| 2261 | continue; |
| 2262 | } |
| 2263 | #ifdef USE_TERMIOS |
| 2264 | if (c == CHAR_ESCAPE) { |
| 2265 | c = check_special(current); |
| 2266 | } |
| 2267 | #endif |
| 2268 | if (c == ctrl('R')) { |
| 2269 | /* Search for the previous (earlier) match */ |
| 2270 | if (searchpos > 0) { |
| @@ -2346,11 +2372,11 @@ | |
| 2372 | /* go on to process the returned char normally */ |
| 2373 | } |
| 2374 | |
| 2375 | #ifdef USE_TERMIOS |
| 2376 | if (c == CHAR_ESCAPE) { /* escape sequence */ |
| 2377 | c = check_special(current); |
| 2378 | } |
| 2379 | #endif |
| 2380 | if (c == -1) { |
| 2381 | /* Return on errors */ |
| 2382 | return sb_len(current->buf); |
| 2383 |
+7
-6
| --- extsrc/pikchr-worker.js | ||
| +++ extsrc/pikchr-worker.js | ||
| @@ -36,11 +36,11 @@ | ||
| 36 | 36 | pikchr: source code for the pikchr, |
| 37 | 37 | darkMode: boolean true to adjust colors for a dark color scheme, |
| 38 | 38 | cssClass: CSS class name to add to the SVG |
| 39 | 39 | } |
| 40 | 40 | |
| 41 | - Workers-to-Main types | |
| 41 | + Workers-to-Main message types: | |
| 42 | 42 | |
| 43 | 43 | - stdout, stderr: indicate stdout/stderr output from the wasm |
| 44 | 44 | layer. The data property is the string of the output, noting |
| 45 | 45 | that the emscripten binding emits these one line at a time. Thus, |
| 46 | 46 | if a C-side puts() emits multiple lines in a single call, the JS |
| @@ -50,21 +50,21 @@ | ||
| 50 | 50 | |
| 51 | 51 | - module: Status text. This is intended to alert the main thread |
| 52 | 52 | about module loading status so that, e.g., the main thread can |
| 53 | 53 | update a progress widget and DTRT when the module is finished |
| 54 | 54 | loading and available for work. Status messages come in the form |
| 55 | - | |
| 55 | + | |
| 56 | 56 | {type:'module', data:{ |
| 57 | 57 | type:'status', |
| 58 | 58 | data: {text:string|null, step:1-based-integer} |
| 59 | 59 | } |
| 60 | 60 | |
| 61 | 61 | with an incrementing step value for each subsequent message. When |
| 62 | 62 | the module loading is complete, a message with a text value of |
| 63 | 63 | null is posted. |
| 64 | 64 | |
| 65 | - - pikchr: | |
| 65 | + - pikchr: | |
| 66 | 66 | |
| 67 | 67 | {type: 'pikchr', |
| 68 | 68 | data:{ |
| 69 | 69 | pikchr: input text, |
| 70 | 70 | result: rendered result (SVG on success, HTML on error), |
| @@ -162,11 +162,11 @@ | ||
| 162 | 162 | } |
| 163 | 163 | return; |
| 164 | 164 | }; |
| 165 | 165 | console.warn("Unknown pikchr-worker message type:",ev); |
| 166 | 166 | }; |
| 167 | - | |
| 167 | + | |
| 168 | 168 | /** |
| 169 | 169 | emscripten module for use with build mode -sMODULARIZE. |
| 170 | 170 | */ |
| 171 | 171 | const pikchrModule = { |
| 172 | 172 | print: function(){wMsg('stdout', Array.prototype.slice.call(arguments));}, |
| @@ -206,16 +206,17 @@ | ||
| 206 | 206 | data:{step: ++f.last.step, text: text||null} |
| 207 | 207 | }); |
| 208 | 208 | } |
| 209 | 209 | }; |
| 210 | 210 | |
| 211 | - importScripts('pikchr.js'); | |
| 211 | + importScripts('pikchr-v2813665466.js'); | |
| 212 | 212 | /** |
| 213 | 213 | initPikchrModule() is installed via pikchr.js due to |
| 214 | 214 | building with: |
| 215 | 215 | |
| 216 | 216 | emcc ... -sMODULARIZE=1 -sEXPORT_NAME=initPikchrModule |
| 217 | 217 | */ |
| 218 | 218 | initPikchrModule(pikchrModule).then(function(thisModule){ |
| 219 | - wMsg('pikchr-ready'); | |
| 219 | + //globalThis.M = pikchrModule; console.warn("pikchrModule=globalThis.M=",globalThis.M); | |
| 220 | + wMsg('pikchr-ready', pikchrModule.ccall('pikchr_version','string')); | |
| 220 | 221 | }); |
| 221 | 222 | })(); |
| 222 | 223 |
| --- extsrc/pikchr-worker.js | |
| +++ extsrc/pikchr-worker.js | |
| @@ -36,11 +36,11 @@ | |
| 36 | pikchr: source code for the pikchr, |
| 37 | darkMode: boolean true to adjust colors for a dark color scheme, |
| 38 | cssClass: CSS class name to add to the SVG |
| 39 | } |
| 40 | |
| 41 | Workers-to-Main types |
| 42 | |
| 43 | - stdout, stderr: indicate stdout/stderr output from the wasm |
| 44 | layer. The data property is the string of the output, noting |
| 45 | that the emscripten binding emits these one line at a time. Thus, |
| 46 | if a C-side puts() emits multiple lines in a single call, the JS |
| @@ -50,21 +50,21 @@ | |
| 50 | |
| 51 | - module: Status text. This is intended to alert the main thread |
| 52 | about module loading status so that, e.g., the main thread can |
| 53 | update a progress widget and DTRT when the module is finished |
| 54 | loading and available for work. Status messages come in the form |
| 55 | |
| 56 | {type:'module', data:{ |
| 57 | type:'status', |
| 58 | data: {text:string|null, step:1-based-integer} |
| 59 | } |
| 60 | |
| 61 | with an incrementing step value for each subsequent message. When |
| 62 | the module loading is complete, a message with a text value of |
| 63 | null is posted. |
| 64 | |
| 65 | - pikchr: |
| 66 | |
| 67 | {type: 'pikchr', |
| 68 | data:{ |
| 69 | pikchr: input text, |
| 70 | result: rendered result (SVG on success, HTML on error), |
| @@ -162,11 +162,11 @@ | |
| 162 | } |
| 163 | return; |
| 164 | }; |
| 165 | console.warn("Unknown pikchr-worker message type:",ev); |
| 166 | }; |
| 167 | |
| 168 | /** |
| 169 | emscripten module for use with build mode -sMODULARIZE. |
| 170 | */ |
| 171 | const pikchrModule = { |
| 172 | print: function(){wMsg('stdout', Array.prototype.slice.call(arguments));}, |
| @@ -206,16 +206,17 @@ | |
| 206 | data:{step: ++f.last.step, text: text||null} |
| 207 | }); |
| 208 | } |
| 209 | }; |
| 210 | |
| 211 | importScripts('pikchr.js'); |
| 212 | /** |
| 213 | initPikchrModule() is installed via pikchr.js due to |
| 214 | building with: |
| 215 | |
| 216 | emcc ... -sMODULARIZE=1 -sEXPORT_NAME=initPikchrModule |
| 217 | */ |
| 218 | initPikchrModule(pikchrModule).then(function(thisModule){ |
| 219 | wMsg('pikchr-ready'); |
| 220 | }); |
| 221 | })(); |
| 222 |
| --- extsrc/pikchr-worker.js | |
| +++ extsrc/pikchr-worker.js | |
| @@ -36,11 +36,11 @@ | |
| 36 | pikchr: source code for the pikchr, |
| 37 | darkMode: boolean true to adjust colors for a dark color scheme, |
| 38 | cssClass: CSS class name to add to the SVG |
| 39 | } |
| 40 | |
| 41 | Workers-to-Main message types: |
| 42 | |
| 43 | - stdout, stderr: indicate stdout/stderr output from the wasm |
| 44 | layer. The data property is the string of the output, noting |
| 45 | that the emscripten binding emits these one line at a time. Thus, |
| 46 | if a C-side puts() emits multiple lines in a single call, the JS |
| @@ -50,21 +50,21 @@ | |
| 50 | |
| 51 | - module: Status text. This is intended to alert the main thread |
| 52 | about module loading status so that, e.g., the main thread can |
| 53 | update a progress widget and DTRT when the module is finished |
| 54 | loading and available for work. Status messages come in the form |
| 55 | |
| 56 | {type:'module', data:{ |
| 57 | type:'status', |
| 58 | data: {text:string|null, step:1-based-integer} |
| 59 | } |
| 60 | |
| 61 | with an incrementing step value for each subsequent message. When |
| 62 | the module loading is complete, a message with a text value of |
| 63 | null is posted. |
| 64 | |
| 65 | - pikchr: |
| 66 | |
| 67 | {type: 'pikchr', |
| 68 | data:{ |
| 69 | pikchr: input text, |
| 70 | result: rendered result (SVG on success, HTML on error), |
| @@ -162,11 +162,11 @@ | |
| 162 | } |
| 163 | return; |
| 164 | }; |
| 165 | console.warn("Unknown pikchr-worker message type:",ev); |
| 166 | }; |
| 167 | |
| 168 | /** |
| 169 | emscripten module for use with build mode -sMODULARIZE. |
| 170 | */ |
| 171 | const pikchrModule = { |
| 172 | print: function(){wMsg('stdout', Array.prototype.slice.call(arguments));}, |
| @@ -206,16 +206,17 @@ | |
| 206 | data:{step: ++f.last.step, text: text||null} |
| 207 | }); |
| 208 | } |
| 209 | }; |
| 210 | |
| 211 | importScripts('pikchr-v2813665466.js'); |
| 212 | /** |
| 213 | initPikchrModule() is installed via pikchr.js due to |
| 214 | building with: |
| 215 | |
| 216 | emcc ... -sMODULARIZE=1 -sEXPORT_NAME=initPikchrModule |
| 217 | */ |
| 218 | initPikchrModule(pikchrModule).then(function(thisModule){ |
| 219 | //globalThis.M = pikchrModule; console.warn("pikchrModule=globalThis.M=",globalThis.M); |
| 220 | wMsg('pikchr-ready', pikchrModule.ccall('pikchr_version','string')); |
| 221 | }); |
| 222 | })(); |
| 223 |
+1147
-1059
| --- extsrc/pikchr.c | ||
| +++ extsrc/pikchr.c | ||
| @@ -1,8 +1,47 @@ | ||
| 1 | 1 | /* This file is automatically generated by Lemon from input grammar |
| 2 | 2 | ** source file "pikchr.y". |
| 3 | 3 | */ |
| 4 | +/* | |
| 5 | +** 2000-05-29 | |
| 6 | +** | |
| 7 | +** The author disclaims copyright to this source code. In place of | |
| 8 | +** a legal notice, here is a blessing: | |
| 9 | +** | |
| 10 | +** May you do good and not evil. | |
| 11 | +** May you find forgiveness for yourself and forgive others. | |
| 12 | +** May you share freely, never taking more than you give. | |
| 13 | +** | |
| 14 | +************************************************************************* | |
| 15 | +** Driver template for the LEMON parser generator. | |
| 16 | +** | |
| 17 | +** The "lemon" program processes an LALR(1) input grammar file, then uses | |
| 18 | +** this template to construct a parser. The "lemon" program inserts text | |
| 19 | +** at each "%%" line. Also, any "P-a-r-s-e" identifier prefix (without the | |
| 20 | +** interstitial "-" characters) contained in this template is changed into | |
| 21 | +** the value of the %name directive from the grammar. Otherwise, the content | |
| 22 | +** of this template is copied straight through into the generate parser | |
| 23 | +** source file. | |
| 24 | +** | |
| 25 | +** The following is the concatenation of all %include directives from the | |
| 26 | +** input grammar file: | |
| 27 | +*/ | |
| 28 | +/************ Begin %include sections from the grammar ************************/ | |
| 29 | +#line 1 "VERSION.h" | |
| 30 | +#define MANIFEST_UUID "8a43b020141f772a0ac45291a7fd73041d2efba5e3665c6bd2f334ad9b2e9845" | |
| 31 | +#define MANIFEST_VERSION "[8a43b02014]" | |
| 32 | +#define MANIFEST_DATE "2025-03-19 16:19:43" | |
| 33 | +#define MANIFEST_YEAR "2025" | |
| 34 | +#define MANIFEST_ISODATE "20250319161943" | |
| 35 | +#define MANIFEST_NUMERIC_DATE 20250319 | |
| 36 | +#define MANIFEST_NUMERIC_TIME 161943 | |
| 37 | +#define RELEASE_VERSION "1.0" | |
| 38 | +#define RELEASE_VERSION_NUMBER 10000 | |
| 39 | +#define RELEASE_RESOURCE_VERSION 1,0,0,0 | |
| 40 | +#define COMPILER "gcc-13.3.0" | |
| 41 | +#line 2 "pikchr.y" | |
| 42 | + | |
| 4 | 43 | /* |
| 5 | 44 | ** Zero-Clause BSD license: |
| 6 | 45 | ** |
| 7 | 46 | ** Copyright (C) 2020-09-01 by D. Richard Hipp <[email protected]> |
| 8 | 47 | ** |
| @@ -125,10 +164,22 @@ | ||
| 125 | 164 | #include <assert.h> |
| 126 | 165 | #define count(X) (sizeof(X)/sizeof(X[0])) |
| 127 | 166 | #ifndef M_PI |
| 128 | 167 | # define M_PI 3.1415926535897932385 |
| 129 | 168 | #endif |
| 169 | + | |
| 170 | +/* | |
| 171 | +** Typesafe version of ctype.h macros. Cygwin requires this, I'm told. | |
| 172 | +*/ | |
| 173 | +#define IsUpper(X) isupper((unsigned char)(X)) | |
| 174 | +#define IsLower(X) islower((unsigned char)(X)) | |
| 175 | +#define ToLower(X) tolower((unsigned char)(X)) | |
| 176 | +#define IsDigit(X) isdigit((unsigned char)(X)) | |
| 177 | +#define IsXDigit(X) isxdigit((unsigned char)(X)) | |
| 178 | +#define IsSpace(X) isspace((unsigned char)(X)) | |
| 179 | +#define IsAlnum(X) isalnum((unsigned char)(X)) | |
| 180 | + | |
| 130 | 181 | |
| 131 | 182 | /* Limit the number of tokens in a single script to avoid run-away |
| 132 | 183 | ** macro expansion attacks. See forum post |
| 133 | 184 | ** https://pikchr.org/home/forumpost/ef8684c6955a411a |
| 134 | 185 | */ |
| @@ -474,11 +525,11 @@ | ||
| 474 | 525 | static void pik_bbox_addbox(PBox*,PBox*); |
| 475 | 526 | static void pik_bbox_add_xy(PBox*,PNum,PNum); |
| 476 | 527 | static void pik_bbox_addellipse(PBox*,PNum x,PNum y,PNum rx,PNum ry); |
| 477 | 528 | static void pik_add_txt(Pik*,PToken*,int); |
| 478 | 529 | static int pik_text_length(const PToken *pToken, const int isMonospace); |
| 479 | -static void pik_size_to_fit(Pik*,PToken*,int); | |
| 530 | +static void pik_size_to_fit(Pik*,PObj*,PToken*,int); | |
| 480 | 531 | static int pik_text_position(int,PToken*); |
| 481 | 532 | static PNum pik_property_of(PObj*,PToken*); |
| 482 | 533 | static PNum pik_func(Pik*,PToken*,PNum,PNum); |
| 483 | 534 | static PPoint pik_position_between(PNum x, PPoint p1, PPoint p2); |
| 484 | 535 | static PPoint pik_position_at_angle(PNum dist, PNum r, PPoint pt); |
| @@ -492,11 +543,11 @@ | ||
| 492 | 543 | static PObj *pik_position_assert(Pik*,PPoint*,PToken*,PPoint*); |
| 493 | 544 | static PNum pik_dist(PPoint*,PPoint*); |
| 494 | 545 | static void pik_add_macro(Pik*,PToken *pId,PToken *pCode); |
| 495 | 546 | |
| 496 | 547 | |
| 497 | -#line 523 "pikchr.c" | |
| 548 | +#line 549 "pikchr.c" | |
| 498 | 549 | /**************** End of %include directives **********************************/ |
| 499 | 550 | /* These constants specify the various numeric values for terminal symbols. |
| 500 | 551 | ***************** Begin token definitions *************************************/ |
| 501 | 552 | #ifndef T_ID |
| 502 | 553 | #define T_ID 1 |
| @@ -522,84 +573,85 @@ | ||
| 522 | 573 | #define T_COLOR 21 |
| 523 | 574 | #define T_THICKNESS 22 |
| 524 | 575 | #define T_PRINT 23 |
| 525 | 576 | #define T_STRING 24 |
| 526 | 577 | #define T_COMMA 25 |
| 527 | -#define T_CLASSNAME 26 | |
| 528 | -#define T_LB 27 | |
| 529 | -#define T_RB 28 | |
| 530 | -#define T_UP 29 | |
| 531 | -#define T_DOWN 30 | |
| 532 | -#define T_LEFT 31 | |
| 533 | -#define T_RIGHT 32 | |
| 534 | -#define T_CLOSE 33 | |
| 535 | -#define T_CHOP 34 | |
| 536 | -#define T_FROM 35 | |
| 537 | -#define T_TO 36 | |
| 538 | -#define T_THEN 37 | |
| 539 | -#define T_HEADING 38 | |
| 540 | -#define T_GO 39 | |
| 541 | -#define T_AT 40 | |
| 542 | -#define T_WITH 41 | |
| 543 | -#define T_SAME 42 | |
| 544 | -#define T_AS 43 | |
| 545 | -#define T_FIT 44 | |
| 546 | -#define T_BEHIND 45 | |
| 547 | -#define T_UNTIL 46 | |
| 548 | -#define T_EVEN 47 | |
| 549 | -#define T_DOT_E 48 | |
| 550 | -#define T_HEIGHT 49 | |
| 551 | -#define T_WIDTH 50 | |
| 552 | -#define T_RADIUS 51 | |
| 553 | -#define T_DIAMETER 52 | |
| 554 | -#define T_DOTTED 53 | |
| 555 | -#define T_DASHED 54 | |
| 556 | -#define T_CW 55 | |
| 557 | -#define T_CCW 56 | |
| 558 | -#define T_LARROW 57 | |
| 559 | -#define T_RARROW 58 | |
| 560 | -#define T_LRARROW 59 | |
| 561 | -#define T_INVIS 60 | |
| 562 | -#define T_THICK 61 | |
| 563 | -#define T_THIN 62 | |
| 564 | -#define T_SOLID 63 | |
| 565 | -#define T_CENTER 64 | |
| 566 | -#define T_LJUST 65 | |
| 567 | -#define T_RJUST 66 | |
| 568 | -#define T_ABOVE 67 | |
| 569 | -#define T_BELOW 68 | |
| 570 | -#define T_ITALIC 69 | |
| 571 | -#define T_BOLD 70 | |
| 572 | -#define T_MONO 71 | |
| 573 | -#define T_ALIGNED 72 | |
| 574 | -#define T_BIG 73 | |
| 575 | -#define T_SMALL 74 | |
| 576 | -#define T_AND 75 | |
| 577 | -#define T_LT 76 | |
| 578 | -#define T_GT 77 | |
| 579 | -#define T_ON 78 | |
| 580 | -#define T_WAY 79 | |
| 581 | -#define T_BETWEEN 80 | |
| 582 | -#define T_THE 81 | |
| 583 | -#define T_NTH 82 | |
| 584 | -#define T_VERTEX 83 | |
| 585 | -#define T_TOP 84 | |
| 586 | -#define T_BOTTOM 85 | |
| 587 | -#define T_START 86 | |
| 588 | -#define T_END 87 | |
| 589 | -#define T_IN 88 | |
| 590 | -#define T_THIS 89 | |
| 591 | -#define T_DOT_U 90 | |
| 592 | -#define T_LAST 91 | |
| 593 | -#define T_NUMBER 92 | |
| 594 | -#define T_FUNC1 93 | |
| 595 | -#define T_FUNC2 94 | |
| 596 | -#define T_DIST 95 | |
| 597 | -#define T_DOT_XY 96 | |
| 598 | -#define T_X 97 | |
| 599 | -#define T_Y 98 | |
| 600 | -#define T_DOT_L 99 | |
| 578 | +#define T_ISODATE 26 | |
| 579 | +#define T_CLASSNAME 27 | |
| 580 | +#define T_LB 28 | |
| 581 | +#define T_RB 29 | |
| 582 | +#define T_UP 30 | |
| 583 | +#define T_DOWN 31 | |
| 584 | +#define T_LEFT 32 | |
| 585 | +#define T_RIGHT 33 | |
| 586 | +#define T_CLOSE 34 | |
| 587 | +#define T_CHOP 35 | |
| 588 | +#define T_FROM 36 | |
| 589 | +#define T_TO 37 | |
| 590 | +#define T_THEN 38 | |
| 591 | +#define T_HEADING 39 | |
| 592 | +#define T_GO 40 | |
| 593 | +#define T_AT 41 | |
| 594 | +#define T_WITH 42 | |
| 595 | +#define T_SAME 43 | |
| 596 | +#define T_AS 44 | |
| 597 | +#define T_FIT 45 | |
| 598 | +#define T_BEHIND 46 | |
| 599 | +#define T_UNTIL 47 | |
| 600 | +#define T_EVEN 48 | |
| 601 | +#define T_DOT_E 49 | |
| 602 | +#define T_HEIGHT 50 | |
| 603 | +#define T_WIDTH 51 | |
| 604 | +#define T_RADIUS 52 | |
| 605 | +#define T_DIAMETER 53 | |
| 606 | +#define T_DOTTED 54 | |
| 607 | +#define T_DASHED 55 | |
| 608 | +#define T_CW 56 | |
| 609 | +#define T_CCW 57 | |
| 610 | +#define T_LARROW 58 | |
| 611 | +#define T_RARROW 59 | |
| 612 | +#define T_LRARROW 60 | |
| 613 | +#define T_INVIS 61 | |
| 614 | +#define T_THICK 62 | |
| 615 | +#define T_THIN 63 | |
| 616 | +#define T_SOLID 64 | |
| 617 | +#define T_CENTER 65 | |
| 618 | +#define T_LJUST 66 | |
| 619 | +#define T_RJUST 67 | |
| 620 | +#define T_ABOVE 68 | |
| 621 | +#define T_BELOW 69 | |
| 622 | +#define T_ITALIC 70 | |
| 623 | +#define T_BOLD 71 | |
| 624 | +#define T_MONO 72 | |
| 625 | +#define T_ALIGNED 73 | |
| 626 | +#define T_BIG 74 | |
| 627 | +#define T_SMALL 75 | |
| 628 | +#define T_AND 76 | |
| 629 | +#define T_LT 77 | |
| 630 | +#define T_GT 78 | |
| 631 | +#define T_ON 79 | |
| 632 | +#define T_WAY 80 | |
| 633 | +#define T_BETWEEN 81 | |
| 634 | +#define T_THE 82 | |
| 635 | +#define T_NTH 83 | |
| 636 | +#define T_VERTEX 84 | |
| 637 | +#define T_TOP 85 | |
| 638 | +#define T_BOTTOM 86 | |
| 639 | +#define T_START 87 | |
| 640 | +#define T_END 88 | |
| 641 | +#define T_IN 89 | |
| 642 | +#define T_THIS 90 | |
| 643 | +#define T_DOT_U 91 | |
| 644 | +#define T_LAST 92 | |
| 645 | +#define T_NUMBER 93 | |
| 646 | +#define T_FUNC1 94 | |
| 647 | +#define T_FUNC2 95 | |
| 648 | +#define T_DIST 96 | |
| 649 | +#define T_DOT_XY 97 | |
| 650 | +#define T_X 98 | |
| 651 | +#define T_Y 99 | |
| 652 | +#define T_DOT_L 100 | |
| 601 | 653 | #endif |
| 602 | 654 | /**************** End token definitions ***************************************/ |
| 603 | 655 | |
| 604 | 656 | /* The next sections is a series of control #defines. |
| 605 | 657 | ** various aspects of the generated parser. |
| @@ -660,22 +712,22 @@ | ||
| 660 | 712 | #ifndef INTERFACE |
| 661 | 713 | # define INTERFACE 1 |
| 662 | 714 | #endif |
| 663 | 715 | /************* Begin control #defines *****************************************/ |
| 664 | 716 | #define YYCODETYPE unsigned char |
| 665 | -#define YYNOCODE 136 | |
| 717 | +#define YYNOCODE 138 | |
| 666 | 718 | #define YYACTIONTYPE unsigned short int |
| 667 | 719 | #define pik_parserTOKENTYPE PToken |
| 668 | 720 | typedef union { |
| 669 | 721 | int yyinit; |
| 670 | 722 | pik_parserTOKENTYPE yy0; |
| 671 | - PNum yy21; | |
| 672 | - PPoint yy63; | |
| 673 | - PRel yy72; | |
| 674 | - PObj* yy162; | |
| 675 | - short int yy188; | |
| 676 | - PList* yy235; | |
| 723 | + PList* yy23; | |
| 724 | + PRel yy28; | |
| 725 | + PObj* yy54; | |
| 726 | + PNum yy129; | |
| 727 | + PPoint yy187; | |
| 728 | + short int yy272; | |
| 677 | 729 | } YYMINORTYPE; |
| 678 | 730 | #ifndef YYSTACKDEPTH |
| 679 | 731 | #define YYSTACKDEPTH 100 |
| 680 | 732 | #endif |
| 681 | 733 | #define pik_parserARG_SDECL |
| @@ -693,21 +745,21 @@ | ||
| 693 | 745 | #define pik_parserCTX_STORE yypParser->p=p; |
| 694 | 746 | #define YYFALLBACK 1 |
| 695 | 747 | #define YYNSTATE 164 |
| 696 | 748 | #define YYNRULE 156 |
| 697 | 749 | #define YYNRULE_WITH_ACTION 116 |
| 698 | -#define YYNTOKEN 100 | |
| 750 | +#define YYNTOKEN 101 | |
| 699 | 751 | #define YY_MAX_SHIFT 163 |
| 700 | 752 | #define YY_MIN_SHIFTREDUCE 287 |
| 701 | 753 | #define YY_MAX_SHIFTREDUCE 442 |
| 702 | 754 | #define YY_ERROR_ACTION 443 |
| 703 | 755 | #define YY_ACCEPT_ACTION 444 |
| 704 | 756 | #define YY_NO_ACTION 445 |
| 705 | 757 | #define YY_MIN_REDUCE 446 |
| 706 | 758 | #define YY_MAX_REDUCE 601 |
| 707 | -#define YY_MIN_DSTRCTR 100 | |
| 708 | -#define YY_MAX_DSTRCTR 103 | |
| 759 | +#define YY_MIN_DSTRCTR 101 | |
| 760 | +#define YY_MAX_DSTRCTR 104 | |
| 709 | 761 | /************* End control #defines *******************************************/ |
| 710 | 762 | #define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0]))) |
| 711 | 763 | |
| 712 | 764 | /* Define the yytestcase() macro to be a no-op if is not already defined |
| 713 | 765 | ** otherwise. |
| @@ -786,324 +838,322 @@ | ||
| 786 | 838 | ** yy_reduce_ofst[] For each state, the offset into yy_action for |
| 787 | 839 | ** shifting non-terminals after a reduce. |
| 788 | 840 | ** yy_default[] Default action for each state. |
| 789 | 841 | ** |
| 790 | 842 | *********** Begin parsing tables **********************************************/ |
| 791 | -#define YY_ACTTAB_COUNT (1313) | |
| 843 | +#define YY_ACTTAB_COUNT (1305) | |
| 792 | 844 | static const YYACTIONTYPE yy_action[] = { |
| 793 | 845 | /* 0 */ 575, 495, 161, 119, 25, 452, 29, 74, 129, 148, |
| 794 | - /* 10 */ 575, 492, 161, 119, 453, 113, 120, 161, 119, 530, | |
| 795 | - /* 20 */ 427, 428, 339, 559, 81, 30, 560, 561, 575, 64, | |
| 796 | - /* 30 */ 63, 62, 61, 322, 323, 9, 8, 33, 149, 32, | |
| 797 | - /* 40 */ 7, 71, 127, 38, 335, 66, 48, 37, 28, 339, | |
| 798 | - /* 50 */ 339, 339, 339, 425, 426, 340, 341, 342, 343, 344, | |
| 799 | - /* 60 */ 345, 346, 347, 348, 474, 528, 161, 119, 577, 77, | |
| 800 | - /* 70 */ 577, 73, 306, 148, 474, 533, 161, 119, 112, 113, | |
| 801 | - /* 80 */ 120, 161, 119, 128, 427, 428, 339, 31, 81, 531, | |
| 802 | - /* 90 */ 161, 119, 474, 35, 330, 378, 158, 322, 323, 9, | |
| 803 | - /* 100 */ 8, 33, 149, 32, 7, 71, 127, 328, 335, 66, | |
| 804 | - /* 110 */ 579, 378, 158, 339, 339, 339, 339, 425, 426, 340, | |
| 805 | - /* 120 */ 341, 342, 343, 344, 345, 346, 347, 348, 394, 435, | |
| 806 | - /* 130 */ 46, 59, 60, 64, 63, 62, 61, 357, 36, 376, | |
| 807 | - /* 140 */ 54, 51, 2, 47, 403, 13, 297, 411, 412, 413, | |
| 808 | - /* 150 */ 414, 80, 162, 308, 79, 133, 310, 126, 441, 440, | |
| 809 | - /* 160 */ 118, 123, 83, 404, 405, 406, 408, 80, 84, 308, | |
| 810 | - /* 170 */ 79, 299, 411, 412, 413, 414, 118, 69, 350, 350, | |
| 811 | - /* 180 */ 350, 350, 350, 350, 350, 350, 350, 350, 350, 62, | |
| 812 | - /* 190 */ 61, 434, 64, 63, 62, 61, 313, 398, 399, 427, | |
| 813 | - /* 200 */ 428, 339, 380, 157, 64, 63, 62, 61, 122, 106, | |
| 814 | - /* 210 */ 535, 436, 437, 438, 439, 298, 375, 391, 117, 393, | |
| 815 | - /* 220 */ 155, 154, 153, 394, 435, 49, 59, 60, 339, 339, | |
| 816 | - /* 230 */ 339, 339, 425, 426, 376, 3, 4, 2, 64, 63, | |
| 817 | - /* 240 */ 62, 61, 156, 156, 156, 394, 379, 159, 59, 60, | |
| 818 | - /* 250 */ 76, 67, 535, 441, 440, 5, 102, 6, 535, 42, | |
| 819 | - /* 260 */ 131, 535, 69, 107, 301, 302, 303, 394, 305, 15, | |
| 820 | - /* 270 */ 59, 60, 120, 161, 119, 446, 463, 424, 376, 423, | |
| 821 | - /* 280 */ 1, 42, 397, 78, 78, 36, 434, 11, 394, 435, | |
| 822 | - /* 290 */ 356, 59, 60, 12, 152, 139, 432, 14, 16, 376, | |
| 823 | - /* 300 */ 18, 65, 2, 138, 106, 430, 436, 437, 438, 439, | |
| 824 | - /* 310 */ 44, 375, 19, 117, 393, 155, 154, 153, 441, 440, | |
| 825 | - /* 320 */ 142, 140, 64, 63, 62, 61, 106, 20, 68, 376, | |
| 826 | - /* 330 */ 359, 107, 23, 375, 45, 117, 393, 155, 154, 153, | |
| 827 | - /* 340 */ 120, 161, 119, 55, 463, 114, 26, 57, 106, 147, | |
| 828 | - /* 350 */ 146, 434, 569, 58, 392, 375, 43, 117, 393, 155, | |
| 829 | - /* 360 */ 154, 153, 152, 384, 64, 63, 62, 61, 382, 106, | |
| 830 | - /* 370 */ 383, 436, 437, 438, 439, 377, 375, 70, 117, 393, | |
| 831 | - /* 380 */ 155, 154, 153, 160, 39, 22, 21, 445, 142, 140, | |
| 832 | - /* 390 */ 64, 63, 62, 61, 24, 17, 145, 141, 431, 108, | |
| 833 | - /* 400 */ 445, 445, 445, 391, 445, 445, 375, 445, 117, 445, | |
| 834 | - /* 410 */ 445, 55, 74, 445, 148, 445, 445, 147, 146, 124, | |
| 835 | - /* 420 */ 113, 120, 161, 119, 43, 445, 445, 142, 140, 64, | |
| 836 | - /* 430 */ 63, 62, 61, 445, 394, 445, 445, 59, 60, 64, | |
| 837 | - /* 440 */ 63, 62, 61, 149, 445, 376, 445, 445, 42, 445, | |
| 838 | - /* 450 */ 55, 445, 391, 22, 21, 445, 147, 146, 445, 445, | |
| 839 | - /* 460 */ 52, 445, 24, 43, 145, 141, 431, 394, 445, 445, | |
| 840 | - /* 470 */ 59, 60, 64, 63, 62, 61, 445, 445, 376, 132, | |
| 841 | - /* 480 */ 130, 42, 445, 445, 445, 355, 156, 156, 156, 445, | |
| 842 | - /* 490 */ 445, 445, 22, 21, 445, 394, 473, 445, 59, 60, | |
| 843 | - /* 500 */ 445, 24, 445, 145, 141, 431, 376, 445, 107, 42, | |
| 844 | - /* 510 */ 64, 63, 62, 61, 445, 106, 445, 120, 161, 119, | |
| 845 | - /* 520 */ 445, 478, 375, 354, 117, 393, 155, 154, 153, 445, | |
| 846 | - /* 530 */ 394, 143, 473, 59, 60, 64, 63, 62, 61, 152, | |
| 847 | - /* 540 */ 445, 376, 445, 445, 42, 445, 445, 445, 106, 64, | |
| 848 | - /* 550 */ 63, 62, 61, 445, 445, 375, 50, 117, 393, 155, | |
| 849 | - /* 560 */ 154, 153, 445, 394, 144, 445, 59, 60, 445, 445, | |
| 850 | - /* 570 */ 53, 72, 445, 148, 376, 445, 106, 42, 125, 113, | |
| 851 | - /* 580 */ 120, 161, 119, 375, 445, 117, 393, 155, 154, 153, | |
| 852 | - /* 590 */ 394, 445, 445, 59, 60, 445, 445, 445, 445, 445, | |
| 853 | - /* 600 */ 445, 102, 149, 445, 42, 445, 74, 445, 148, 445, | |
| 854 | - /* 610 */ 445, 106, 445, 497, 113, 120, 161, 119, 375, 445, | |
| 855 | - /* 620 */ 117, 393, 155, 154, 153, 394, 445, 445, 59, 60, | |
| 856 | - /* 630 */ 445, 445, 88, 445, 445, 445, 376, 149, 445, 40, | |
| 857 | - /* 640 */ 445, 120, 161, 119, 106, 445, 445, 435, 110, 110, | |
| 858 | - /* 650 */ 445, 375, 445, 117, 393, 155, 154, 153, 394, 445, | |
| 859 | - /* 660 */ 445, 59, 60, 152, 85, 445, 445, 445, 445, 376, | |
| 860 | - /* 670 */ 445, 106, 41, 120, 161, 119, 441, 440, 375, 445, | |
| 861 | - /* 680 */ 117, 393, 155, 154, 153, 448, 454, 29, 445, 445, | |
| 862 | - /* 690 */ 74, 450, 148, 75, 88, 152, 445, 496, 113, 120, | |
| 863 | - /* 700 */ 161, 119, 163, 120, 161, 119, 106, 27, 445, 434, | |
| 864 | - /* 710 */ 111, 111, 445, 375, 445, 117, 393, 155, 154, 153, | |
| 865 | - /* 720 */ 445, 149, 445, 445, 445, 152, 74, 445, 148, 436, | |
| 866 | - /* 730 */ 437, 438, 439, 490, 113, 120, 161, 119, 445, 106, | |
| 867 | - /* 740 */ 121, 447, 454, 29, 445, 445, 375, 450, 117, 393, | |
| 868 | - /* 750 */ 155, 154, 153, 445, 445, 445, 445, 149, 163, 74, | |
| 869 | - /* 760 */ 445, 148, 444, 27, 445, 445, 484, 113, 120, 161, | |
| 870 | - /* 770 */ 119, 445, 445, 445, 74, 445, 148, 445, 445, 445, | |
| 871 | - /* 780 */ 445, 483, 113, 120, 161, 119, 74, 445, 148, 86, | |
| 872 | - /* 790 */ 149, 445, 445, 480, 113, 120, 161, 119, 120, 161, | |
| 873 | - /* 800 */ 119, 445, 74, 445, 148, 149, 445, 445, 445, 134, | |
| 874 | - /* 810 */ 113, 120, 161, 119, 74, 445, 148, 149, 445, 445, | |
| 875 | - /* 820 */ 152, 517, 113, 120, 161, 119, 88, 64, 63, 62, | |
| 876 | - /* 830 */ 61, 445, 445, 149, 445, 120, 161, 119, 445, 74, | |
| 877 | - /* 840 */ 396, 148, 475, 445, 445, 149, 137, 113, 120, 161, | |
| 878 | - /* 850 */ 119, 74, 445, 148, 445, 445, 445, 152, 525, 113, | |
| 879 | - /* 860 */ 120, 161, 119, 445, 74, 445, 148, 445, 445, 445, | |
| 880 | - /* 870 */ 149, 527, 113, 120, 161, 119, 445, 445, 445, 74, | |
| 881 | - /* 880 */ 445, 148, 149, 445, 445, 445, 524, 113, 120, 161, | |
| 882 | - /* 890 */ 119, 74, 445, 148, 98, 149, 445, 445, 526, 113, | |
| 883 | - /* 900 */ 120, 161, 119, 120, 161, 119, 445, 74, 445, 148, | |
| 884 | - /* 910 */ 149, 445, 445, 445, 523, 113, 120, 161, 119, 74, | |
| 885 | - /* 920 */ 445, 148, 149, 445, 445, 152, 522, 113, 120, 161, | |
| 886 | - /* 930 */ 119, 89, 64, 63, 62, 61, 445, 445, 149, 445, | |
| 887 | - /* 940 */ 120, 161, 119, 445, 74, 395, 148, 445, 445, 445, | |
| 888 | - /* 950 */ 149, 521, 113, 120, 161, 119, 74, 445, 148, 445, | |
| 889 | - /* 960 */ 445, 445, 152, 520, 113, 120, 161, 119, 445, 74, | |
| 890 | - /* 970 */ 445, 148, 445, 445, 445, 149, 519, 113, 120, 161, | |
| 891 | - /* 980 */ 119, 445, 445, 445, 74, 445, 148, 149, 445, 445, | |
| 892 | - /* 990 */ 445, 150, 113, 120, 161, 119, 74, 445, 148, 90, | |
| 893 | - /* 1000 */ 149, 445, 445, 151, 113, 120, 161, 119, 120, 161, | |
| 894 | - /* 1010 */ 119, 445, 74, 445, 148, 149, 445, 435, 445, 136, | |
| 895 | - /* 1020 */ 113, 120, 161, 119, 74, 445, 148, 149, 445, 445, | |
| 896 | - /* 1030 */ 152, 135, 113, 120, 161, 119, 64, 63, 62, 61, | |
| 897 | - /* 1040 */ 445, 445, 445, 149, 445, 445, 441, 440, 445, 88, | |
| 898 | - /* 1050 */ 445, 445, 445, 445, 445, 149, 445, 56, 120, 161, | |
| 899 | - /* 1060 */ 119, 88, 445, 445, 10, 479, 479, 445, 445, 445, | |
| 900 | - /* 1070 */ 120, 161, 119, 445, 445, 445, 445, 82, 445, 434, | |
| 901 | - /* 1080 */ 152, 445, 445, 445, 466, 445, 34, 109, 447, 454, | |
| 902 | - /* 1090 */ 29, 445, 152, 445, 450, 445, 445, 445, 107, 436, | |
| 903 | - /* 1100 */ 437, 438, 439, 87, 445, 163, 445, 120, 161, 119, | |
| 904 | - /* 1110 */ 27, 451, 120, 161, 119, 99, 445, 64, 63, 62, | |
| 905 | - /* 1120 */ 61, 445, 100, 445, 120, 161, 119, 101, 445, 152, | |
| 906 | - /* 1130 */ 391, 120, 161, 119, 152, 445, 120, 161, 119, 91, | |
| 907 | - /* 1140 */ 445, 445, 445, 445, 445, 445, 152, 445, 120, 161, | |
| 908 | - /* 1150 */ 119, 103, 445, 152, 92, 445, 445, 445, 152, 445, | |
| 909 | - /* 1160 */ 120, 161, 119, 120, 161, 119, 93, 445, 445, 104, | |
| 910 | - /* 1170 */ 152, 445, 445, 445, 445, 120, 161, 119, 120, 161, | |
| 911 | - /* 1180 */ 119, 445, 152, 445, 94, 152, 445, 445, 445, 445, | |
| 912 | - /* 1190 */ 445, 445, 105, 120, 161, 119, 445, 152, 445, 95, | |
| 913 | - /* 1200 */ 152, 120, 161, 119, 445, 445, 445, 96, 120, 161, | |
| 914 | - /* 1210 */ 119, 445, 445, 445, 445, 152, 120, 161, 119, 445, | |
| 915 | - /* 1220 */ 445, 445, 445, 152, 445, 445, 445, 445, 445, 445, | |
| 916 | - /* 1230 */ 152, 97, 445, 445, 549, 445, 445, 548, 152, 445, | |
| 917 | - /* 1240 */ 120, 161, 119, 120, 161, 119, 120, 161, 119, 445, | |
| 918 | - /* 1250 */ 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, | |
| 919 | - /* 1260 */ 445, 445, 152, 547, 445, 152, 546, 445, 152, 115, | |
| 920 | - /* 1270 */ 445, 445, 120, 161, 119, 120, 161, 119, 120, 161, | |
| 921 | - /* 1280 */ 119, 116, 445, 445, 445, 445, 445, 445, 445, 445, | |
| 922 | - /* 1290 */ 120, 161, 119, 445, 152, 445, 445, 152, 445, 445, | |
| 923 | - /* 1300 */ 152, 445, 445, 445, 445, 445, 445, 445, 445, 445, | |
| 924 | - /* 1310 */ 445, 445, 152, | |
| 846 | + /* 10 */ 575, 64, 63, 62, 61, 453, 113, 120, 161, 119, | |
| 847 | + /* 20 */ 427, 428, 339, 357, 81, 121, 447, 454, 29, 575, | |
| 848 | + /* 30 */ 530, 13, 50, 450, 322, 323, 9, 8, 33, 149, | |
| 849 | + /* 40 */ 32, 7, 71, 127, 163, 335, 66, 28, 444, 27, | |
| 850 | + /* 50 */ 339, 339, 339, 339, 425, 426, 340, 341, 342, 343, | |
| 851 | + /* 60 */ 344, 345, 346, 347, 348, 474, 64, 63, 62, 61, | |
| 852 | + /* 70 */ 54, 51, 73, 306, 148, 474, 492, 161, 119, 297, | |
| 853 | + /* 80 */ 112, 113, 120, 161, 119, 427, 428, 339, 30, 81, | |
| 854 | + /* 90 */ 109, 447, 454, 29, 474, 528, 161, 119, 450, 322, | |
| 855 | + /* 100 */ 323, 9, 8, 33, 149, 32, 7, 71, 127, 163, | |
| 856 | + /* 110 */ 335, 66, 535, 36, 27, 339, 339, 339, 339, 425, | |
| 857 | + /* 120 */ 426, 340, 341, 342, 343, 344, 345, 346, 347, 348, | |
| 858 | + /* 130 */ 394, 435, 310, 59, 60, 64, 63, 62, 61, 313, | |
| 859 | + /* 140 */ 74, 376, 148, 69, 2, 533, 161, 119, 124, 113, | |
| 860 | + /* 150 */ 120, 161, 119, 80, 535, 31, 308, 79, 83, 107, | |
| 861 | + /* 160 */ 535, 441, 440, 535, 394, 435, 299, 59, 60, 120, | |
| 862 | + /* 170 */ 161, 119, 149, 463, 376, 376, 330, 84, 2, 122, | |
| 863 | + /* 180 */ 78, 78, 38, 156, 156, 156, 48, 37, 559, 328, | |
| 864 | + /* 190 */ 128, 152, 560, 561, 434, 441, 440, 350, 350, 350, | |
| 865 | + /* 200 */ 350, 350, 350, 350, 350, 350, 350, 350, 577, 77, | |
| 866 | + /* 210 */ 577, 35, 106, 46, 436, 437, 438, 439, 579, 375, | |
| 867 | + /* 220 */ 298, 117, 393, 155, 154, 153, 47, 4, 434, 69, | |
| 868 | + /* 230 */ 394, 435, 3, 59, 60, 411, 412, 413, 414, 398, | |
| 869 | + /* 240 */ 399, 376, 62, 61, 2, 108, 106, 5, 436, 437, | |
| 870 | + /* 250 */ 438, 439, 375, 375, 117, 117, 393, 155, 154, 153, | |
| 871 | + /* 260 */ 76, 441, 440, 67, 6, 142, 140, 64, 63, 62, | |
| 872 | + /* 270 */ 61, 380, 157, 424, 427, 428, 339, 379, 159, 45, | |
| 873 | + /* 280 */ 423, 72, 131, 148, 531, 161, 119, 1, 55, 125, | |
| 874 | + /* 290 */ 113, 120, 161, 119, 434, 147, 146, 64, 63, 62, | |
| 875 | + /* 300 */ 61, 397, 43, 11, 339, 339, 339, 339, 425, 426, | |
| 876 | + /* 310 */ 355, 65, 106, 149, 436, 437, 438, 439, 74, 375, | |
| 877 | + /* 320 */ 148, 117, 393, 155, 154, 153, 497, 113, 120, 161, | |
| 878 | + /* 330 */ 119, 22, 21, 12, 142, 140, 64, 63, 62, 61, | |
| 879 | + /* 340 */ 24, 356, 145, 141, 431, 64, 63, 62, 61, 391, | |
| 880 | + /* 350 */ 149, 448, 454, 29, 378, 158, 85, 55, 450, 394, | |
| 881 | + /* 360 */ 432, 138, 59, 60, 147, 146, 120, 161, 119, 163, | |
| 882 | + /* 370 */ 102, 43, 139, 42, 27, 430, 14, 15, 301, 302, | |
| 883 | + /* 380 */ 303, 446, 305, 16, 44, 74, 18, 148, 152, 19, | |
| 884 | + /* 390 */ 20, 36, 68, 496, 113, 120, 161, 119, 114, 359, | |
| 885 | + /* 400 */ 22, 21, 23, 142, 140, 64, 63, 62, 61, 24, | |
| 886 | + /* 410 */ 107, 145, 141, 431, 26, 57, 377, 149, 58, 118, | |
| 887 | + /* 420 */ 120, 161, 119, 392, 463, 384, 55, 64, 63, 62, | |
| 888 | + /* 430 */ 61, 382, 569, 147, 146, 160, 383, 435, 39, 70, | |
| 889 | + /* 440 */ 43, 106, 152, 445, 445, 88, 445, 445, 375, 445, | |
| 890 | + /* 450 */ 117, 393, 155, 154, 153, 120, 161, 119, 445, 17, | |
| 891 | + /* 460 */ 445, 10, 479, 479, 445, 445, 435, 441, 440, 22, | |
| 892 | + /* 470 */ 21, 445, 403, 64, 63, 62, 61, 152, 24, 445, | |
| 893 | + /* 480 */ 145, 141, 431, 133, 75, 126, 354, 445, 445, 123, | |
| 894 | + /* 490 */ 445, 404, 405, 406, 408, 80, 441, 440, 308, 79, | |
| 895 | + /* 500 */ 434, 411, 412, 413, 414, 394, 445, 445, 59, 60, | |
| 896 | + /* 510 */ 64, 63, 62, 61, 445, 445, 376, 445, 445, 42, | |
| 897 | + /* 520 */ 436, 437, 438, 439, 156, 156, 156, 394, 445, 434, | |
| 898 | + /* 530 */ 59, 60, 64, 63, 62, 61, 445, 445, 376, 445, | |
| 899 | + /* 540 */ 445, 42, 445, 394, 473, 391, 59, 60, 445, 436, | |
| 900 | + /* 550 */ 437, 438, 439, 49, 376, 445, 74, 42, 148, 445, | |
| 901 | + /* 560 */ 88, 445, 445, 445, 490, 113, 120, 161, 119, 445, | |
| 902 | + /* 570 */ 120, 161, 119, 132, 130, 394, 143, 475, 59, 60, | |
| 903 | + /* 580 */ 445, 473, 64, 63, 62, 61, 376, 106, 149, 42, | |
| 904 | + /* 590 */ 445, 445, 152, 445, 375, 391, 117, 393, 155, 154, | |
| 905 | + /* 600 */ 153, 394, 144, 52, 59, 60, 445, 445, 445, 106, | |
| 906 | + /* 610 */ 445, 445, 376, 445, 445, 42, 375, 445, 117, 393, | |
| 907 | + /* 620 */ 155, 154, 153, 445, 445, 106, 64, 63, 62, 61, | |
| 908 | + /* 630 */ 445, 445, 375, 445, 117, 393, 155, 154, 153, 394, | |
| 909 | + /* 640 */ 445, 445, 59, 60, 88, 445, 445, 53, 445, 445, | |
| 910 | + /* 650 */ 376, 445, 445, 42, 120, 161, 119, 106, 445, 445, | |
| 911 | + /* 660 */ 445, 110, 110, 445, 375, 445, 117, 393, 155, 154, | |
| 912 | + /* 670 */ 153, 394, 445, 445, 59, 60, 152, 107, 445, 445, | |
| 913 | + /* 680 */ 445, 445, 102, 106, 445, 42, 445, 120, 161, 119, | |
| 914 | + /* 690 */ 375, 451, 117, 393, 155, 154, 153, 394, 445, 445, | |
| 915 | + /* 700 */ 59, 60, 64, 63, 62, 61, 445, 445, 376, 152, | |
| 916 | + /* 710 */ 445, 40, 445, 394, 445, 396, 59, 60, 445, 445, | |
| 917 | + /* 720 */ 445, 106, 445, 445, 376, 88, 445, 41, 375, 445, | |
| 918 | + /* 730 */ 117, 393, 155, 154, 153, 120, 161, 119, 74, 445, | |
| 919 | + /* 740 */ 148, 445, 111, 111, 107, 445, 484, 113, 120, 161, | |
| 920 | + /* 750 */ 119, 445, 445, 106, 120, 161, 119, 152, 478, 445, | |
| 921 | + /* 760 */ 375, 86, 117, 393, 155, 154, 153, 445, 445, 445, | |
| 922 | + /* 770 */ 149, 120, 161, 119, 445, 445, 152, 445, 445, 106, | |
| 923 | + /* 780 */ 445, 64, 63, 62, 61, 445, 375, 445, 117, 393, | |
| 924 | + /* 790 */ 155, 154, 153, 152, 395, 106, 64, 63, 62, 61, | |
| 925 | + /* 800 */ 98, 445, 375, 445, 117, 393, 155, 154, 153, 445, | |
| 926 | + /* 810 */ 120, 161, 119, 445, 74, 445, 148, 56, 445, 74, | |
| 927 | + /* 820 */ 445, 148, 483, 113, 120, 161, 119, 480, 113, 120, | |
| 928 | + /* 830 */ 161, 119, 152, 74, 445, 148, 445, 89, 445, 445, | |
| 929 | + /* 840 */ 445, 134, 113, 120, 161, 119, 149, 120, 161, 119, | |
| 930 | + /* 850 */ 445, 149, 74, 445, 148, 445, 445, 445, 378, 158, | |
| 931 | + /* 860 */ 517, 113, 120, 161, 119, 149, 74, 445, 148, 152, | |
| 932 | + /* 870 */ 445, 74, 445, 148, 137, 113, 120, 161, 119, 525, | |
| 933 | + /* 880 */ 113, 120, 161, 119, 149, 74, 445, 148, 64, 63, | |
| 934 | + /* 890 */ 62, 61, 445, 527, 113, 120, 161, 119, 149, 445, | |
| 935 | + /* 900 */ 445, 391, 445, 149, 445, 445, 445, 445, 445, 445, | |
| 936 | + /* 910 */ 74, 445, 148, 445, 445, 162, 445, 149, 524, 113, | |
| 937 | + /* 920 */ 120, 161, 119, 118, 445, 74, 445, 148, 445, 445, | |
| 938 | + /* 930 */ 445, 445, 445, 526, 113, 120, 161, 119, 445, 74, | |
| 939 | + /* 940 */ 445, 148, 149, 445, 445, 445, 445, 523, 113, 120, | |
| 940 | + /* 950 */ 161, 119, 74, 445, 148, 445, 445, 149, 445, 445, | |
| 941 | + /* 960 */ 522, 113, 120, 161, 119, 445, 74, 445, 148, 445, | |
| 942 | + /* 970 */ 445, 149, 445, 445, 521, 113, 120, 161, 119, 74, | |
| 943 | + /* 980 */ 445, 148, 445, 445, 149, 445, 445, 520, 113, 120, | |
| 944 | + /* 990 */ 161, 119, 445, 74, 445, 148, 445, 445, 149, 445, | |
| 945 | + /* 1000 */ 445, 519, 113, 120, 161, 119, 445, 445, 445, 445, | |
| 946 | + /* 1010 */ 445, 149, 445, 445, 445, 445, 445, 445, 74, 445, | |
| 947 | + /* 1020 */ 148, 445, 445, 445, 445, 149, 150, 113, 120, 161, | |
| 948 | + /* 1030 */ 119, 74, 445, 148, 445, 445, 445, 445, 445, 151, | |
| 949 | + /* 1040 */ 113, 120, 161, 119, 445, 74, 445, 148, 445, 445, | |
| 950 | + /* 1050 */ 149, 445, 445, 136, 113, 120, 161, 119, 74, 445, | |
| 951 | + /* 1060 */ 148, 445, 445, 149, 445, 445, 135, 113, 120, 161, | |
| 952 | + /* 1070 */ 119, 445, 88, 445, 445, 445, 445, 149, 445, 445, | |
| 953 | + /* 1080 */ 445, 90, 120, 161, 119, 445, 445, 445, 445, 82, | |
| 954 | + /* 1090 */ 149, 120, 161, 119, 445, 87, 466, 445, 34, 99, | |
| 955 | + /* 1100 */ 445, 445, 445, 445, 152, 120, 161, 119, 100, 120, | |
| 956 | + /* 1110 */ 161, 119, 445, 152, 445, 445, 445, 445, 120, 161, | |
| 957 | + /* 1120 */ 119, 445, 445, 445, 101, 445, 445, 152, 445, 445, | |
| 958 | + /* 1130 */ 445, 152, 91, 445, 120, 161, 119, 103, 445, 445, | |
| 959 | + /* 1140 */ 152, 445, 120, 161, 119, 445, 445, 120, 161, 119, | |
| 960 | + /* 1150 */ 445, 92, 445, 445, 445, 445, 152, 445, 445, 445, | |
| 961 | + /* 1160 */ 93, 120, 161, 119, 152, 445, 104, 445, 445, 152, | |
| 962 | + /* 1170 */ 120, 161, 119, 445, 94, 445, 120, 161, 119, 445, | |
| 963 | + /* 1180 */ 445, 445, 445, 152, 120, 161, 119, 445, 445, 105, | |
| 964 | + /* 1190 */ 445, 445, 152, 445, 445, 445, 445, 95, 152, 120, | |
| 965 | + /* 1200 */ 161, 119, 96, 445, 445, 97, 152, 120, 161, 119, | |
| 966 | + /* 1210 */ 445, 445, 120, 161, 119, 120, 161, 119, 445, 445, | |
| 967 | + /* 1220 */ 445, 152, 445, 445, 445, 445, 445, 445, 445, 152, | |
| 968 | + /* 1230 */ 549, 445, 445, 548, 152, 445, 445, 152, 547, 445, | |
| 969 | + /* 1240 */ 120, 161, 119, 120, 161, 119, 546, 445, 120, 161, | |
| 970 | + /* 1250 */ 119, 445, 445, 445, 445, 445, 120, 161, 119, 445, | |
| 971 | + /* 1260 */ 445, 445, 152, 445, 445, 152, 445, 445, 445, 115, | |
| 972 | + /* 1270 */ 152, 445, 116, 445, 445, 445, 445, 445, 152, 120, | |
| 973 | + /* 1280 */ 161, 119, 120, 161, 119, 445, 445, 445, 445, 445, | |
| 974 | + /* 1290 */ 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, | |
| 975 | + /* 1300 */ 445, 152, 445, 445, 152, | |
| 925 | 976 | }; |
| 926 | 977 | static const YYCODETYPE yy_lookahead[] = { |
| 927 | - /* 0 */ 0, 113, 114, 115, 134, 102, 103, 104, 106, 106, | |
| 928 | - /* 10 */ 10, 113, 114, 115, 111, 112, 113, 114, 115, 106, | |
| 929 | - /* 20 */ 20, 21, 22, 105, 24, 126, 108, 109, 28, 4, | |
| 930 | - /* 30 */ 5, 6, 7, 33, 34, 35, 36, 37, 135, 39, | |
| 931 | - /* 40 */ 40, 41, 42, 105, 44, 45, 108, 109, 107, 49, | |
| 978 | + /* 0 */ 0, 115, 116, 117, 136, 103, 104, 105, 107, 107, | |
| 979 | + /* 10 */ 10, 4, 5, 6, 7, 113, 114, 115, 116, 117, | |
| 980 | + /* 20 */ 20, 21, 22, 17, 24, 101, 102, 103, 104, 29, | |
| 981 | + /* 30 */ 107, 25, 25, 109, 34, 35, 36, 37, 38, 137, | |
| 982 | + /* 40 */ 40, 41, 42, 43, 120, 45, 46, 109, 124, 125, | |
| 932 | 983 | /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, |
| 933 | - /* 60 */ 60, 61, 62, 63, 0, 113, 114, 115, 130, 131, | |
| 934 | - /* 70 */ 132, 104, 25, 106, 10, 113, 114, 115, 111, 112, | |
| 935 | - /* 80 */ 113, 114, 115, 106, 20, 21, 22, 128, 24, 113, | |
| 936 | - /* 90 */ 114, 115, 28, 129, 2, 26, 27, 33, 34, 35, | |
| 937 | - /* 100 */ 36, 37, 135, 39, 40, 41, 42, 2, 44, 45, | |
| 938 | - /* 110 */ 133, 26, 27, 49, 50, 51, 52, 53, 54, 55, | |
| 939 | - /* 120 */ 56, 57, 58, 59, 60, 61, 62, 63, 1, 2, | |
| 940 | - /* 130 */ 38, 4, 5, 4, 5, 6, 7, 17, 10, 12, | |
| 941 | - /* 140 */ 4, 5, 15, 38, 1, 25, 17, 29, 30, 31, | |
| 942 | - /* 150 */ 32, 24, 83, 26, 27, 12, 28, 14, 31, 32, | |
| 943 | - /* 160 */ 91, 18, 116, 20, 21, 22, 23, 24, 116, 26, | |
| 944 | - /* 170 */ 27, 19, 29, 30, 31, 32, 91, 3, 64, 65, | |
| 945 | - /* 180 */ 66, 67, 68, 69, 70, 71, 72, 73, 74, 6, | |
| 946 | - /* 190 */ 7, 64, 4, 5, 6, 7, 8, 97, 98, 20, | |
| 947 | - /* 200 */ 21, 22, 26, 27, 4, 5, 6, 7, 1, 82, | |
| 948 | - /* 210 */ 48, 84, 85, 86, 87, 17, 89, 17, 91, 92, | |
| 949 | - /* 220 */ 93, 94, 95, 1, 2, 25, 4, 5, 49, 50, | |
| 950 | - /* 230 */ 51, 52, 53, 54, 12, 16, 15, 15, 4, 5, | |
| 951 | - /* 240 */ 6, 7, 20, 21, 22, 1, 26, 27, 4, 5, | |
| 952 | - /* 250 */ 48, 43, 90, 31, 32, 40, 12, 40, 96, 15, | |
| 953 | - /* 260 */ 47, 99, 88, 104, 20, 21, 22, 1, 24, 35, | |
| 954 | - /* 270 */ 4, 5, 113, 114, 115, 0, 117, 41, 12, 41, | |
| 955 | - /* 280 */ 13, 15, 17, 124, 125, 10, 64, 25, 1, 2, | |
| 956 | - /* 290 */ 17, 4, 5, 75, 135, 81, 80, 3, 3, 12, | |
| 957 | - /* 300 */ 3, 99, 15, 79, 82, 80, 84, 85, 86, 87, | |
| 958 | - /* 310 */ 38, 89, 3, 91, 92, 93, 94, 95, 31, 32, | |
| 959 | - /* 320 */ 2, 3, 4, 5, 6, 7, 82, 3, 3, 12, | |
| 960 | - /* 330 */ 77, 104, 25, 89, 16, 91, 92, 93, 94, 95, | |
| 961 | - /* 340 */ 113, 114, 115, 25, 117, 96, 15, 15, 82, 31, | |
| 962 | - /* 350 */ 32, 64, 125, 15, 17, 89, 38, 91, 92, 93, | |
| 963 | - /* 360 */ 94, 95, 135, 28, 4, 5, 6, 7, 28, 82, | |
| 964 | - /* 370 */ 28, 84, 85, 86, 87, 12, 89, 3, 91, 92, | |
| 965 | - /* 380 */ 93, 94, 95, 90, 11, 67, 68, 136, 2, 3, | |
| 966 | - /* 390 */ 4, 5, 6, 7, 76, 35, 78, 79, 80, 82, | |
| 967 | - /* 400 */ 136, 136, 136, 17, 136, 136, 89, 136, 91, 136, | |
| 968 | - /* 410 */ 136, 25, 104, 136, 106, 136, 136, 31, 32, 111, | |
| 969 | - /* 420 */ 112, 113, 114, 115, 38, 136, 136, 2, 3, 4, | |
| 970 | - /* 430 */ 5, 6, 7, 136, 1, 136, 136, 4, 5, 4, | |
| 971 | - /* 440 */ 5, 6, 7, 135, 136, 12, 136, 136, 15, 136, | |
| 972 | - /* 450 */ 25, 136, 17, 67, 68, 136, 31, 32, 136, 136, | |
| 973 | - /* 460 */ 25, 136, 76, 38, 78, 79, 80, 1, 136, 136, | |
| 974 | - /* 470 */ 4, 5, 4, 5, 6, 7, 136, 136, 12, 46, | |
| 975 | - /* 480 */ 47, 15, 136, 136, 136, 17, 20, 21, 22, 136, | |
| 976 | - /* 490 */ 136, 136, 67, 68, 136, 1, 2, 136, 4, 5, | |
| 977 | - /* 500 */ 136, 76, 136, 78, 79, 80, 12, 136, 104, 15, | |
| 978 | - /* 510 */ 4, 5, 6, 7, 136, 82, 136, 113, 114, 115, | |
| 979 | - /* 520 */ 136, 117, 89, 17, 91, 92, 93, 94, 95, 136, | |
| 980 | - /* 530 */ 1, 2, 38, 4, 5, 4, 5, 6, 7, 135, | |
| 981 | - /* 540 */ 136, 12, 136, 136, 15, 136, 136, 136, 82, 4, | |
| 982 | - /* 550 */ 5, 6, 7, 136, 136, 89, 25, 91, 92, 93, | |
| 983 | - /* 560 */ 94, 95, 136, 1, 2, 136, 4, 5, 136, 136, | |
| 984 | - /* 570 */ 25, 104, 136, 106, 12, 136, 82, 15, 111, 112, | |
| 985 | - /* 580 */ 113, 114, 115, 89, 136, 91, 92, 93, 94, 95, | |
| 986 | - /* 590 */ 1, 136, 136, 4, 5, 136, 136, 136, 136, 136, | |
| 987 | - /* 600 */ 136, 12, 135, 136, 15, 136, 104, 136, 106, 136, | |
| 988 | - /* 610 */ 136, 82, 136, 111, 112, 113, 114, 115, 89, 136, | |
| 989 | - /* 620 */ 91, 92, 93, 94, 95, 1, 136, 136, 4, 5, | |
| 990 | - /* 630 */ 136, 136, 104, 136, 136, 136, 12, 135, 136, 15, | |
| 991 | - /* 640 */ 136, 113, 114, 115, 82, 136, 136, 2, 120, 121, | |
| 992 | - /* 650 */ 136, 89, 136, 91, 92, 93, 94, 95, 1, 136, | |
| 993 | - /* 660 */ 136, 4, 5, 135, 104, 136, 136, 136, 136, 12, | |
| 994 | - /* 670 */ 136, 82, 15, 113, 114, 115, 31, 32, 89, 136, | |
| 995 | - /* 680 */ 91, 92, 93, 94, 95, 101, 102, 103, 136, 136, | |
| 996 | - /* 690 */ 104, 107, 106, 48, 104, 135, 136, 111, 112, 113, | |
| 997 | - /* 700 */ 114, 115, 118, 113, 114, 115, 82, 123, 136, 64, | |
| 998 | - /* 710 */ 120, 121, 136, 89, 136, 91, 92, 93, 94, 95, | |
| 999 | - /* 720 */ 136, 135, 136, 136, 136, 135, 104, 136, 106, 84, | |
| 1000 | - /* 730 */ 85, 86, 87, 111, 112, 113, 114, 115, 136, 82, | |
| 1001 | - /* 740 */ 100, 101, 102, 103, 136, 136, 89, 107, 91, 92, | |
| 1002 | - /* 750 */ 93, 94, 95, 136, 136, 136, 136, 135, 118, 104, | |
| 1003 | - /* 760 */ 136, 106, 122, 123, 136, 136, 111, 112, 113, 114, | |
| 1004 | - /* 770 */ 115, 136, 136, 136, 104, 136, 106, 136, 136, 136, | |
| 1005 | - /* 780 */ 136, 111, 112, 113, 114, 115, 104, 136, 106, 104, | |
| 1006 | - /* 790 */ 135, 136, 136, 111, 112, 113, 114, 115, 113, 114, | |
| 1007 | - /* 800 */ 115, 136, 104, 136, 106, 135, 136, 136, 136, 111, | |
| 1008 | - /* 810 */ 112, 113, 114, 115, 104, 136, 106, 135, 136, 136, | |
| 1009 | - /* 820 */ 135, 111, 112, 113, 114, 115, 104, 4, 5, 6, | |
| 1010 | - /* 830 */ 7, 136, 136, 135, 136, 113, 114, 115, 136, 104, | |
| 1011 | - /* 840 */ 17, 106, 120, 136, 136, 135, 111, 112, 113, 114, | |
| 1012 | - /* 850 */ 115, 104, 136, 106, 136, 136, 136, 135, 111, 112, | |
| 1013 | - /* 860 */ 113, 114, 115, 136, 104, 136, 106, 136, 136, 136, | |
| 1014 | - /* 870 */ 135, 111, 112, 113, 114, 115, 136, 136, 136, 104, | |
| 1015 | - /* 880 */ 136, 106, 135, 136, 136, 136, 111, 112, 113, 114, | |
| 1016 | - /* 890 */ 115, 104, 136, 106, 104, 135, 136, 136, 111, 112, | |
| 1017 | - /* 900 */ 113, 114, 115, 113, 114, 115, 136, 104, 136, 106, | |
| 1018 | - /* 910 */ 135, 136, 136, 136, 111, 112, 113, 114, 115, 104, | |
| 1019 | - /* 920 */ 136, 106, 135, 136, 136, 135, 111, 112, 113, 114, | |
| 1020 | - /* 930 */ 115, 104, 4, 5, 6, 7, 136, 136, 135, 136, | |
| 1021 | - /* 940 */ 113, 114, 115, 136, 104, 17, 106, 136, 136, 136, | |
| 1022 | - /* 950 */ 135, 111, 112, 113, 114, 115, 104, 136, 106, 136, | |
| 1023 | - /* 960 */ 136, 136, 135, 111, 112, 113, 114, 115, 136, 104, | |
| 1024 | - /* 970 */ 136, 106, 136, 136, 136, 135, 111, 112, 113, 114, | |
| 1025 | - /* 980 */ 115, 136, 136, 136, 104, 136, 106, 135, 136, 136, | |
| 1026 | - /* 990 */ 136, 111, 112, 113, 114, 115, 104, 136, 106, 104, | |
| 1027 | - /* 1000 */ 135, 136, 136, 111, 112, 113, 114, 115, 113, 114, | |
| 1028 | - /* 1010 */ 115, 136, 104, 136, 106, 135, 136, 2, 136, 111, | |
| 1029 | - /* 1020 */ 112, 113, 114, 115, 104, 136, 106, 135, 136, 136, | |
| 1030 | - /* 1030 */ 135, 111, 112, 113, 114, 115, 4, 5, 6, 7, | |
| 1031 | - /* 1040 */ 136, 136, 136, 135, 136, 136, 31, 32, 136, 104, | |
| 1032 | - /* 1050 */ 136, 136, 136, 136, 136, 135, 136, 25, 113, 114, | |
| 1033 | - /* 1060 */ 115, 104, 136, 136, 119, 120, 121, 136, 136, 136, | |
| 1034 | - /* 1070 */ 113, 114, 115, 136, 136, 136, 136, 120, 136, 64, | |
| 1035 | - /* 1080 */ 135, 136, 136, 136, 127, 136, 129, 100, 101, 102, | |
| 1036 | - /* 1090 */ 103, 136, 135, 136, 107, 136, 136, 136, 104, 84, | |
| 1037 | - /* 1100 */ 85, 86, 87, 104, 136, 118, 136, 113, 114, 115, | |
| 1038 | - /* 1110 */ 123, 117, 113, 114, 115, 104, 136, 4, 5, 6, | |
| 1039 | - /* 1120 */ 7, 136, 104, 136, 113, 114, 115, 104, 136, 135, | |
| 1040 | - /* 1130 */ 17, 113, 114, 115, 135, 136, 113, 114, 115, 104, | |
| 1041 | - /* 1140 */ 136, 136, 136, 136, 136, 136, 135, 136, 113, 114, | |
| 1042 | - /* 1150 */ 115, 104, 136, 135, 104, 136, 136, 136, 135, 136, | |
| 1043 | - /* 1160 */ 113, 114, 115, 113, 114, 115, 104, 136, 136, 104, | |
| 1044 | - /* 1170 */ 135, 136, 136, 136, 136, 113, 114, 115, 113, 114, | |
| 1045 | - /* 1180 */ 115, 136, 135, 136, 104, 135, 136, 136, 136, 136, | |
| 1046 | - /* 1190 */ 136, 136, 104, 113, 114, 115, 136, 135, 136, 104, | |
| 1047 | - /* 1200 */ 135, 113, 114, 115, 136, 136, 136, 104, 113, 114, | |
| 1048 | - /* 1210 */ 115, 136, 136, 136, 136, 135, 113, 114, 115, 136, | |
| 1049 | - /* 1220 */ 136, 136, 136, 135, 136, 136, 136, 136, 136, 136, | |
| 1050 | - /* 1230 */ 135, 104, 136, 136, 104, 136, 136, 104, 135, 136, | |
| 1051 | - /* 1240 */ 113, 114, 115, 113, 114, 115, 113, 114, 115, 136, | |
| 1052 | - /* 1250 */ 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, | |
| 1053 | - /* 1260 */ 136, 136, 135, 104, 136, 135, 104, 136, 135, 104, | |
| 1054 | - /* 1270 */ 136, 136, 113, 114, 115, 113, 114, 115, 113, 114, | |
| 1055 | - /* 1280 */ 115, 104, 136, 136, 136, 136, 136, 136, 136, 136, | |
| 1056 | - /* 1290 */ 113, 114, 115, 136, 135, 136, 136, 135, 136, 136, | |
| 1057 | - /* 1300 */ 135, 136, 136, 136, 136, 136, 136, 136, 136, 136, | |
| 1058 | - /* 1310 */ 136, 136, 135, 100, 100, 100, 100, 100, 100, 100, | |
| 1059 | - /* 1320 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, | |
| 1060 | - /* 1330 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, | |
| 1061 | - /* 1340 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, | |
| 1062 | - /* 1350 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, | |
| 1063 | - /* 1360 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, | |
| 1064 | - /* 1370 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, | |
| 1065 | - /* 1380 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, | |
| 1066 | - /* 1390 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, | |
| 1067 | - /* 1400 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, | |
| 1068 | - /* 1410 */ 100, 100, 100, | |
| 984 | + /* 60 */ 60, 61, 62, 63, 64, 0, 4, 5, 6, 7, | |
| 985 | + /* 70 */ 4, 5, 105, 25, 107, 10, 115, 116, 117, 17, | |
| 986 | + /* 80 */ 113, 114, 115, 116, 117, 20, 21, 22, 128, 24, | |
| 987 | + /* 90 */ 101, 102, 103, 104, 29, 115, 116, 117, 109, 34, | |
| 988 | + /* 100 */ 35, 36, 37, 38, 137, 40, 41, 42, 43, 120, | |
| 989 | + /* 110 */ 45, 46, 49, 10, 125, 50, 51, 52, 53, 54, | |
| 990 | + /* 120 */ 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, | |
| 991 | + /* 130 */ 1, 2, 29, 4, 5, 4, 5, 6, 7, 8, | |
| 992 | + /* 140 */ 105, 12, 107, 3, 15, 115, 116, 117, 113, 114, | |
| 993 | + /* 150 */ 115, 116, 117, 24, 91, 130, 27, 28, 118, 105, | |
| 994 | + /* 160 */ 97, 32, 33, 100, 1, 2, 19, 4, 5, 115, | |
| 995 | + /* 170 */ 116, 117, 137, 119, 12, 12, 2, 118, 15, 1, | |
| 996 | + /* 180 */ 126, 127, 106, 20, 21, 22, 110, 111, 106, 2, | |
| 997 | + /* 190 */ 107, 137, 110, 111, 65, 32, 33, 65, 66, 67, | |
| 998 | + /* 200 */ 68, 69, 70, 71, 72, 73, 74, 75, 132, 133, | |
| 999 | + /* 210 */ 134, 131, 83, 39, 85, 86, 87, 88, 135, 90, | |
| 1000 | + /* 220 */ 17, 92, 93, 94, 95, 96, 39, 15, 65, 89, | |
| 1001 | + /* 230 */ 1, 2, 16, 4, 5, 30, 31, 32, 33, 98, | |
| 1002 | + /* 240 */ 99, 12, 6, 7, 15, 83, 83, 41, 85, 86, | |
| 1003 | + /* 250 */ 87, 88, 90, 90, 92, 92, 93, 94, 95, 96, | |
| 1004 | + /* 260 */ 49, 32, 33, 44, 41, 2, 3, 4, 5, 6, | |
| 1005 | + /* 270 */ 7, 27, 28, 42, 20, 21, 22, 27, 28, 16, | |
| 1006 | + /* 280 */ 42, 105, 48, 107, 115, 116, 117, 13, 25, 113, | |
| 1007 | + /* 290 */ 114, 115, 116, 117, 65, 32, 33, 4, 5, 6, | |
| 1008 | + /* 300 */ 7, 17, 39, 25, 50, 51, 52, 53, 54, 55, | |
| 1009 | + /* 310 */ 17, 100, 83, 137, 85, 86, 87, 88, 105, 90, | |
| 1010 | + /* 320 */ 107, 92, 93, 94, 95, 96, 113, 114, 115, 116, | |
| 1011 | + /* 330 */ 117, 68, 69, 76, 2, 3, 4, 5, 6, 7, | |
| 1012 | + /* 340 */ 77, 17, 79, 80, 81, 4, 5, 6, 7, 17, | |
| 1013 | + /* 350 */ 137, 102, 103, 104, 27, 28, 105, 25, 109, 1, | |
| 1014 | + /* 360 */ 81, 80, 4, 5, 32, 33, 115, 116, 117, 120, | |
| 1015 | + /* 370 */ 12, 39, 82, 15, 125, 81, 3, 36, 20, 21, | |
| 1016 | + /* 380 */ 22, 0, 24, 3, 39, 105, 3, 107, 137, 3, | |
| 1017 | + /* 390 */ 3, 10, 3, 113, 114, 115, 116, 117, 97, 78, | |
| 1018 | + /* 400 */ 68, 69, 25, 2, 3, 4, 5, 6, 7, 77, | |
| 1019 | + /* 410 */ 105, 79, 80, 81, 15, 15, 12, 137, 15, 92, | |
| 1020 | + /* 420 */ 115, 116, 117, 17, 119, 29, 25, 4, 5, 6, | |
| 1021 | + /* 430 */ 7, 29, 127, 32, 33, 91, 29, 2, 11, 3, | |
| 1022 | + /* 440 */ 39, 83, 137, 138, 138, 105, 138, 138, 90, 138, | |
| 1023 | + /* 450 */ 92, 93, 94, 95, 96, 115, 116, 117, 138, 36, | |
| 1024 | + /* 460 */ 138, 121, 122, 123, 138, 138, 2, 32, 33, 68, | |
| 1025 | + /* 470 */ 69, 138, 1, 4, 5, 6, 7, 137, 77, 138, | |
| 1026 | + /* 480 */ 79, 80, 81, 12, 49, 14, 17, 138, 138, 18, | |
| 1027 | + /* 490 */ 138, 20, 21, 22, 23, 24, 32, 33, 27, 28, | |
| 1028 | + /* 500 */ 65, 30, 31, 32, 33, 1, 138, 138, 4, 5, | |
| 1029 | + /* 510 */ 4, 5, 6, 7, 138, 138, 12, 138, 138, 15, | |
| 1030 | + /* 520 */ 85, 86, 87, 88, 20, 21, 22, 1, 138, 65, | |
| 1031 | + /* 530 */ 4, 5, 4, 5, 6, 7, 138, 138, 12, 138, | |
| 1032 | + /* 540 */ 138, 15, 138, 1, 2, 17, 4, 5, 138, 85, | |
| 1033 | + /* 550 */ 86, 87, 88, 25, 12, 138, 105, 15, 107, 138, | |
| 1034 | + /* 560 */ 105, 138, 138, 138, 113, 114, 115, 116, 117, 138, | |
| 1035 | + /* 570 */ 115, 116, 117, 47, 48, 1, 2, 122, 4, 5, | |
| 1036 | + /* 580 */ 138, 39, 4, 5, 6, 7, 12, 83, 137, 15, | |
| 1037 | + /* 590 */ 138, 138, 137, 138, 90, 17, 92, 93, 94, 95, | |
| 1038 | + /* 600 */ 96, 1, 2, 25, 4, 5, 138, 138, 138, 83, | |
| 1039 | + /* 610 */ 138, 138, 12, 138, 138, 15, 90, 138, 92, 93, | |
| 1040 | + /* 620 */ 94, 95, 96, 138, 138, 83, 4, 5, 6, 7, | |
| 1041 | + /* 630 */ 138, 138, 90, 138, 92, 93, 94, 95, 96, 1, | |
| 1042 | + /* 640 */ 138, 138, 4, 5, 105, 138, 138, 25, 138, 138, | |
| 1043 | + /* 650 */ 12, 138, 138, 15, 115, 116, 117, 83, 138, 138, | |
| 1044 | + /* 660 */ 138, 122, 123, 138, 90, 138, 92, 93, 94, 95, | |
| 1045 | + /* 670 */ 96, 1, 138, 138, 4, 5, 137, 105, 138, 138, | |
| 1046 | + /* 680 */ 138, 138, 12, 83, 138, 15, 138, 115, 116, 117, | |
| 1047 | + /* 690 */ 90, 119, 92, 93, 94, 95, 96, 1, 138, 138, | |
| 1048 | + /* 700 */ 4, 5, 4, 5, 6, 7, 138, 138, 12, 137, | |
| 1049 | + /* 710 */ 138, 15, 138, 1, 138, 17, 4, 5, 138, 138, | |
| 1050 | + /* 720 */ 138, 83, 138, 138, 12, 105, 138, 15, 90, 138, | |
| 1051 | + /* 730 */ 92, 93, 94, 95, 96, 115, 116, 117, 105, 138, | |
| 1052 | + /* 740 */ 107, 138, 122, 123, 105, 138, 113, 114, 115, 116, | |
| 1053 | + /* 750 */ 117, 138, 138, 83, 115, 116, 117, 137, 119, 138, | |
| 1054 | + /* 760 */ 90, 105, 92, 93, 94, 95, 96, 138, 138, 138, | |
| 1055 | + /* 770 */ 137, 115, 116, 117, 138, 138, 137, 138, 138, 83, | |
| 1056 | + /* 780 */ 138, 4, 5, 6, 7, 138, 90, 138, 92, 93, | |
| 1057 | + /* 790 */ 94, 95, 96, 137, 17, 83, 4, 5, 6, 7, | |
| 1058 | + /* 800 */ 105, 138, 90, 138, 92, 93, 94, 95, 96, 138, | |
| 1059 | + /* 810 */ 115, 116, 117, 138, 105, 138, 107, 25, 138, 105, | |
| 1060 | + /* 820 */ 138, 107, 113, 114, 115, 116, 117, 113, 114, 115, | |
| 1061 | + /* 830 */ 116, 117, 137, 105, 138, 107, 138, 105, 138, 138, | |
| 1062 | + /* 840 */ 138, 113, 114, 115, 116, 117, 137, 115, 116, 117, | |
| 1063 | + /* 850 */ 138, 137, 105, 138, 107, 138, 138, 138, 27, 28, | |
| 1064 | + /* 860 */ 113, 114, 115, 116, 117, 137, 105, 138, 107, 137, | |
| 1065 | + /* 870 */ 138, 105, 138, 107, 113, 114, 115, 116, 117, 113, | |
| 1066 | + /* 880 */ 114, 115, 116, 117, 137, 105, 138, 107, 4, 5, | |
| 1067 | + /* 890 */ 6, 7, 138, 113, 114, 115, 116, 117, 137, 138, | |
| 1068 | + /* 900 */ 138, 17, 138, 137, 138, 138, 138, 138, 138, 138, | |
| 1069 | + /* 910 */ 105, 138, 107, 138, 138, 84, 138, 137, 113, 114, | |
| 1070 | + /* 920 */ 115, 116, 117, 92, 138, 105, 138, 107, 138, 138, | |
| 1071 | + /* 930 */ 138, 138, 138, 113, 114, 115, 116, 117, 138, 105, | |
| 1072 | + /* 940 */ 138, 107, 137, 138, 138, 138, 138, 113, 114, 115, | |
| 1073 | + /* 950 */ 116, 117, 105, 138, 107, 138, 138, 137, 138, 138, | |
| 1074 | + /* 960 */ 113, 114, 115, 116, 117, 138, 105, 138, 107, 138, | |
| 1075 | + /* 970 */ 138, 137, 138, 138, 113, 114, 115, 116, 117, 105, | |
| 1076 | + /* 980 */ 138, 107, 138, 138, 137, 138, 138, 113, 114, 115, | |
| 1077 | + /* 990 */ 116, 117, 138, 105, 138, 107, 138, 138, 137, 138, | |
| 1078 | + /* 1000 */ 138, 113, 114, 115, 116, 117, 138, 138, 138, 138, | |
| 1079 | + /* 1010 */ 138, 137, 138, 138, 138, 138, 138, 138, 105, 138, | |
| 1080 | + /* 1020 */ 107, 138, 138, 138, 138, 137, 113, 114, 115, 116, | |
| 1081 | + /* 1030 */ 117, 105, 138, 107, 138, 138, 138, 138, 138, 113, | |
| 1082 | + /* 1040 */ 114, 115, 116, 117, 138, 105, 138, 107, 138, 138, | |
| 1083 | + /* 1050 */ 137, 138, 138, 113, 114, 115, 116, 117, 105, 138, | |
| 1084 | + /* 1060 */ 107, 138, 138, 137, 138, 138, 113, 114, 115, 116, | |
| 1085 | + /* 1070 */ 117, 138, 105, 138, 138, 138, 138, 137, 138, 138, | |
| 1086 | + /* 1080 */ 138, 105, 115, 116, 117, 138, 138, 138, 138, 122, | |
| 1087 | + /* 1090 */ 137, 115, 116, 117, 138, 105, 129, 138, 131, 105, | |
| 1088 | + /* 1100 */ 138, 138, 138, 138, 137, 115, 116, 117, 105, 115, | |
| 1089 | + /* 1110 */ 116, 117, 138, 137, 138, 138, 138, 138, 115, 116, | |
| 1090 | + /* 1120 */ 117, 138, 138, 138, 105, 138, 138, 137, 138, 138, | |
| 1091 | + /* 1130 */ 138, 137, 105, 138, 115, 116, 117, 105, 138, 138, | |
| 1092 | + /* 1140 */ 137, 138, 115, 116, 117, 138, 138, 115, 116, 117, | |
| 1093 | + /* 1150 */ 138, 105, 138, 138, 138, 138, 137, 138, 138, 138, | |
| 1094 | + /* 1160 */ 105, 115, 116, 117, 137, 138, 105, 138, 138, 137, | |
| 1095 | + /* 1170 */ 115, 116, 117, 138, 105, 138, 115, 116, 117, 138, | |
| 1096 | + /* 1180 */ 138, 138, 138, 137, 115, 116, 117, 138, 138, 105, | |
| 1097 | + /* 1190 */ 138, 138, 137, 138, 138, 138, 138, 105, 137, 115, | |
| 1098 | + /* 1200 */ 116, 117, 105, 138, 138, 105, 137, 115, 116, 117, | |
| 1099 | + /* 1210 */ 138, 138, 115, 116, 117, 115, 116, 117, 138, 138, | |
| 1100 | + /* 1220 */ 138, 137, 138, 138, 138, 138, 138, 138, 138, 137, | |
| 1101 | + /* 1230 */ 105, 138, 138, 105, 137, 138, 138, 137, 105, 138, | |
| 1102 | + /* 1240 */ 115, 116, 117, 115, 116, 117, 105, 138, 115, 116, | |
| 1103 | + /* 1250 */ 117, 138, 138, 138, 138, 138, 115, 116, 117, 138, | |
| 1104 | + /* 1260 */ 138, 138, 137, 138, 138, 137, 138, 138, 138, 105, | |
| 1105 | + /* 1270 */ 137, 138, 105, 138, 138, 138, 138, 138, 137, 115, | |
| 1106 | + /* 1280 */ 116, 117, 115, 116, 117, 138, 138, 138, 138, 138, | |
| 1107 | + /* 1290 */ 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, | |
| 1108 | + /* 1300 */ 138, 137, 138, 138, 137, 101, 101, 101, 101, 101, | |
| 1109 | + /* 1310 */ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, | |
| 1110 | + /* 1320 */ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, | |
| 1111 | + /* 1330 */ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, | |
| 1112 | + /* 1340 */ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, | |
| 1113 | + /* 1350 */ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, | |
| 1114 | + /* 1360 */ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, | |
| 1115 | + /* 1370 */ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, | |
| 1116 | + /* 1380 */ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, | |
| 1117 | + /* 1390 */ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, | |
| 1118 | + /* 1400 */ 101, 101, 101, 101, 101, 101, | |
| 1069 | 1119 | }; |
| 1070 | 1120 | #define YY_SHIFT_COUNT (163) |
| 1071 | 1121 | #define YY_SHIFT_MIN (0) |
| 1072 | -#define YY_SHIFT_MAX (1113) | |
| 1122 | +#define YY_SHIFT_MAX (884) | |
| 1073 | 1123 | static const unsigned short int yy_shift_ofst[] = { |
| 1074 | - /* 0 */ 143, 127, 222, 287, 287, 287, 287, 287, 287, 287, | |
| 1075 | - /* 10 */ 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, | |
| 1076 | - /* 20 */ 287, 287, 287, 287, 287, 287, 287, 244, 433, 266, | |
| 1077 | - /* 30 */ 244, 143, 494, 494, 0, 64, 143, 589, 266, 589, | |
| 1078 | - /* 40 */ 466, 466, 466, 529, 562, 266, 266, 266, 266, 266, | |
| 1079 | - /* 50 */ 266, 624, 266, 266, 657, 266, 266, 266, 266, 266, | |
| 1080 | - /* 60 */ 266, 266, 266, 266, 266, 179, 317, 317, 317, 317, | |
| 1081 | - /* 70 */ 317, 645, 318, 386, 425, 1015, 1015, 118, 47, 1313, | |
| 1082 | - /* 80 */ 1313, 1313, 1313, 114, 114, 200, 435, 129, 188, 234, | |
| 1083 | - /* 90 */ 360, 468, 531, 506, 545, 823, 1032, 928, 1113, 25, | |
| 1084 | - /* 100 */ 25, 25, 162, 25, 25, 25, 69, 25, 85, 128, | |
| 1085 | - /* 110 */ 92, 105, 120, 136, 100, 183, 183, 176, 220, 174, | |
| 1086 | - /* 120 */ 202, 275, 152, 207, 198, 219, 221, 208, 215, 217, | |
| 1087 | - /* 130 */ 236, 238, 213, 267, 265, 262, 218, 273, 216, 224, | |
| 1088 | - /* 140 */ 214, 225, 294, 295, 297, 272, 309, 324, 325, 249, | |
| 1089 | - /* 150 */ 253, 307, 249, 331, 332, 338, 337, 335, 340, 342, | |
| 1090 | - /* 160 */ 363, 293, 374, 373, | |
| 1124 | + /* 0 */ 471, 129, 163, 229, 229, 229, 229, 229, 229, 229, | |
| 1125 | + /* 10 */ 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, | |
| 1126 | + /* 20 */ 229, 229, 229, 229, 229, 229, 229, 358, 526, 638, | |
| 1127 | + /* 30 */ 358, 471, 542, 542, 0, 65, 471, 670, 638, 670, | |
| 1128 | + /* 40 */ 504, 504, 504, 574, 600, 638, 638, 638, 638, 638, | |
| 1129 | + /* 50 */ 638, 696, 638, 638, 712, 638, 638, 638, 638, 638, | |
| 1130 | + /* 60 */ 638, 638, 638, 638, 638, 254, 162, 162, 162, 162, | |
| 1131 | + /* 70 */ 162, 435, 263, 332, 401, 464, 464, 205, 48, 1305, | |
| 1132 | + /* 80 */ 1305, 1305, 1305, 132, 132, 528, 578, 62, 131, 341, | |
| 1133 | + /* 90 */ 423, 293, 7, 469, 622, 698, 792, 777, 884, 506, | |
| 1134 | + /* 100 */ 506, 506, 63, 506, 506, 506, 831, 506, 327, 103, | |
| 1135 | + /* 110 */ 174, 187, 6, 66, 141, 236, 236, 244, 250, 140, | |
| 1136 | + /* 120 */ 211, 381, 147, 178, 203, 216, 212, 219, 206, 223, | |
| 1137 | + /* 130 */ 231, 238, 234, 274, 284, 278, 257, 324, 279, 281, | |
| 1138 | + /* 140 */ 290, 294, 373, 380, 383, 345, 386, 387, 389, 301, | |
| 1139 | + /* 150 */ 321, 377, 301, 399, 400, 403, 406, 396, 402, 407, | |
| 1140 | + /* 160 */ 404, 344, 436, 427, | |
| 1091 | 1141 | }; |
| 1092 | 1142 | #define YY_REDUCE_COUNT (82) |
| 1093 | -#define YY_REDUCE_MIN (-130) | |
| 1094 | -#define YY_REDUCE_MAX (1177) | |
| 1143 | +#define YY_REDUCE_MIN (-132) | |
| 1144 | +#define YY_REDUCE_MAX (1167) | |
| 1095 | 1145 | static const short yy_reduce_ofst[] = { |
| 1096 | - /* 0 */ 640, -97, -33, 308, 467, 502, 586, 622, 655, 670, | |
| 1097 | - /* 10 */ 682, 698, 710, 735, 747, 760, 775, 787, 803, 815, | |
| 1098 | - /* 20 */ 840, 852, 865, 880, 892, 908, 920, 159, 945, 957, | |
| 1099 | - /* 30 */ 227, 987, 528, 590, -62, -62, 584, 404, 722, 994, | |
| 1100 | - /* 40 */ 560, 685, 790, 827, 895, 999, 1011, 1018, 1023, 1035, | |
| 1101 | - /* 50 */ 1047, 1050, 1062, 1065, 1080, 1088, 1095, 1103, 1127, 1130, | |
| 1102 | - /* 60 */ 1133, 1159, 1162, 1165, 1177, -82, -112, -102, -48, -38, | |
| 1103 | - /* 70 */ -24, -23, -130, -130, -130, -98, -87, -59, -101, -41, | |
| 1104 | - /* 80 */ 46, 52, -36, | |
| 1146 | + /* 0 */ -76, -98, -33, 35, 176, 213, 280, 451, 633, 709, | |
| 1147 | + /* 10 */ 714, 728, 747, 761, 766, 780, 805, 820, 834, 847, | |
| 1148 | + /* 20 */ 861, 874, 888, 913, 926, 940, 953, 54, 340, 967, | |
| 1149 | + /* 30 */ 305, -11, 539, 620, 76, 76, 249, 639, 455, 572, | |
| 1150 | + /* 40 */ 251, 656, 695, 732, 976, 990, 994, 1003, 1019, 1027, | |
| 1151 | + /* 50 */ 1032, 1046, 1055, 1061, 1069, 1084, 1092, 1097, 1100, 1125, | |
| 1152 | + /* 60 */ 1128, 1133, 1141, 1164, 1167, 82, -114, -39, -20, 30, | |
| 1153 | + /* 70 */ 169, 83, -132, -132, -132, -99, -77, -62, -40, 25, | |
| 1154 | + /* 80 */ 40, 59, 80, | |
| 1105 | 1155 | }; |
| 1106 | 1156 | static const YYACTIONTYPE yy_default[] = { |
| 1107 | 1157 | /* 0 */ 449, 443, 443, 443, 443, 443, 443, 443, 443, 443, |
| 1108 | 1158 | /* 10 */ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, |
| 1109 | 1159 | /* 20 */ 443, 443, 443, 443, 443, 443, 443, 443, 473, 576, |
| @@ -1164,10 +1214,11 @@ | ||
| 1164 | 1214 | 0, /* COLOR => nothing */ |
| 1165 | 1215 | 0, /* THICKNESS => nothing */ |
| 1166 | 1216 | 0, /* PRINT => nothing */ |
| 1167 | 1217 | 0, /* STRING => nothing */ |
| 1168 | 1218 | 0, /* COMMA => nothing */ |
| 1219 | + 0, /* ISODATE => nothing */ | |
| 1169 | 1220 | 0, /* CLASSNAME => nothing */ |
| 1170 | 1221 | 0, /* LB => nothing */ |
| 1171 | 1222 | 0, /* RB => nothing */ |
| 1172 | 1223 | 0, /* UP => nothing */ |
| 1173 | 1224 | 0, /* DOWN => nothing */ |
| @@ -1347,120 +1398,122 @@ | ||
| 1347 | 1398 | /* 21 */ "COLOR", |
| 1348 | 1399 | /* 22 */ "THICKNESS", |
| 1349 | 1400 | /* 23 */ "PRINT", |
| 1350 | 1401 | /* 24 */ "STRING", |
| 1351 | 1402 | /* 25 */ "COMMA", |
| 1352 | - /* 26 */ "CLASSNAME", | |
| 1353 | - /* 27 */ "LB", | |
| 1354 | - /* 28 */ "RB", | |
| 1355 | - /* 29 */ "UP", | |
| 1356 | - /* 30 */ "DOWN", | |
| 1357 | - /* 31 */ "LEFT", | |
| 1358 | - /* 32 */ "RIGHT", | |
| 1359 | - /* 33 */ "CLOSE", | |
| 1360 | - /* 34 */ "CHOP", | |
| 1361 | - /* 35 */ "FROM", | |
| 1362 | - /* 36 */ "TO", | |
| 1363 | - /* 37 */ "THEN", | |
| 1364 | - /* 38 */ "HEADING", | |
| 1365 | - /* 39 */ "GO", | |
| 1366 | - /* 40 */ "AT", | |
| 1367 | - /* 41 */ "WITH", | |
| 1368 | - /* 42 */ "SAME", | |
| 1369 | - /* 43 */ "AS", | |
| 1370 | - /* 44 */ "FIT", | |
| 1371 | - /* 45 */ "BEHIND", | |
| 1372 | - /* 46 */ "UNTIL", | |
| 1373 | - /* 47 */ "EVEN", | |
| 1374 | - /* 48 */ "DOT_E", | |
| 1375 | - /* 49 */ "HEIGHT", | |
| 1376 | - /* 50 */ "WIDTH", | |
| 1377 | - /* 51 */ "RADIUS", | |
| 1378 | - /* 52 */ "DIAMETER", | |
| 1379 | - /* 53 */ "DOTTED", | |
| 1380 | - /* 54 */ "DASHED", | |
| 1381 | - /* 55 */ "CW", | |
| 1382 | - /* 56 */ "CCW", | |
| 1383 | - /* 57 */ "LARROW", | |
| 1384 | - /* 58 */ "RARROW", | |
| 1385 | - /* 59 */ "LRARROW", | |
| 1386 | - /* 60 */ "INVIS", | |
| 1387 | - /* 61 */ "THICK", | |
| 1388 | - /* 62 */ "THIN", | |
| 1389 | - /* 63 */ "SOLID", | |
| 1390 | - /* 64 */ "CENTER", | |
| 1391 | - /* 65 */ "LJUST", | |
| 1392 | - /* 66 */ "RJUST", | |
| 1393 | - /* 67 */ "ABOVE", | |
| 1394 | - /* 68 */ "BELOW", | |
| 1395 | - /* 69 */ "ITALIC", | |
| 1396 | - /* 70 */ "BOLD", | |
| 1397 | - /* 71 */ "MONO", | |
| 1398 | - /* 72 */ "ALIGNED", | |
| 1399 | - /* 73 */ "BIG", | |
| 1400 | - /* 74 */ "SMALL", | |
| 1401 | - /* 75 */ "AND", | |
| 1402 | - /* 76 */ "LT", | |
| 1403 | - /* 77 */ "GT", | |
| 1404 | - /* 78 */ "ON", | |
| 1405 | - /* 79 */ "WAY", | |
| 1406 | - /* 80 */ "BETWEEN", | |
| 1407 | - /* 81 */ "THE", | |
| 1408 | - /* 82 */ "NTH", | |
| 1409 | - /* 83 */ "VERTEX", | |
| 1410 | - /* 84 */ "TOP", | |
| 1411 | - /* 85 */ "BOTTOM", | |
| 1412 | - /* 86 */ "START", | |
| 1413 | - /* 87 */ "END", | |
| 1414 | - /* 88 */ "IN", | |
| 1415 | - /* 89 */ "THIS", | |
| 1416 | - /* 90 */ "DOT_U", | |
| 1417 | - /* 91 */ "LAST", | |
| 1418 | - /* 92 */ "NUMBER", | |
| 1419 | - /* 93 */ "FUNC1", | |
| 1420 | - /* 94 */ "FUNC2", | |
| 1421 | - /* 95 */ "DIST", | |
| 1422 | - /* 96 */ "DOT_XY", | |
| 1423 | - /* 97 */ "X", | |
| 1424 | - /* 98 */ "Y", | |
| 1425 | - /* 99 */ "DOT_L", | |
| 1426 | - /* 100 */ "statement_list", | |
| 1427 | - /* 101 */ "statement", | |
| 1428 | - /* 102 */ "unnamed_statement", | |
| 1429 | - /* 103 */ "basetype", | |
| 1430 | - /* 104 */ "expr", | |
| 1431 | - /* 105 */ "numproperty", | |
| 1432 | - /* 106 */ "edge", | |
| 1433 | - /* 107 */ "direction", | |
| 1434 | - /* 108 */ "dashproperty", | |
| 1435 | - /* 109 */ "colorproperty", | |
| 1436 | - /* 110 */ "locproperty", | |
| 1437 | - /* 111 */ "position", | |
| 1438 | - /* 112 */ "place", | |
| 1439 | - /* 113 */ "object", | |
| 1440 | - /* 114 */ "objectname", | |
| 1441 | - /* 115 */ "nth", | |
| 1442 | - /* 116 */ "textposition", | |
| 1443 | - /* 117 */ "rvalue", | |
| 1444 | - /* 118 */ "lvalue", | |
| 1445 | - /* 119 */ "even", | |
| 1446 | - /* 120 */ "relexpr", | |
| 1447 | - /* 121 */ "optrelexpr", | |
| 1448 | - /* 122 */ "document", | |
| 1449 | - /* 123 */ "print", | |
| 1450 | - /* 124 */ "prlist", | |
| 1451 | - /* 125 */ "pritem", | |
| 1452 | - /* 126 */ "prsep", | |
| 1453 | - /* 127 */ "attribute_list", | |
| 1454 | - /* 128 */ "savelist", | |
| 1455 | - /* 129 */ "alist", | |
| 1456 | - /* 130 */ "attribute", | |
| 1457 | - /* 131 */ "go", | |
| 1458 | - /* 132 */ "boolproperty", | |
| 1459 | - /* 133 */ "withclause", | |
| 1460 | - /* 134 */ "between", | |
| 1461 | - /* 135 */ "place2", | |
| 1403 | + /* 26 */ "ISODATE", | |
| 1404 | + /* 27 */ "CLASSNAME", | |
| 1405 | + /* 28 */ "LB", | |
| 1406 | + /* 29 */ "RB", | |
| 1407 | + /* 30 */ "UP", | |
| 1408 | + /* 31 */ "DOWN", | |
| 1409 | + /* 32 */ "LEFT", | |
| 1410 | + /* 33 */ "RIGHT", | |
| 1411 | + /* 34 */ "CLOSE", | |
| 1412 | + /* 35 */ "CHOP", | |
| 1413 | + /* 36 */ "FROM", | |
| 1414 | + /* 37 */ "TO", | |
| 1415 | + /* 38 */ "THEN", | |
| 1416 | + /* 39 */ "HEADING", | |
| 1417 | + /* 40 */ "GO", | |
| 1418 | + /* 41 */ "AT", | |
| 1419 | + /* 42 */ "WITH", | |
| 1420 | + /* 43 */ "SAME", | |
| 1421 | + /* 44 */ "AS", | |
| 1422 | + /* 45 */ "FIT", | |
| 1423 | + /* 46 */ "BEHIND", | |
| 1424 | + /* 47 */ "UNTIL", | |
| 1425 | + /* 48 */ "EVEN", | |
| 1426 | + /* 49 */ "DOT_E", | |
| 1427 | + /* 50 */ "HEIGHT", | |
| 1428 | + /* 51 */ "WIDTH", | |
| 1429 | + /* 52 */ "RADIUS", | |
| 1430 | + /* 53 */ "DIAMETER", | |
| 1431 | + /* 54 */ "DOTTED", | |
| 1432 | + /* 55 */ "DASHED", | |
| 1433 | + /* 56 */ "CW", | |
| 1434 | + /* 57 */ "CCW", | |
| 1435 | + /* 58 */ "LARROW", | |
| 1436 | + /* 59 */ "RARROW", | |
| 1437 | + /* 60 */ "LRARROW", | |
| 1438 | + /* 61 */ "INVIS", | |
| 1439 | + /* 62 */ "THICK", | |
| 1440 | + /* 63 */ "THIN", | |
| 1441 | + /* 64 */ "SOLID", | |
| 1442 | + /* 65 */ "CENTER", | |
| 1443 | + /* 66 */ "LJUST", | |
| 1444 | + /* 67 */ "RJUST", | |
| 1445 | + /* 68 */ "ABOVE", | |
| 1446 | + /* 69 */ "BELOW", | |
| 1447 | + /* 70 */ "ITALIC", | |
| 1448 | + /* 71 */ "BOLD", | |
| 1449 | + /* 72 */ "MONO", | |
| 1450 | + /* 73 */ "ALIGNED", | |
| 1451 | + /* 74 */ "BIG", | |
| 1452 | + /* 75 */ "SMALL", | |
| 1453 | + /* 76 */ "AND", | |
| 1454 | + /* 77 */ "LT", | |
| 1455 | + /* 78 */ "GT", | |
| 1456 | + /* 79 */ "ON", | |
| 1457 | + /* 80 */ "WAY", | |
| 1458 | + /* 81 */ "BETWEEN", | |
| 1459 | + /* 82 */ "THE", | |
| 1460 | + /* 83 */ "NTH", | |
| 1461 | + /* 84 */ "VERTEX", | |
| 1462 | + /* 85 */ "TOP", | |
| 1463 | + /* 86 */ "BOTTOM", | |
| 1464 | + /* 87 */ "START", | |
| 1465 | + /* 88 */ "END", | |
| 1466 | + /* 89 */ "IN", | |
| 1467 | + /* 90 */ "THIS", | |
| 1468 | + /* 91 */ "DOT_U", | |
| 1469 | + /* 92 */ "LAST", | |
| 1470 | + /* 93 */ "NUMBER", | |
| 1471 | + /* 94 */ "FUNC1", | |
| 1472 | + /* 95 */ "FUNC2", | |
| 1473 | + /* 96 */ "DIST", | |
| 1474 | + /* 97 */ "DOT_XY", | |
| 1475 | + /* 98 */ "X", | |
| 1476 | + /* 99 */ "Y", | |
| 1477 | + /* 100 */ "DOT_L", | |
| 1478 | + /* 101 */ "statement_list", | |
| 1479 | + /* 102 */ "statement", | |
| 1480 | + /* 103 */ "unnamed_statement", | |
| 1481 | + /* 104 */ "basetype", | |
| 1482 | + /* 105 */ "expr", | |
| 1483 | + /* 106 */ "numproperty", | |
| 1484 | + /* 107 */ "edge", | |
| 1485 | + /* 108 */ "isodate", | |
| 1486 | + /* 109 */ "direction", | |
| 1487 | + /* 110 */ "dashproperty", | |
| 1488 | + /* 111 */ "colorproperty", | |
| 1489 | + /* 112 */ "locproperty", | |
| 1490 | + /* 113 */ "position", | |
| 1491 | + /* 114 */ "place", | |
| 1492 | + /* 115 */ "object", | |
| 1493 | + /* 116 */ "objectname", | |
| 1494 | + /* 117 */ "nth", | |
| 1495 | + /* 118 */ "textposition", | |
| 1496 | + /* 119 */ "rvalue", | |
| 1497 | + /* 120 */ "lvalue", | |
| 1498 | + /* 121 */ "even", | |
| 1499 | + /* 122 */ "relexpr", | |
| 1500 | + /* 123 */ "optrelexpr", | |
| 1501 | + /* 124 */ "document", | |
| 1502 | + /* 125 */ "print", | |
| 1503 | + /* 126 */ "prlist", | |
| 1504 | + /* 127 */ "pritem", | |
| 1505 | + /* 128 */ "prsep", | |
| 1506 | + /* 129 */ "attribute_list", | |
| 1507 | + /* 130 */ "savelist", | |
| 1508 | + /* 131 */ "alist", | |
| 1509 | + /* 132 */ "attribute", | |
| 1510 | + /* 133 */ "go", | |
| 1511 | + /* 134 */ "boolproperty", | |
| 1512 | + /* 135 */ "withclause", | |
| 1513 | + /* 136 */ "between", | |
| 1514 | + /* 137 */ "place2", | |
| 1462 | 1515 | }; |
| 1463 | 1516 | #endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */ |
| 1464 | 1517 | |
| 1465 | 1518 | #ifndef NDEBUG |
| 1466 | 1519 | /* For tracing reduce actions, the names of all rules are required. |
| @@ -1743,24 +1796,24 @@ | ||
| 1743 | 1796 | ** Note: during a reduce, the only symbols destroyed are those |
| 1744 | 1797 | ** which appear on the RHS of the rule, but which are *not* used |
| 1745 | 1798 | ** inside the C code. |
| 1746 | 1799 | */ |
| 1747 | 1800 | /********* Begin destructor definitions ***************************************/ |
| 1748 | - case 100: /* statement_list */ | |
| 1749 | -{ | |
| 1750 | -#line 511 "pikchr.y" | |
| 1751 | -pik_elist_free(p,(yypminor->yy235)); | |
| 1752 | -#line 1777 "pikchr.c" | |
| 1753 | -} | |
| 1754 | - break; | |
| 1755 | - case 101: /* statement */ | |
| 1756 | - case 102: /* unnamed_statement */ | |
| 1757 | - case 103: /* basetype */ | |
| 1758 | -{ | |
| 1759 | -#line 513 "pikchr.y" | |
| 1760 | -pik_elem_free(p,(yypminor->yy162)); | |
| 1761 | -#line 1786 "pikchr.c" | |
| 1801 | + case 101: /* statement_list */ | |
| 1802 | +{ | |
| 1803 | +#line 524 "pikchr.y" | |
| 1804 | +pik_elist_free(p,(yypminor->yy23)); | |
| 1805 | +#line 1805 "pikchr.c" | |
| 1806 | +} | |
| 1807 | + break; | |
| 1808 | + case 102: /* statement */ | |
| 1809 | + case 103: /* unnamed_statement */ | |
| 1810 | + case 104: /* basetype */ | |
| 1811 | +{ | |
| 1812 | +#line 526 "pikchr.y" | |
| 1813 | +pik_elem_free(p,(yypminor->yy54)); | |
| 1814 | +#line 1814 "pikchr.c" | |
| 1762 | 1815 | } |
| 1763 | 1816 | break; |
| 1764 | 1817 | /********* End destructor definitions *****************************************/ |
| 1765 | 1818 | default: break; /* If no destructor action specified: do nothing */ |
| 1766 | 1819 | } |
| @@ -1991,14 +2044,14 @@ | ||
| 1991 | 2044 | #endif |
| 1992 | 2045 | while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser); |
| 1993 | 2046 | /* Here code is inserted which will execute if the parser |
| 1994 | 2047 | ** stack every overflows */ |
| 1995 | 2048 | /******** Begin %stack_overflow code ******************************************/ |
| 1996 | -#line 545 "pikchr.y" | |
| 2049 | +#line 559 "pikchr.y" | |
| 1997 | 2050 | |
| 1998 | 2051 | pik_error(p, 0, "parser stack overflow"); |
| 1999 | -#line 2024 "pikchr.c" | |
| 2052 | +#line 2052 "pikchr.c" | |
| 2000 | 2053 | /******** End %stack_overflow code ********************************************/ |
| 2001 | 2054 | pik_parserARG_STORE /* Suppress warning about unused %extra_argument var */ |
| 2002 | 2055 | pik_parserCTX_STORE |
| 2003 | 2056 | } |
| 2004 | 2057 | |
| @@ -2060,166 +2113,166 @@ | ||
| 2060 | 2113 | } |
| 2061 | 2114 | |
| 2062 | 2115 | /* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side |
| 2063 | 2116 | ** of that rule */ |
| 2064 | 2117 | static const YYCODETYPE yyRuleInfoLhs[] = { |
| 2065 | - 122, /* (0) document ::= statement_list */ | |
| 2066 | - 100, /* (1) statement_list ::= statement */ | |
| 2067 | - 100, /* (2) statement_list ::= statement_list EOL statement */ | |
| 2068 | - 101, /* (3) statement ::= */ | |
| 2069 | - 101, /* (4) statement ::= direction */ | |
| 2070 | - 101, /* (5) statement ::= lvalue ASSIGN rvalue */ | |
| 2071 | - 101, /* (6) statement ::= PLACENAME COLON unnamed_statement */ | |
| 2072 | - 101, /* (7) statement ::= PLACENAME COLON position */ | |
| 2073 | - 101, /* (8) statement ::= unnamed_statement */ | |
| 2074 | - 101, /* (9) statement ::= print prlist */ | |
| 2075 | - 101, /* (10) statement ::= ASSERT LP expr EQ expr RP */ | |
| 2076 | - 101, /* (11) statement ::= ASSERT LP position EQ position RP */ | |
| 2077 | - 101, /* (12) statement ::= DEFINE ID CODEBLOCK */ | |
| 2078 | - 117, /* (13) rvalue ::= PLACENAME */ | |
| 2079 | - 125, /* (14) pritem ::= FILL */ | |
| 2080 | - 125, /* (15) pritem ::= COLOR */ | |
| 2081 | - 125, /* (16) pritem ::= THICKNESS */ | |
| 2082 | - 125, /* (17) pritem ::= rvalue */ | |
| 2083 | - 125, /* (18) pritem ::= STRING */ | |
| 2084 | - 126, /* (19) prsep ::= COMMA */ | |
| 2085 | - 102, /* (20) unnamed_statement ::= basetype attribute_list */ | |
| 2086 | - 103, /* (21) basetype ::= CLASSNAME */ | |
| 2087 | - 103, /* (22) basetype ::= STRING textposition */ | |
| 2088 | - 103, /* (23) basetype ::= LB savelist statement_list RB */ | |
| 2089 | - 128, /* (24) savelist ::= */ | |
| 2090 | - 120, /* (25) relexpr ::= expr */ | |
| 2091 | - 120, /* (26) relexpr ::= expr PERCENT */ | |
| 2092 | - 121, /* (27) optrelexpr ::= */ | |
| 2093 | - 127, /* (28) attribute_list ::= relexpr alist */ | |
| 2094 | - 130, /* (29) attribute ::= numproperty relexpr */ | |
| 2095 | - 130, /* (30) attribute ::= dashproperty expr */ | |
| 2096 | - 130, /* (31) attribute ::= dashproperty */ | |
| 2097 | - 130, /* (32) attribute ::= colorproperty rvalue */ | |
| 2098 | - 130, /* (33) attribute ::= go direction optrelexpr */ | |
| 2099 | - 130, /* (34) attribute ::= go direction even position */ | |
| 2100 | - 130, /* (35) attribute ::= CLOSE */ | |
| 2101 | - 130, /* (36) attribute ::= CHOP */ | |
| 2102 | - 130, /* (37) attribute ::= FROM position */ | |
| 2103 | - 130, /* (38) attribute ::= TO position */ | |
| 2104 | - 130, /* (39) attribute ::= THEN */ | |
| 2105 | - 130, /* (40) attribute ::= THEN optrelexpr HEADING expr */ | |
| 2106 | - 130, /* (41) attribute ::= THEN optrelexpr EDGEPT */ | |
| 2107 | - 130, /* (42) attribute ::= GO optrelexpr HEADING expr */ | |
| 2108 | - 130, /* (43) attribute ::= GO optrelexpr EDGEPT */ | |
| 2109 | - 130, /* (44) attribute ::= AT position */ | |
| 2110 | - 130, /* (45) attribute ::= SAME */ | |
| 2111 | - 130, /* (46) attribute ::= SAME AS object */ | |
| 2112 | - 130, /* (47) attribute ::= STRING textposition */ | |
| 2113 | - 130, /* (48) attribute ::= FIT */ | |
| 2114 | - 130, /* (49) attribute ::= BEHIND object */ | |
| 2115 | - 133, /* (50) withclause ::= DOT_E edge AT position */ | |
| 2116 | - 133, /* (51) withclause ::= edge AT position */ | |
| 2117 | - 105, /* (52) numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */ | |
| 2118 | - 132, /* (53) boolproperty ::= CW */ | |
| 2119 | - 132, /* (54) boolproperty ::= CCW */ | |
| 2120 | - 132, /* (55) boolproperty ::= LARROW */ | |
| 2121 | - 132, /* (56) boolproperty ::= RARROW */ | |
| 2122 | - 132, /* (57) boolproperty ::= LRARROW */ | |
| 2123 | - 132, /* (58) boolproperty ::= INVIS */ | |
| 2124 | - 132, /* (59) boolproperty ::= THICK */ | |
| 2125 | - 132, /* (60) boolproperty ::= THIN */ | |
| 2126 | - 132, /* (61) boolproperty ::= SOLID */ | |
| 2127 | - 116, /* (62) textposition ::= */ | |
| 2128 | - 116, /* (63) textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL */ | |
| 2129 | - 111, /* (64) position ::= expr COMMA expr */ | |
| 2130 | - 111, /* (65) position ::= place PLUS expr COMMA expr */ | |
| 2131 | - 111, /* (66) position ::= place MINUS expr COMMA expr */ | |
| 2132 | - 111, /* (67) position ::= place PLUS LP expr COMMA expr RP */ | |
| 2133 | - 111, /* (68) position ::= place MINUS LP expr COMMA expr RP */ | |
| 2134 | - 111, /* (69) position ::= LP position COMMA position RP */ | |
| 2135 | - 111, /* (70) position ::= LP position RP */ | |
| 2136 | - 111, /* (71) position ::= expr between position AND position */ | |
| 2137 | - 111, /* (72) position ::= expr LT position COMMA position GT */ | |
| 2138 | - 111, /* (73) position ::= expr ABOVE position */ | |
| 2139 | - 111, /* (74) position ::= expr BELOW position */ | |
| 2140 | - 111, /* (75) position ::= expr LEFT OF position */ | |
| 2141 | - 111, /* (76) position ::= expr RIGHT OF position */ | |
| 2142 | - 111, /* (77) position ::= expr ON HEADING EDGEPT OF position */ | |
| 2143 | - 111, /* (78) position ::= expr HEADING EDGEPT OF position */ | |
| 2144 | - 111, /* (79) position ::= expr EDGEPT OF position */ | |
| 2145 | - 111, /* (80) position ::= expr ON HEADING expr FROM position */ | |
| 2146 | - 111, /* (81) position ::= expr HEADING expr FROM position */ | |
| 2147 | - 112, /* (82) place ::= edge OF object */ | |
| 2148 | - 135, /* (83) place2 ::= object */ | |
| 2149 | - 135, /* (84) place2 ::= object DOT_E edge */ | |
| 2150 | - 135, /* (85) place2 ::= NTH VERTEX OF object */ | |
| 2151 | - 113, /* (86) object ::= nth */ | |
| 2152 | - 113, /* (87) object ::= nth OF|IN object */ | |
| 2153 | - 114, /* (88) objectname ::= THIS */ | |
| 2154 | - 114, /* (89) objectname ::= PLACENAME */ | |
| 2155 | - 114, /* (90) objectname ::= objectname DOT_U PLACENAME */ | |
| 2156 | - 115, /* (91) nth ::= NTH CLASSNAME */ | |
| 2157 | - 115, /* (92) nth ::= NTH LAST CLASSNAME */ | |
| 2158 | - 115, /* (93) nth ::= LAST CLASSNAME */ | |
| 2159 | - 115, /* (94) nth ::= LAST */ | |
| 2160 | - 115, /* (95) nth ::= NTH LB RB */ | |
| 2161 | - 115, /* (96) nth ::= NTH LAST LB RB */ | |
| 2162 | - 115, /* (97) nth ::= LAST LB RB */ | |
| 2163 | - 104, /* (98) expr ::= expr PLUS expr */ | |
| 2164 | - 104, /* (99) expr ::= expr MINUS expr */ | |
| 2165 | - 104, /* (100) expr ::= expr STAR expr */ | |
| 2166 | - 104, /* (101) expr ::= expr SLASH expr */ | |
| 2167 | - 104, /* (102) expr ::= MINUS expr */ | |
| 2168 | - 104, /* (103) expr ::= PLUS expr */ | |
| 2169 | - 104, /* (104) expr ::= LP expr RP */ | |
| 2170 | - 104, /* (105) expr ::= LP FILL|COLOR|THICKNESS RP */ | |
| 2171 | - 104, /* (106) expr ::= NUMBER */ | |
| 2172 | - 104, /* (107) expr ::= ID */ | |
| 2173 | - 104, /* (108) expr ::= FUNC1 LP expr RP */ | |
| 2174 | - 104, /* (109) expr ::= FUNC2 LP expr COMMA expr RP */ | |
| 2175 | - 104, /* (110) expr ::= DIST LP position COMMA position RP */ | |
| 2176 | - 104, /* (111) expr ::= place2 DOT_XY X */ | |
| 2177 | - 104, /* (112) expr ::= place2 DOT_XY Y */ | |
| 2178 | - 104, /* (113) expr ::= object DOT_L numproperty */ | |
| 2179 | - 104, /* (114) expr ::= object DOT_L dashproperty */ | |
| 2180 | - 104, /* (115) expr ::= object DOT_L colorproperty */ | |
| 2181 | - 118, /* (116) lvalue ::= ID */ | |
| 2182 | - 118, /* (117) lvalue ::= FILL */ | |
| 2183 | - 118, /* (118) lvalue ::= COLOR */ | |
| 2184 | - 118, /* (119) lvalue ::= THICKNESS */ | |
| 2185 | - 117, /* (120) rvalue ::= expr */ | |
| 2186 | - 123, /* (121) print ::= PRINT */ | |
| 2187 | - 124, /* (122) prlist ::= pritem */ | |
| 2188 | - 124, /* (123) prlist ::= prlist prsep pritem */ | |
| 2189 | - 107, /* (124) direction ::= UP */ | |
| 2190 | - 107, /* (125) direction ::= DOWN */ | |
| 2191 | - 107, /* (126) direction ::= LEFT */ | |
| 2192 | - 107, /* (127) direction ::= RIGHT */ | |
| 2193 | - 121, /* (128) optrelexpr ::= relexpr */ | |
| 2194 | - 127, /* (129) attribute_list ::= alist */ | |
| 2195 | - 129, /* (130) alist ::= */ | |
| 2196 | - 129, /* (131) alist ::= alist attribute */ | |
| 2197 | - 130, /* (132) attribute ::= boolproperty */ | |
| 2198 | - 130, /* (133) attribute ::= WITH withclause */ | |
| 2199 | - 131, /* (134) go ::= GO */ | |
| 2200 | - 131, /* (135) go ::= */ | |
| 2201 | - 119, /* (136) even ::= UNTIL EVEN WITH */ | |
| 2202 | - 119, /* (137) even ::= EVEN WITH */ | |
| 2203 | - 108, /* (138) dashproperty ::= DOTTED */ | |
| 2204 | - 108, /* (139) dashproperty ::= DASHED */ | |
| 2205 | - 109, /* (140) colorproperty ::= FILL */ | |
| 2206 | - 109, /* (141) colorproperty ::= COLOR */ | |
| 2207 | - 111, /* (142) position ::= place */ | |
| 2208 | - 134, /* (143) between ::= WAY BETWEEN */ | |
| 2209 | - 134, /* (144) between ::= BETWEEN */ | |
| 2210 | - 134, /* (145) between ::= OF THE WAY BETWEEN */ | |
| 2211 | - 112, /* (146) place ::= place2 */ | |
| 2212 | - 106, /* (147) edge ::= CENTER */ | |
| 2213 | - 106, /* (148) edge ::= EDGEPT */ | |
| 2214 | - 106, /* (149) edge ::= TOP */ | |
| 2215 | - 106, /* (150) edge ::= BOTTOM */ | |
| 2216 | - 106, /* (151) edge ::= START */ | |
| 2217 | - 106, /* (152) edge ::= END */ | |
| 2218 | - 106, /* (153) edge ::= RIGHT */ | |
| 2219 | - 106, /* (154) edge ::= LEFT */ | |
| 2220 | - 113, /* (155) object ::= objectname */ | |
| 2118 | + 124, /* (0) document ::= statement_list */ | |
| 2119 | + 101, /* (1) statement_list ::= statement */ | |
| 2120 | + 101, /* (2) statement_list ::= statement_list EOL statement */ | |
| 2121 | + 102, /* (3) statement ::= */ | |
| 2122 | + 102, /* (4) statement ::= direction */ | |
| 2123 | + 102, /* (5) statement ::= lvalue ASSIGN rvalue */ | |
| 2124 | + 102, /* (6) statement ::= PLACENAME COLON unnamed_statement */ | |
| 2125 | + 102, /* (7) statement ::= PLACENAME COLON position */ | |
| 2126 | + 102, /* (8) statement ::= unnamed_statement */ | |
| 2127 | + 102, /* (9) statement ::= print prlist */ | |
| 2128 | + 102, /* (10) statement ::= ASSERT LP expr EQ expr RP */ | |
| 2129 | + 102, /* (11) statement ::= ASSERT LP position EQ position RP */ | |
| 2130 | + 102, /* (12) statement ::= DEFINE ID CODEBLOCK */ | |
| 2131 | + 119, /* (13) rvalue ::= PLACENAME */ | |
| 2132 | + 127, /* (14) pritem ::= FILL */ | |
| 2133 | + 127, /* (15) pritem ::= COLOR */ | |
| 2134 | + 127, /* (16) pritem ::= THICKNESS */ | |
| 2135 | + 127, /* (17) pritem ::= rvalue */ | |
| 2136 | + 127, /* (18) pritem ::= STRING */ | |
| 2137 | + 128, /* (19) prsep ::= COMMA */ | |
| 2138 | + 103, /* (20) unnamed_statement ::= basetype attribute_list */ | |
| 2139 | + 104, /* (21) basetype ::= CLASSNAME */ | |
| 2140 | + 104, /* (22) basetype ::= STRING textposition */ | |
| 2141 | + 104, /* (23) basetype ::= LB savelist statement_list RB */ | |
| 2142 | + 130, /* (24) savelist ::= */ | |
| 2143 | + 122, /* (25) relexpr ::= expr */ | |
| 2144 | + 122, /* (26) relexpr ::= expr PERCENT */ | |
| 2145 | + 123, /* (27) optrelexpr ::= */ | |
| 2146 | + 129, /* (28) attribute_list ::= relexpr alist */ | |
| 2147 | + 132, /* (29) attribute ::= numproperty relexpr */ | |
| 2148 | + 132, /* (30) attribute ::= dashproperty expr */ | |
| 2149 | + 132, /* (31) attribute ::= dashproperty */ | |
| 2150 | + 132, /* (32) attribute ::= colorproperty rvalue */ | |
| 2151 | + 132, /* (33) attribute ::= go direction optrelexpr */ | |
| 2152 | + 132, /* (34) attribute ::= go direction even position */ | |
| 2153 | + 132, /* (35) attribute ::= CLOSE */ | |
| 2154 | + 132, /* (36) attribute ::= CHOP */ | |
| 2155 | + 132, /* (37) attribute ::= FROM position */ | |
| 2156 | + 132, /* (38) attribute ::= TO position */ | |
| 2157 | + 132, /* (39) attribute ::= THEN */ | |
| 2158 | + 132, /* (40) attribute ::= THEN optrelexpr HEADING expr */ | |
| 2159 | + 132, /* (41) attribute ::= THEN optrelexpr EDGEPT */ | |
| 2160 | + 132, /* (42) attribute ::= GO optrelexpr HEADING expr */ | |
| 2161 | + 132, /* (43) attribute ::= GO optrelexpr EDGEPT */ | |
| 2162 | + 132, /* (44) attribute ::= AT position */ | |
| 2163 | + 132, /* (45) attribute ::= SAME */ | |
| 2164 | + 132, /* (46) attribute ::= SAME AS object */ | |
| 2165 | + 132, /* (47) attribute ::= STRING textposition */ | |
| 2166 | + 132, /* (48) attribute ::= FIT */ | |
| 2167 | + 132, /* (49) attribute ::= BEHIND object */ | |
| 2168 | + 135, /* (50) withclause ::= DOT_E edge AT position */ | |
| 2169 | + 135, /* (51) withclause ::= edge AT position */ | |
| 2170 | + 106, /* (52) numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */ | |
| 2171 | + 134, /* (53) boolproperty ::= CW */ | |
| 2172 | + 134, /* (54) boolproperty ::= CCW */ | |
| 2173 | + 134, /* (55) boolproperty ::= LARROW */ | |
| 2174 | + 134, /* (56) boolproperty ::= RARROW */ | |
| 2175 | + 134, /* (57) boolproperty ::= LRARROW */ | |
| 2176 | + 134, /* (58) boolproperty ::= INVIS */ | |
| 2177 | + 134, /* (59) boolproperty ::= THICK */ | |
| 2178 | + 134, /* (60) boolproperty ::= THIN */ | |
| 2179 | + 134, /* (61) boolproperty ::= SOLID */ | |
| 2180 | + 118, /* (62) textposition ::= */ | |
| 2181 | + 118, /* (63) textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL */ | |
| 2182 | + 113, /* (64) position ::= expr COMMA expr */ | |
| 2183 | + 113, /* (65) position ::= place PLUS expr COMMA expr */ | |
| 2184 | + 113, /* (66) position ::= place MINUS expr COMMA expr */ | |
| 2185 | + 113, /* (67) position ::= place PLUS LP expr COMMA expr RP */ | |
| 2186 | + 113, /* (68) position ::= place MINUS LP expr COMMA expr RP */ | |
| 2187 | + 113, /* (69) position ::= LP position COMMA position RP */ | |
| 2188 | + 113, /* (70) position ::= LP position RP */ | |
| 2189 | + 113, /* (71) position ::= expr between position AND position */ | |
| 2190 | + 113, /* (72) position ::= expr LT position COMMA position GT */ | |
| 2191 | + 113, /* (73) position ::= expr ABOVE position */ | |
| 2192 | + 113, /* (74) position ::= expr BELOW position */ | |
| 2193 | + 113, /* (75) position ::= expr LEFT OF position */ | |
| 2194 | + 113, /* (76) position ::= expr RIGHT OF position */ | |
| 2195 | + 113, /* (77) position ::= expr ON HEADING EDGEPT OF position */ | |
| 2196 | + 113, /* (78) position ::= expr HEADING EDGEPT OF position */ | |
| 2197 | + 113, /* (79) position ::= expr EDGEPT OF position */ | |
| 2198 | + 113, /* (80) position ::= expr ON HEADING expr FROM position */ | |
| 2199 | + 113, /* (81) position ::= expr HEADING expr FROM position */ | |
| 2200 | + 114, /* (82) place ::= edge OF object */ | |
| 2201 | + 137, /* (83) place2 ::= object */ | |
| 2202 | + 137, /* (84) place2 ::= object DOT_E edge */ | |
| 2203 | + 137, /* (85) place2 ::= NTH VERTEX OF object */ | |
| 2204 | + 115, /* (86) object ::= nth */ | |
| 2205 | + 115, /* (87) object ::= nth OF|IN object */ | |
| 2206 | + 116, /* (88) objectname ::= THIS */ | |
| 2207 | + 116, /* (89) objectname ::= PLACENAME */ | |
| 2208 | + 116, /* (90) objectname ::= objectname DOT_U PLACENAME */ | |
| 2209 | + 117, /* (91) nth ::= NTH CLASSNAME */ | |
| 2210 | + 117, /* (92) nth ::= NTH LAST CLASSNAME */ | |
| 2211 | + 117, /* (93) nth ::= LAST CLASSNAME */ | |
| 2212 | + 117, /* (94) nth ::= LAST */ | |
| 2213 | + 117, /* (95) nth ::= NTH LB RB */ | |
| 2214 | + 117, /* (96) nth ::= NTH LAST LB RB */ | |
| 2215 | + 117, /* (97) nth ::= LAST LB RB */ | |
| 2216 | + 105, /* (98) expr ::= expr PLUS expr */ | |
| 2217 | + 105, /* (99) expr ::= expr MINUS expr */ | |
| 2218 | + 105, /* (100) expr ::= expr STAR expr */ | |
| 2219 | + 105, /* (101) expr ::= expr SLASH expr */ | |
| 2220 | + 105, /* (102) expr ::= MINUS expr */ | |
| 2221 | + 105, /* (103) expr ::= PLUS expr */ | |
| 2222 | + 105, /* (104) expr ::= LP expr RP */ | |
| 2223 | + 105, /* (105) expr ::= LP FILL|COLOR|THICKNESS RP */ | |
| 2224 | + 105, /* (106) expr ::= NUMBER */ | |
| 2225 | + 105, /* (107) expr ::= ID */ | |
| 2226 | + 105, /* (108) expr ::= FUNC1 LP expr RP */ | |
| 2227 | + 105, /* (109) expr ::= FUNC2 LP expr COMMA expr RP */ | |
| 2228 | + 105, /* (110) expr ::= DIST LP position COMMA position RP */ | |
| 2229 | + 105, /* (111) expr ::= place2 DOT_XY X */ | |
| 2230 | + 105, /* (112) expr ::= place2 DOT_XY Y */ | |
| 2231 | + 105, /* (113) expr ::= object DOT_L numproperty */ | |
| 2232 | + 105, /* (114) expr ::= object DOT_L dashproperty */ | |
| 2233 | + 105, /* (115) expr ::= object DOT_L colorproperty */ | |
| 2234 | + 120, /* (116) lvalue ::= ID */ | |
| 2235 | + 120, /* (117) lvalue ::= FILL */ | |
| 2236 | + 120, /* (118) lvalue ::= COLOR */ | |
| 2237 | + 120, /* (119) lvalue ::= THICKNESS */ | |
| 2238 | + 119, /* (120) rvalue ::= expr */ | |
| 2239 | + 125, /* (121) print ::= PRINT */ | |
| 2240 | + 126, /* (122) prlist ::= pritem */ | |
| 2241 | + 126, /* (123) prlist ::= prlist prsep pritem */ | |
| 2242 | + 109, /* (124) direction ::= UP */ | |
| 2243 | + 109, /* (125) direction ::= DOWN */ | |
| 2244 | + 109, /* (126) direction ::= LEFT */ | |
| 2245 | + 109, /* (127) direction ::= RIGHT */ | |
| 2246 | + 123, /* (128) optrelexpr ::= relexpr */ | |
| 2247 | + 129, /* (129) attribute_list ::= alist */ | |
| 2248 | + 131, /* (130) alist ::= */ | |
| 2249 | + 131, /* (131) alist ::= alist attribute */ | |
| 2250 | + 132, /* (132) attribute ::= boolproperty */ | |
| 2251 | + 132, /* (133) attribute ::= WITH withclause */ | |
| 2252 | + 133, /* (134) go ::= GO */ | |
| 2253 | + 133, /* (135) go ::= */ | |
| 2254 | + 121, /* (136) even ::= UNTIL EVEN WITH */ | |
| 2255 | + 121, /* (137) even ::= EVEN WITH */ | |
| 2256 | + 110, /* (138) dashproperty ::= DOTTED */ | |
| 2257 | + 110, /* (139) dashproperty ::= DASHED */ | |
| 2258 | + 111, /* (140) colorproperty ::= FILL */ | |
| 2259 | + 111, /* (141) colorproperty ::= COLOR */ | |
| 2260 | + 113, /* (142) position ::= place */ | |
| 2261 | + 136, /* (143) between ::= WAY BETWEEN */ | |
| 2262 | + 136, /* (144) between ::= BETWEEN */ | |
| 2263 | + 136, /* (145) between ::= OF THE WAY BETWEEN */ | |
| 2264 | + 114, /* (146) place ::= place2 */ | |
| 2265 | + 107, /* (147) edge ::= CENTER */ | |
| 2266 | + 107, /* (148) edge ::= EDGEPT */ | |
| 2267 | + 107, /* (149) edge ::= TOP */ | |
| 2268 | + 107, /* (150) edge ::= BOTTOM */ | |
| 2269 | + 107, /* (151) edge ::= START */ | |
| 2270 | + 107, /* (152) edge ::= END */ | |
| 2271 | + 107, /* (153) edge ::= RIGHT */ | |
| 2272 | + 107, /* (154) edge ::= LEFT */ | |
| 2273 | + 115, /* (155) object ::= objectname */ | |
| 2221 | 2274 | }; |
| 2222 | 2275 | |
| 2223 | 2276 | /* For rule J, yyRuleInfoNRhs[J] contains the negative of the number |
| 2224 | 2277 | ** of symbols on the right-hand side of that rule. */ |
| 2225 | 2278 | static const signed char yyRuleInfoNRhs[] = { |
| @@ -2419,620 +2472,620 @@ | ||
| 2419 | 2472 | ** break; |
| 2420 | 2473 | */ |
| 2421 | 2474 | /********** Begin reduce actions **********************************************/ |
| 2422 | 2475 | YYMINORTYPE yylhsminor; |
| 2423 | 2476 | case 0: /* document ::= statement_list */ |
| 2424 | -#line 549 "pikchr.y" | |
| 2425 | -{pik_render(p,yymsp[0].minor.yy235);} | |
| 2426 | -#line 2451 "pikchr.c" | |
| 2477 | +#line 563 "pikchr.y" | |
| 2478 | +{pik_render(p,yymsp[0].minor.yy23);} | |
| 2479 | +#line 2479 "pikchr.c" | |
| 2427 | 2480 | break; |
| 2428 | 2481 | case 1: /* statement_list ::= statement */ |
| 2429 | -#line 552 "pikchr.y" | |
| 2430 | -{ yylhsminor.yy235 = pik_elist_append(p,0,yymsp[0].minor.yy162); } | |
| 2431 | -#line 2456 "pikchr.c" | |
| 2432 | - yymsp[0].minor.yy235 = yylhsminor.yy235; | |
| 2482 | +#line 566 "pikchr.y" | |
| 2483 | +{ yylhsminor.yy23 = pik_elist_append(p,0,yymsp[0].minor.yy54); } | |
| 2484 | +#line 2484 "pikchr.c" | |
| 2485 | + yymsp[0].minor.yy23 = yylhsminor.yy23; | |
| 2433 | 2486 | break; |
| 2434 | 2487 | case 2: /* statement_list ::= statement_list EOL statement */ |
| 2435 | -#line 554 "pikchr.y" | |
| 2436 | -{ yylhsminor.yy235 = pik_elist_append(p,yymsp[-2].minor.yy235,yymsp[0].minor.yy162); } | |
| 2437 | -#line 2462 "pikchr.c" | |
| 2438 | - yymsp[-2].minor.yy235 = yylhsminor.yy235; | |
| 2488 | +#line 568 "pikchr.y" | |
| 2489 | +{ yylhsminor.yy23 = pik_elist_append(p,yymsp[-2].minor.yy23,yymsp[0].minor.yy54); } | |
| 2490 | +#line 2490 "pikchr.c" | |
| 2491 | + yymsp[-2].minor.yy23 = yylhsminor.yy23; | |
| 2439 | 2492 | break; |
| 2440 | 2493 | case 3: /* statement ::= */ |
| 2441 | -#line 557 "pikchr.y" | |
| 2442 | -{ yymsp[1].minor.yy162 = 0; } | |
| 2443 | -#line 2468 "pikchr.c" | |
| 2494 | +#line 571 "pikchr.y" | |
| 2495 | +{ yymsp[1].minor.yy54 = 0; } | |
| 2496 | +#line 2496 "pikchr.c" | |
| 2444 | 2497 | break; |
| 2445 | 2498 | case 4: /* statement ::= direction */ |
| 2446 | -#line 558 "pikchr.y" | |
| 2447 | -{ pik_set_direction(p,yymsp[0].minor.yy0.eCode); yylhsminor.yy162=0; } | |
| 2448 | -#line 2473 "pikchr.c" | |
| 2449 | - yymsp[0].minor.yy162 = yylhsminor.yy162; | |
| 2499 | +#line 572 "pikchr.y" | |
| 2500 | +{ pik_set_direction(p,yymsp[0].minor.yy0.eCode); yylhsminor.yy54=0; } | |
| 2501 | +#line 2501 "pikchr.c" | |
| 2502 | + yymsp[0].minor.yy54 = yylhsminor.yy54; | |
| 2450 | 2503 | break; |
| 2451 | 2504 | case 5: /* statement ::= lvalue ASSIGN rvalue */ |
| 2452 | -#line 559 "pikchr.y" | |
| 2453 | -{pik_set_var(p,&yymsp[-2].minor.yy0,yymsp[0].minor.yy21,&yymsp[-1].minor.yy0); yylhsminor.yy162=0;} | |
| 2454 | -#line 2479 "pikchr.c" | |
| 2455 | - yymsp[-2].minor.yy162 = yylhsminor.yy162; | |
| 2505 | +#line 573 "pikchr.y" | |
| 2506 | +{pik_set_var(p,&yymsp[-2].minor.yy0,yymsp[0].minor.yy129,&yymsp[-1].minor.yy0); yylhsminor.yy54=0;} | |
| 2507 | +#line 2507 "pikchr.c" | |
| 2508 | + yymsp[-2].minor.yy54 = yylhsminor.yy54; | |
| 2456 | 2509 | break; |
| 2457 | 2510 | case 6: /* statement ::= PLACENAME COLON unnamed_statement */ |
| 2458 | -#line 561 "pikchr.y" | |
| 2459 | -{ yylhsminor.yy162 = yymsp[0].minor.yy162; pik_elem_setname(p,yymsp[0].minor.yy162,&yymsp[-2].minor.yy0); } | |
| 2460 | -#line 2485 "pikchr.c" | |
| 2461 | - yymsp[-2].minor.yy162 = yylhsminor.yy162; | |
| 2511 | +#line 575 "pikchr.y" | |
| 2512 | +{ yylhsminor.yy54 = yymsp[0].minor.yy54; pik_elem_setname(p,yymsp[0].minor.yy54,&yymsp[-2].minor.yy0); } | |
| 2513 | +#line 2513 "pikchr.c" | |
| 2514 | + yymsp[-2].minor.yy54 = yylhsminor.yy54; | |
| 2462 | 2515 | break; |
| 2463 | 2516 | case 7: /* statement ::= PLACENAME COLON position */ |
| 2464 | -#line 563 "pikchr.y" | |
| 2465 | -{ yylhsminor.yy162 = pik_elem_new(p,0,0,0); | |
| 2466 | - if(yylhsminor.yy162){ yylhsminor.yy162->ptAt = yymsp[0].minor.yy63; pik_elem_setname(p,yylhsminor.yy162,&yymsp[-2].minor.yy0); }} | |
| 2467 | -#line 2492 "pikchr.c" | |
| 2468 | - yymsp[-2].minor.yy162 = yylhsminor.yy162; | |
| 2517 | +#line 577 "pikchr.y" | |
| 2518 | +{ yylhsminor.yy54 = pik_elem_new(p,0,0,0); | |
| 2519 | + if(yylhsminor.yy54){ yylhsminor.yy54->ptAt = yymsp[0].minor.yy187; pik_elem_setname(p,yylhsminor.yy54,&yymsp[-2].minor.yy0); }} | |
| 2520 | +#line 2520 "pikchr.c" | |
| 2521 | + yymsp[-2].minor.yy54 = yylhsminor.yy54; | |
| 2469 | 2522 | break; |
| 2470 | 2523 | case 8: /* statement ::= unnamed_statement */ |
| 2471 | -#line 565 "pikchr.y" | |
| 2472 | -{yylhsminor.yy162 = yymsp[0].minor.yy162;} | |
| 2473 | -#line 2498 "pikchr.c" | |
| 2474 | - yymsp[0].minor.yy162 = yylhsminor.yy162; | |
| 2524 | +#line 579 "pikchr.y" | |
| 2525 | +{yylhsminor.yy54 = yymsp[0].minor.yy54;} | |
| 2526 | +#line 2526 "pikchr.c" | |
| 2527 | + yymsp[0].minor.yy54 = yylhsminor.yy54; | |
| 2475 | 2528 | break; |
| 2476 | 2529 | case 9: /* statement ::= print prlist */ |
| 2477 | -#line 566 "pikchr.y" | |
| 2478 | -{pik_append(p,"<br>\n",5); yymsp[-1].minor.yy162=0;} | |
| 2479 | -#line 2504 "pikchr.c" | |
| 2530 | +#line 580 "pikchr.y" | |
| 2531 | +{pik_append(p,"<br>\n",5); yymsp[-1].minor.yy54=0;} | |
| 2532 | +#line 2532 "pikchr.c" | |
| 2480 | 2533 | break; |
| 2481 | 2534 | case 10: /* statement ::= ASSERT LP expr EQ expr RP */ |
| 2482 | -#line 571 "pikchr.y" | |
| 2483 | -{yymsp[-5].minor.yy162=pik_assert(p,yymsp[-3].minor.yy21,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy21);} | |
| 2484 | -#line 2509 "pikchr.c" | |
| 2535 | +#line 585 "pikchr.y" | |
| 2536 | +{yymsp[-5].minor.yy54=pik_assert(p,yymsp[-3].minor.yy129,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy129);} | |
| 2537 | +#line 2537 "pikchr.c" | |
| 2485 | 2538 | break; |
| 2486 | 2539 | case 11: /* statement ::= ASSERT LP position EQ position RP */ |
| 2487 | -#line 573 "pikchr.y" | |
| 2488 | -{yymsp[-5].minor.yy162=pik_position_assert(p,&yymsp[-3].minor.yy63,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy63);} | |
| 2489 | -#line 2514 "pikchr.c" | |
| 2540 | +#line 587 "pikchr.y" | |
| 2541 | +{yymsp[-5].minor.yy54=pik_position_assert(p,&yymsp[-3].minor.yy187,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy187);} | |
| 2542 | +#line 2542 "pikchr.c" | |
| 2490 | 2543 | break; |
| 2491 | 2544 | case 12: /* statement ::= DEFINE ID CODEBLOCK */ |
| 2492 | -#line 574 "pikchr.y" | |
| 2493 | -{yymsp[-2].minor.yy162=0; pik_add_macro(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);} | |
| 2494 | -#line 2519 "pikchr.c" | |
| 2545 | +#line 588 "pikchr.y" | |
| 2546 | +{yymsp[-2].minor.yy54=0; pik_add_macro(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);} | |
| 2547 | +#line 2547 "pikchr.c" | |
| 2495 | 2548 | break; |
| 2496 | 2549 | case 13: /* rvalue ::= PLACENAME */ |
| 2497 | -#line 585 "pikchr.y" | |
| 2498 | -{yylhsminor.yy21 = pik_lookup_color(p,&yymsp[0].minor.yy0);} | |
| 2499 | -#line 2524 "pikchr.c" | |
| 2500 | - yymsp[0].minor.yy21 = yylhsminor.yy21; | |
| 2550 | +#line 599 "pikchr.y" | |
| 2551 | +{yylhsminor.yy129 = pik_lookup_color(p,&yymsp[0].minor.yy0);} | |
| 2552 | +#line 2552 "pikchr.c" | |
| 2553 | + yymsp[0].minor.yy129 = yylhsminor.yy129; | |
| 2501 | 2554 | break; |
| 2502 | 2555 | case 14: /* pritem ::= FILL */ |
| 2503 | 2556 | case 15: /* pritem ::= COLOR */ yytestcase(yyruleno==15); |
| 2504 | 2557 | case 16: /* pritem ::= THICKNESS */ yytestcase(yyruleno==16); |
| 2505 | -#line 590 "pikchr.y" | |
| 2558 | +#line 604 "pikchr.y" | |
| 2506 | 2559 | {pik_append_num(p,"",pik_value(p,yymsp[0].minor.yy0.z,yymsp[0].minor.yy0.n,0));} |
| 2507 | -#line 2532 "pikchr.c" | |
| 2560 | +#line 2560 "pikchr.c" | |
| 2508 | 2561 | break; |
| 2509 | 2562 | case 17: /* pritem ::= rvalue */ |
| 2510 | -#line 593 "pikchr.y" | |
| 2511 | -{pik_append_num(p,"",yymsp[0].minor.yy21);} | |
| 2512 | -#line 2537 "pikchr.c" | |
| 2563 | +#line 607 "pikchr.y" | |
| 2564 | +{pik_append_num(p,"",yymsp[0].minor.yy129);} | |
| 2565 | +#line 2565 "pikchr.c" | |
| 2513 | 2566 | break; |
| 2514 | 2567 | case 18: /* pritem ::= STRING */ |
| 2515 | -#line 594 "pikchr.y" | |
| 2568 | +#line 608 "pikchr.y" | |
| 2516 | 2569 | {pik_append_text(p,yymsp[0].minor.yy0.z+1,yymsp[0].minor.yy0.n-2,0);} |
| 2517 | -#line 2542 "pikchr.c" | |
| 2570 | +#line 2570 "pikchr.c" | |
| 2518 | 2571 | break; |
| 2519 | 2572 | case 19: /* prsep ::= COMMA */ |
| 2520 | -#line 595 "pikchr.y" | |
| 2573 | +#line 609 "pikchr.y" | |
| 2521 | 2574 | {pik_append(p, " ", 1);} |
| 2522 | -#line 2547 "pikchr.c" | |
| 2575 | +#line 2575 "pikchr.c" | |
| 2523 | 2576 | break; |
| 2524 | 2577 | case 20: /* unnamed_statement ::= basetype attribute_list */ |
| 2525 | -#line 598 "pikchr.y" | |
| 2526 | -{yylhsminor.yy162 = yymsp[-1].minor.yy162; pik_after_adding_attributes(p,yylhsminor.yy162);} | |
| 2527 | -#line 2552 "pikchr.c" | |
| 2528 | - yymsp[-1].minor.yy162 = yylhsminor.yy162; | |
| 2578 | +#line 614 "pikchr.y" | |
| 2579 | +{yylhsminor.yy54 = yymsp[-1].minor.yy54; pik_after_adding_attributes(p,yylhsminor.yy54);} | |
| 2580 | +#line 2580 "pikchr.c" | |
| 2581 | + yymsp[-1].minor.yy54 = yylhsminor.yy54; | |
| 2529 | 2582 | break; |
| 2530 | 2583 | case 21: /* basetype ::= CLASSNAME */ |
| 2531 | -#line 600 "pikchr.y" | |
| 2532 | -{yylhsminor.yy162 = pik_elem_new(p,&yymsp[0].minor.yy0,0,0); } | |
| 2533 | -#line 2558 "pikchr.c" | |
| 2534 | - yymsp[0].minor.yy162 = yylhsminor.yy162; | |
| 2584 | +#line 616 "pikchr.y" | |
| 2585 | +{yylhsminor.yy54 = pik_elem_new(p,&yymsp[0].minor.yy0,0,0); } | |
| 2586 | +#line 2586 "pikchr.c" | |
| 2587 | + yymsp[0].minor.yy54 = yylhsminor.yy54; | |
| 2535 | 2588 | break; |
| 2536 | 2589 | case 22: /* basetype ::= STRING textposition */ |
| 2537 | -#line 602 "pikchr.y" | |
| 2538 | -{yymsp[-1].minor.yy0.eCode = yymsp[0].minor.yy188; yylhsminor.yy162 = pik_elem_new(p,0,&yymsp[-1].minor.yy0,0); } | |
| 2539 | -#line 2564 "pikchr.c" | |
| 2540 | - yymsp[-1].minor.yy162 = yylhsminor.yy162; | |
| 2590 | +#line 618 "pikchr.y" | |
| 2591 | +{yymsp[-1].minor.yy0.eCode = yymsp[0].minor.yy272; yylhsminor.yy54 = pik_elem_new(p,0,&yymsp[-1].minor.yy0,0); } | |
| 2592 | +#line 2592 "pikchr.c" | |
| 2593 | + yymsp[-1].minor.yy54 = yylhsminor.yy54; | |
| 2541 | 2594 | break; |
| 2542 | 2595 | case 23: /* basetype ::= LB savelist statement_list RB */ |
| 2543 | -#line 604 "pikchr.y" | |
| 2544 | -{ p->list = yymsp[-2].minor.yy235; yymsp[-3].minor.yy162 = pik_elem_new(p,0,0,yymsp[-1].minor.yy235); if(yymsp[-3].minor.yy162) yymsp[-3].minor.yy162->errTok = yymsp[0].minor.yy0; } | |
| 2545 | -#line 2570 "pikchr.c" | |
| 2596 | +#line 620 "pikchr.y" | |
| 2597 | +{ p->list = yymsp[-2].minor.yy23; yymsp[-3].minor.yy54 = pik_elem_new(p,0,0,yymsp[-1].minor.yy23); if(yymsp[-3].minor.yy54) yymsp[-3].minor.yy54->errTok = yymsp[0].minor.yy0; } | |
| 2598 | +#line 2598 "pikchr.c" | |
| 2546 | 2599 | break; |
| 2547 | 2600 | case 24: /* savelist ::= */ |
| 2548 | -#line 609 "pikchr.y" | |
| 2549 | -{yymsp[1].minor.yy235 = p->list; p->list = 0;} | |
| 2550 | -#line 2575 "pikchr.c" | |
| 2601 | +#line 625 "pikchr.y" | |
| 2602 | +{yymsp[1].minor.yy23 = p->list; p->list = 0;} | |
| 2603 | +#line 2603 "pikchr.c" | |
| 2551 | 2604 | break; |
| 2552 | 2605 | case 25: /* relexpr ::= expr */ |
| 2553 | -#line 616 "pikchr.y" | |
| 2554 | -{yylhsminor.yy72.rAbs = yymsp[0].minor.yy21; yylhsminor.yy72.rRel = 0;} | |
| 2555 | -#line 2580 "pikchr.c" | |
| 2556 | - yymsp[0].minor.yy72 = yylhsminor.yy72; | |
| 2606 | +#line 632 "pikchr.y" | |
| 2607 | +{yylhsminor.yy28.rAbs = yymsp[0].minor.yy129; yylhsminor.yy28.rRel = 0;} | |
| 2608 | +#line 2608 "pikchr.c" | |
| 2609 | + yymsp[0].minor.yy28 = yylhsminor.yy28; | |
| 2557 | 2610 | break; |
| 2558 | 2611 | case 26: /* relexpr ::= expr PERCENT */ |
| 2559 | -#line 617 "pikchr.y" | |
| 2560 | -{yylhsminor.yy72.rAbs = 0; yylhsminor.yy72.rRel = yymsp[-1].minor.yy21/100;} | |
| 2561 | -#line 2586 "pikchr.c" | |
| 2562 | - yymsp[-1].minor.yy72 = yylhsminor.yy72; | |
| 2612 | +#line 633 "pikchr.y" | |
| 2613 | +{yylhsminor.yy28.rAbs = 0; yylhsminor.yy28.rRel = yymsp[-1].minor.yy129/100;} | |
| 2614 | +#line 2614 "pikchr.c" | |
| 2615 | + yymsp[-1].minor.yy28 = yylhsminor.yy28; | |
| 2563 | 2616 | break; |
| 2564 | 2617 | case 27: /* optrelexpr ::= */ |
| 2565 | -#line 619 "pikchr.y" | |
| 2566 | -{yymsp[1].minor.yy72.rAbs = 0; yymsp[1].minor.yy72.rRel = 1.0;} | |
| 2567 | -#line 2592 "pikchr.c" | |
| 2618 | +#line 635 "pikchr.y" | |
| 2619 | +{yymsp[1].minor.yy28.rAbs = 0; yymsp[1].minor.yy28.rRel = 1.0;} | |
| 2620 | +#line 2620 "pikchr.c" | |
| 2568 | 2621 | break; |
| 2569 | 2622 | case 28: /* attribute_list ::= relexpr alist */ |
| 2570 | -#line 621 "pikchr.y" | |
| 2571 | -{pik_add_direction(p,0,&yymsp[-1].minor.yy72);} | |
| 2572 | -#line 2597 "pikchr.c" | |
| 2623 | +#line 637 "pikchr.y" | |
| 2624 | +{pik_add_direction(p,0,&yymsp[-1].minor.yy28);} | |
| 2625 | +#line 2625 "pikchr.c" | |
| 2573 | 2626 | break; |
| 2574 | 2627 | case 29: /* attribute ::= numproperty relexpr */ |
| 2575 | -#line 625 "pikchr.y" | |
| 2576 | -{ pik_set_numprop(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy72); } | |
| 2577 | -#line 2602 "pikchr.c" | |
| 2628 | +#line 641 "pikchr.y" | |
| 2629 | +{ pik_set_numprop(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy28); } | |
| 2630 | +#line 2630 "pikchr.c" | |
| 2578 | 2631 | break; |
| 2579 | 2632 | case 30: /* attribute ::= dashproperty expr */ |
| 2580 | -#line 626 "pikchr.y" | |
| 2581 | -{ pik_set_dashed(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy21); } | |
| 2582 | -#line 2607 "pikchr.c" | |
| 2633 | +#line 642 "pikchr.y" | |
| 2634 | +{ pik_set_dashed(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy129); } | |
| 2635 | +#line 2635 "pikchr.c" | |
| 2583 | 2636 | break; |
| 2584 | 2637 | case 31: /* attribute ::= dashproperty */ |
| 2585 | -#line 627 "pikchr.y" | |
| 2638 | +#line 643 "pikchr.y" | |
| 2586 | 2639 | { pik_set_dashed(p,&yymsp[0].minor.yy0,0); } |
| 2587 | -#line 2612 "pikchr.c" | |
| 2640 | +#line 2640 "pikchr.c" | |
| 2588 | 2641 | break; |
| 2589 | 2642 | case 32: /* attribute ::= colorproperty rvalue */ |
| 2590 | -#line 628 "pikchr.y" | |
| 2591 | -{ pik_set_clrprop(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy21); } | |
| 2592 | -#line 2617 "pikchr.c" | |
| 2643 | +#line 644 "pikchr.y" | |
| 2644 | +{ pik_set_clrprop(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy129); } | |
| 2645 | +#line 2645 "pikchr.c" | |
| 2593 | 2646 | break; |
| 2594 | 2647 | case 33: /* attribute ::= go direction optrelexpr */ |
| 2595 | -#line 629 "pikchr.y" | |
| 2596 | -{ pik_add_direction(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy72);} | |
| 2597 | -#line 2622 "pikchr.c" | |
| 2648 | +#line 645 "pikchr.y" | |
| 2649 | +{ pik_add_direction(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy28);} | |
| 2650 | +#line 2650 "pikchr.c" | |
| 2598 | 2651 | break; |
| 2599 | 2652 | case 34: /* attribute ::= go direction even position */ |
| 2600 | -#line 630 "pikchr.y" | |
| 2601 | -{pik_evenwith(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy63);} | |
| 2602 | -#line 2627 "pikchr.c" | |
| 2653 | +#line 646 "pikchr.y" | |
| 2654 | +{pik_evenwith(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy187);} | |
| 2655 | +#line 2655 "pikchr.c" | |
| 2603 | 2656 | break; |
| 2604 | 2657 | case 35: /* attribute ::= CLOSE */ |
| 2605 | -#line 631 "pikchr.y" | |
| 2658 | +#line 647 "pikchr.y" | |
| 2606 | 2659 | { pik_close_path(p,&yymsp[0].minor.yy0); } |
| 2607 | -#line 2632 "pikchr.c" | |
| 2660 | +#line 2660 "pikchr.c" | |
| 2608 | 2661 | break; |
| 2609 | 2662 | case 36: /* attribute ::= CHOP */ |
| 2610 | -#line 632 "pikchr.y" | |
| 2663 | +#line 648 "pikchr.y" | |
| 2611 | 2664 | { p->cur->bChop = 1; } |
| 2612 | -#line 2637 "pikchr.c" | |
| 2665 | +#line 2665 "pikchr.c" | |
| 2613 | 2666 | break; |
| 2614 | 2667 | case 37: /* attribute ::= FROM position */ |
| 2615 | -#line 633 "pikchr.y" | |
| 2616 | -{ pik_set_from(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy63); } | |
| 2617 | -#line 2642 "pikchr.c" | |
| 2668 | +#line 649 "pikchr.y" | |
| 2669 | +{ pik_set_from(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy187); } | |
| 2670 | +#line 2670 "pikchr.c" | |
| 2618 | 2671 | break; |
| 2619 | 2672 | case 38: /* attribute ::= TO position */ |
| 2620 | -#line 634 "pikchr.y" | |
| 2621 | -{ pik_add_to(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy63); } | |
| 2622 | -#line 2647 "pikchr.c" | |
| 2673 | +#line 650 "pikchr.y" | |
| 2674 | +{ pik_add_to(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy187); } | |
| 2675 | +#line 2675 "pikchr.c" | |
| 2623 | 2676 | break; |
| 2624 | 2677 | case 39: /* attribute ::= THEN */ |
| 2625 | -#line 635 "pikchr.y" | |
| 2678 | +#line 651 "pikchr.y" | |
| 2626 | 2679 | { pik_then(p, &yymsp[0].minor.yy0, p->cur); } |
| 2627 | -#line 2652 "pikchr.c" | |
| 2680 | +#line 2680 "pikchr.c" | |
| 2628 | 2681 | break; |
| 2629 | 2682 | case 40: /* attribute ::= THEN optrelexpr HEADING expr */ |
| 2630 | 2683 | case 42: /* attribute ::= GO optrelexpr HEADING expr */ yytestcase(yyruleno==42); |
| 2631 | -#line 637 "pikchr.y" | |
| 2632 | -{pik_move_hdg(p,&yymsp[-2].minor.yy72,&yymsp[-1].minor.yy0,yymsp[0].minor.yy21,0,&yymsp[-3].minor.yy0);} | |
| 2633 | -#line 2658 "pikchr.c" | |
| 2684 | +#line 653 "pikchr.y" | |
| 2685 | +{pik_move_hdg(p,&yymsp[-2].minor.yy28,&yymsp[-1].minor.yy0,yymsp[0].minor.yy129,0,&yymsp[-3].minor.yy0);} | |
| 2686 | +#line 2686 "pikchr.c" | |
| 2634 | 2687 | break; |
| 2635 | 2688 | case 41: /* attribute ::= THEN optrelexpr EDGEPT */ |
| 2636 | 2689 | case 43: /* attribute ::= GO optrelexpr EDGEPT */ yytestcase(yyruleno==43); |
| 2637 | -#line 638 "pikchr.y" | |
| 2638 | -{pik_move_hdg(p,&yymsp[-1].minor.yy72,0,0,&yymsp[0].minor.yy0,&yymsp[-2].minor.yy0);} | |
| 2639 | -#line 2664 "pikchr.c" | |
| 2690 | +#line 654 "pikchr.y" | |
| 2691 | +{pik_move_hdg(p,&yymsp[-1].minor.yy28,0,0,&yymsp[0].minor.yy0,&yymsp[-2].minor.yy0);} | |
| 2692 | +#line 2692 "pikchr.c" | |
| 2640 | 2693 | break; |
| 2641 | 2694 | case 44: /* attribute ::= AT position */ |
| 2642 | -#line 643 "pikchr.y" | |
| 2643 | -{ pik_set_at(p,0,&yymsp[0].minor.yy63,&yymsp[-1].minor.yy0); } | |
| 2644 | -#line 2669 "pikchr.c" | |
| 2695 | +#line 659 "pikchr.y" | |
| 2696 | +{ pik_set_at(p,0,&yymsp[0].minor.yy187,&yymsp[-1].minor.yy0); } | |
| 2697 | +#line 2697 "pikchr.c" | |
| 2645 | 2698 | break; |
| 2646 | 2699 | case 45: /* attribute ::= SAME */ |
| 2647 | -#line 645 "pikchr.y" | |
| 2700 | +#line 661 "pikchr.y" | |
| 2648 | 2701 | {pik_same(p,0,&yymsp[0].minor.yy0);} |
| 2649 | -#line 2674 "pikchr.c" | |
| 2702 | +#line 2702 "pikchr.c" | |
| 2650 | 2703 | break; |
| 2651 | 2704 | case 46: /* attribute ::= SAME AS object */ |
| 2652 | -#line 646 "pikchr.y" | |
| 2653 | -{pik_same(p,yymsp[0].minor.yy162,&yymsp[-2].minor.yy0);} | |
| 2654 | -#line 2679 "pikchr.c" | |
| 2705 | +#line 662 "pikchr.y" | |
| 2706 | +{pik_same(p,yymsp[0].minor.yy54,&yymsp[-2].minor.yy0);} | |
| 2707 | +#line 2707 "pikchr.c" | |
| 2655 | 2708 | break; |
| 2656 | 2709 | case 47: /* attribute ::= STRING textposition */ |
| 2657 | -#line 647 "pikchr.y" | |
| 2658 | -{pik_add_txt(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy188);} | |
| 2659 | -#line 2684 "pikchr.c" | |
| 2710 | +#line 663 "pikchr.y" | |
| 2711 | +{pik_add_txt(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy272);} | |
| 2712 | +#line 2712 "pikchr.c" | |
| 2660 | 2713 | break; |
| 2661 | 2714 | case 48: /* attribute ::= FIT */ |
| 2662 | -#line 648 "pikchr.y" | |
| 2663 | -{pik_size_to_fit(p,&yymsp[0].minor.yy0,3); } | |
| 2664 | -#line 2689 "pikchr.c" | |
| 2715 | +#line 664 "pikchr.y" | |
| 2716 | +{pik_size_to_fit(p,0,&yymsp[0].minor.yy0,3); } | |
| 2717 | +#line 2717 "pikchr.c" | |
| 2665 | 2718 | break; |
| 2666 | 2719 | case 49: /* attribute ::= BEHIND object */ |
| 2667 | -#line 649 "pikchr.y" | |
| 2668 | -{pik_behind(p,yymsp[0].minor.yy162);} | |
| 2669 | -#line 2694 "pikchr.c" | |
| 2720 | +#line 665 "pikchr.y" | |
| 2721 | +{pik_behind(p,yymsp[0].minor.yy54);} | |
| 2722 | +#line 2722 "pikchr.c" | |
| 2670 | 2723 | break; |
| 2671 | 2724 | case 50: /* withclause ::= DOT_E edge AT position */ |
| 2672 | 2725 | case 51: /* withclause ::= edge AT position */ yytestcase(yyruleno==51); |
| 2673 | -#line 657 "pikchr.y" | |
| 2674 | -{ pik_set_at(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy63,&yymsp[-1].minor.yy0); } | |
| 2675 | -#line 2700 "pikchr.c" | |
| 2726 | +#line 673 "pikchr.y" | |
| 2727 | +{ pik_set_at(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy187,&yymsp[-1].minor.yy0); } | |
| 2728 | +#line 2728 "pikchr.c" | |
| 2676 | 2729 | break; |
| 2677 | 2730 | case 52: /* numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */ |
| 2678 | -#line 661 "pikchr.y" | |
| 2731 | +#line 677 "pikchr.y" | |
| 2679 | 2732 | {yylhsminor.yy0 = yymsp[0].minor.yy0;} |
| 2680 | -#line 2705 "pikchr.c" | |
| 2733 | +#line 2733 "pikchr.c" | |
| 2681 | 2734 | yymsp[0].minor.yy0 = yylhsminor.yy0; |
| 2682 | 2735 | break; |
| 2683 | 2736 | case 53: /* boolproperty ::= CW */ |
| 2684 | -#line 672 "pikchr.y" | |
| 2737 | +#line 688 "pikchr.y" | |
| 2685 | 2738 | {p->cur->cw = 1;} |
| 2686 | -#line 2711 "pikchr.c" | |
| 2739 | +#line 2739 "pikchr.c" | |
| 2687 | 2740 | break; |
| 2688 | 2741 | case 54: /* boolproperty ::= CCW */ |
| 2689 | -#line 673 "pikchr.y" | |
| 2742 | +#line 689 "pikchr.y" | |
| 2690 | 2743 | {p->cur->cw = 0;} |
| 2691 | -#line 2716 "pikchr.c" | |
| 2744 | +#line 2744 "pikchr.c" | |
| 2692 | 2745 | break; |
| 2693 | 2746 | case 55: /* boolproperty ::= LARROW */ |
| 2694 | -#line 674 "pikchr.y" | |
| 2747 | +#line 690 "pikchr.y" | |
| 2695 | 2748 | {p->cur->larrow=1; p->cur->rarrow=0; } |
| 2696 | -#line 2721 "pikchr.c" | |
| 2749 | +#line 2749 "pikchr.c" | |
| 2697 | 2750 | break; |
| 2698 | 2751 | case 56: /* boolproperty ::= RARROW */ |
| 2699 | -#line 675 "pikchr.y" | |
| 2752 | +#line 691 "pikchr.y" | |
| 2700 | 2753 | {p->cur->larrow=0; p->cur->rarrow=1; } |
| 2701 | -#line 2726 "pikchr.c" | |
| 2754 | +#line 2754 "pikchr.c" | |
| 2702 | 2755 | break; |
| 2703 | 2756 | case 57: /* boolproperty ::= LRARROW */ |
| 2704 | -#line 676 "pikchr.y" | |
| 2757 | +#line 692 "pikchr.y" | |
| 2705 | 2758 | {p->cur->larrow=1; p->cur->rarrow=1; } |
| 2706 | -#line 2731 "pikchr.c" | |
| 2759 | +#line 2759 "pikchr.c" | |
| 2707 | 2760 | break; |
| 2708 | 2761 | case 58: /* boolproperty ::= INVIS */ |
| 2709 | -#line 677 "pikchr.y" | |
| 2762 | +#line 693 "pikchr.y" | |
| 2710 | 2763 | {p->cur->sw = -0.00001;} |
| 2711 | -#line 2736 "pikchr.c" | |
| 2764 | +#line 2764 "pikchr.c" | |
| 2712 | 2765 | break; |
| 2713 | 2766 | case 59: /* boolproperty ::= THICK */ |
| 2714 | -#line 678 "pikchr.y" | |
| 2767 | +#line 694 "pikchr.y" | |
| 2715 | 2768 | {p->cur->sw *= 1.5;} |
| 2716 | -#line 2741 "pikchr.c" | |
| 2769 | +#line 2769 "pikchr.c" | |
| 2717 | 2770 | break; |
| 2718 | 2771 | case 60: /* boolproperty ::= THIN */ |
| 2719 | -#line 679 "pikchr.y" | |
| 2772 | +#line 695 "pikchr.y" | |
| 2720 | 2773 | {p->cur->sw *= 0.67;} |
| 2721 | -#line 2746 "pikchr.c" | |
| 2774 | +#line 2774 "pikchr.c" | |
| 2722 | 2775 | break; |
| 2723 | 2776 | case 61: /* boolproperty ::= SOLID */ |
| 2724 | -#line 680 "pikchr.y" | |
| 2777 | +#line 696 "pikchr.y" | |
| 2725 | 2778 | {p->cur->sw = pik_value(p,"thickness",9,0); |
| 2726 | 2779 | p->cur->dotted = p->cur->dashed = 0.0;} |
| 2727 | -#line 2752 "pikchr.c" | |
| 2780 | +#line 2780 "pikchr.c" | |
| 2728 | 2781 | break; |
| 2729 | 2782 | case 62: /* textposition ::= */ |
| 2730 | -#line 683 "pikchr.y" | |
| 2731 | -{yymsp[1].minor.yy188 = 0;} | |
| 2732 | -#line 2757 "pikchr.c" | |
| 2783 | +#line 699 "pikchr.y" | |
| 2784 | +{yymsp[1].minor.yy272 = 0;} | |
| 2785 | +#line 2785 "pikchr.c" | |
| 2733 | 2786 | break; |
| 2734 | 2787 | case 63: /* textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL */ |
| 2735 | -#line 686 "pikchr.y" | |
| 2736 | -{yylhsminor.yy188 = (short int)pik_text_position(yymsp[-1].minor.yy188,&yymsp[0].minor.yy0);} | |
| 2737 | -#line 2762 "pikchr.c" | |
| 2738 | - yymsp[-1].minor.yy188 = yylhsminor.yy188; | |
| 2788 | +#line 702 "pikchr.y" | |
| 2789 | +{yylhsminor.yy272 = (short int)pik_text_position(yymsp[-1].minor.yy272,&yymsp[0].minor.yy0);} | |
| 2790 | +#line 2790 "pikchr.c" | |
| 2791 | + yymsp[-1].minor.yy272 = yylhsminor.yy272; | |
| 2739 | 2792 | break; |
| 2740 | 2793 | case 64: /* position ::= expr COMMA expr */ |
| 2741 | -#line 689 "pikchr.y" | |
| 2742 | -{yylhsminor.yy63.x=yymsp[-2].minor.yy21; yylhsminor.yy63.y=yymsp[0].minor.yy21;} | |
| 2743 | -#line 2768 "pikchr.c" | |
| 2744 | - yymsp[-2].minor.yy63 = yylhsminor.yy63; | |
| 2794 | +#line 705 "pikchr.y" | |
| 2795 | +{yylhsminor.yy187.x=yymsp[-2].minor.yy129; yylhsminor.yy187.y=yymsp[0].minor.yy129;} | |
| 2796 | +#line 2796 "pikchr.c" | |
| 2797 | + yymsp[-2].minor.yy187 = yylhsminor.yy187; | |
| 2745 | 2798 | break; |
| 2746 | 2799 | case 65: /* position ::= place PLUS expr COMMA expr */ |
| 2747 | -#line 691 "pikchr.y" | |
| 2748 | -{yylhsminor.yy63.x=yymsp[-4].minor.yy63.x+yymsp[-2].minor.yy21; yylhsminor.yy63.y=yymsp[-4].minor.yy63.y+yymsp[0].minor.yy21;} | |
| 2749 | -#line 2774 "pikchr.c" | |
| 2750 | - yymsp[-4].minor.yy63 = yylhsminor.yy63; | |
| 2800 | +#line 707 "pikchr.y" | |
| 2801 | +{yylhsminor.yy187.x=yymsp[-4].minor.yy187.x+yymsp[-2].minor.yy129; yylhsminor.yy187.y=yymsp[-4].minor.yy187.y+yymsp[0].minor.yy129;} | |
| 2802 | +#line 2802 "pikchr.c" | |
| 2803 | + yymsp[-4].minor.yy187 = yylhsminor.yy187; | |
| 2751 | 2804 | break; |
| 2752 | 2805 | case 66: /* position ::= place MINUS expr COMMA expr */ |
| 2753 | -#line 692 "pikchr.y" | |
| 2754 | -{yylhsminor.yy63.x=yymsp[-4].minor.yy63.x-yymsp[-2].minor.yy21; yylhsminor.yy63.y=yymsp[-4].minor.yy63.y-yymsp[0].minor.yy21;} | |
| 2755 | -#line 2780 "pikchr.c" | |
| 2756 | - yymsp[-4].minor.yy63 = yylhsminor.yy63; | |
| 2806 | +#line 708 "pikchr.y" | |
| 2807 | +{yylhsminor.yy187.x=yymsp[-4].minor.yy187.x-yymsp[-2].minor.yy129; yylhsminor.yy187.y=yymsp[-4].minor.yy187.y-yymsp[0].minor.yy129;} | |
| 2808 | +#line 2808 "pikchr.c" | |
| 2809 | + yymsp[-4].minor.yy187 = yylhsminor.yy187; | |
| 2757 | 2810 | break; |
| 2758 | 2811 | case 67: /* position ::= place PLUS LP expr COMMA expr RP */ |
| 2759 | -#line 694 "pikchr.y" | |
| 2760 | -{yylhsminor.yy63.x=yymsp[-6].minor.yy63.x+yymsp[-3].minor.yy21; yylhsminor.yy63.y=yymsp[-6].minor.yy63.y+yymsp[-1].minor.yy21;} | |
| 2761 | -#line 2786 "pikchr.c" | |
| 2762 | - yymsp[-6].minor.yy63 = yylhsminor.yy63; | |
| 2812 | +#line 710 "pikchr.y" | |
| 2813 | +{yylhsminor.yy187.x=yymsp[-6].minor.yy187.x+yymsp[-3].minor.yy129; yylhsminor.yy187.y=yymsp[-6].minor.yy187.y+yymsp[-1].minor.yy129;} | |
| 2814 | +#line 2814 "pikchr.c" | |
| 2815 | + yymsp[-6].minor.yy187 = yylhsminor.yy187; | |
| 2763 | 2816 | break; |
| 2764 | 2817 | case 68: /* position ::= place MINUS LP expr COMMA expr RP */ |
| 2765 | -#line 696 "pikchr.y" | |
| 2766 | -{yylhsminor.yy63.x=yymsp[-6].minor.yy63.x-yymsp[-3].minor.yy21; yylhsminor.yy63.y=yymsp[-6].minor.yy63.y-yymsp[-1].minor.yy21;} | |
| 2767 | -#line 2792 "pikchr.c" | |
| 2768 | - yymsp[-6].minor.yy63 = yylhsminor.yy63; | |
| 2818 | +#line 712 "pikchr.y" | |
| 2819 | +{yylhsminor.yy187.x=yymsp[-6].minor.yy187.x-yymsp[-3].minor.yy129; yylhsminor.yy187.y=yymsp[-6].minor.yy187.y-yymsp[-1].minor.yy129;} | |
| 2820 | +#line 2820 "pikchr.c" | |
| 2821 | + yymsp[-6].minor.yy187 = yylhsminor.yy187; | |
| 2769 | 2822 | break; |
| 2770 | 2823 | case 69: /* position ::= LP position COMMA position RP */ |
| 2771 | -#line 697 "pikchr.y" | |
| 2772 | -{yymsp[-4].minor.yy63.x=yymsp[-3].minor.yy63.x; yymsp[-4].minor.yy63.y=yymsp[-1].minor.yy63.y;} | |
| 2773 | -#line 2798 "pikchr.c" | |
| 2824 | +#line 713 "pikchr.y" | |
| 2825 | +{yymsp[-4].minor.yy187.x=yymsp[-3].minor.yy187.x; yymsp[-4].minor.yy187.y=yymsp[-1].minor.yy187.y;} | |
| 2826 | +#line 2826 "pikchr.c" | |
| 2774 | 2827 | break; |
| 2775 | 2828 | case 70: /* position ::= LP position RP */ |
| 2776 | -#line 698 "pikchr.y" | |
| 2777 | -{yymsp[-2].minor.yy63=yymsp[-1].minor.yy63;} | |
| 2778 | -#line 2803 "pikchr.c" | |
| 2829 | +#line 714 "pikchr.y" | |
| 2830 | +{yymsp[-2].minor.yy187=yymsp[-1].minor.yy187;} | |
| 2831 | +#line 2831 "pikchr.c" | |
| 2779 | 2832 | break; |
| 2780 | 2833 | case 71: /* position ::= expr between position AND position */ |
| 2781 | -#line 700 "pikchr.y" | |
| 2782 | -{yylhsminor.yy63 = pik_position_between(yymsp[-4].minor.yy21,yymsp[-2].minor.yy63,yymsp[0].minor.yy63);} | |
| 2783 | -#line 2808 "pikchr.c" | |
| 2784 | - yymsp[-4].minor.yy63 = yylhsminor.yy63; | |
| 2834 | +#line 716 "pikchr.y" | |
| 2835 | +{yylhsminor.yy187 = pik_position_between(yymsp[-4].minor.yy129,yymsp[-2].minor.yy187,yymsp[0].minor.yy187);} | |
| 2836 | +#line 2836 "pikchr.c" | |
| 2837 | + yymsp[-4].minor.yy187 = yylhsminor.yy187; | |
| 2785 | 2838 | break; |
| 2786 | 2839 | case 72: /* position ::= expr LT position COMMA position GT */ |
| 2787 | -#line 702 "pikchr.y" | |
| 2788 | -{yylhsminor.yy63 = pik_position_between(yymsp[-5].minor.yy21,yymsp[-3].minor.yy63,yymsp[-1].minor.yy63);} | |
| 2789 | -#line 2814 "pikchr.c" | |
| 2790 | - yymsp[-5].minor.yy63 = yylhsminor.yy63; | |
| 2840 | +#line 718 "pikchr.y" | |
| 2841 | +{yylhsminor.yy187 = pik_position_between(yymsp[-5].minor.yy129,yymsp[-3].minor.yy187,yymsp[-1].minor.yy187);} | |
| 2842 | +#line 2842 "pikchr.c" | |
| 2843 | + yymsp[-5].minor.yy187 = yylhsminor.yy187; | |
| 2791 | 2844 | break; |
| 2792 | 2845 | case 73: /* position ::= expr ABOVE position */ |
| 2793 | -#line 703 "pikchr.y" | |
| 2794 | -{yylhsminor.yy63=yymsp[0].minor.yy63; yylhsminor.yy63.y += yymsp[-2].minor.yy21;} | |
| 2795 | -#line 2820 "pikchr.c" | |
| 2796 | - yymsp[-2].minor.yy63 = yylhsminor.yy63; | |
| 2846 | +#line 719 "pikchr.y" | |
| 2847 | +{yylhsminor.yy187=yymsp[0].minor.yy187; yylhsminor.yy187.y += yymsp[-2].minor.yy129;} | |
| 2848 | +#line 2848 "pikchr.c" | |
| 2849 | + yymsp[-2].minor.yy187 = yylhsminor.yy187; | |
| 2797 | 2850 | break; |
| 2798 | 2851 | case 74: /* position ::= expr BELOW position */ |
| 2799 | -#line 704 "pikchr.y" | |
| 2800 | -{yylhsminor.yy63=yymsp[0].minor.yy63; yylhsminor.yy63.y -= yymsp[-2].minor.yy21;} | |
| 2801 | -#line 2826 "pikchr.c" | |
| 2802 | - yymsp[-2].minor.yy63 = yylhsminor.yy63; | |
| 2852 | +#line 720 "pikchr.y" | |
| 2853 | +{yylhsminor.yy187=yymsp[0].minor.yy187; yylhsminor.yy187.y -= yymsp[-2].minor.yy129;} | |
| 2854 | +#line 2854 "pikchr.c" | |
| 2855 | + yymsp[-2].minor.yy187 = yylhsminor.yy187; | |
| 2803 | 2856 | break; |
| 2804 | 2857 | case 75: /* position ::= expr LEFT OF position */ |
| 2805 | -#line 705 "pikchr.y" | |
| 2806 | -{yylhsminor.yy63=yymsp[0].minor.yy63; yylhsminor.yy63.x -= yymsp[-3].minor.yy21;} | |
| 2807 | -#line 2832 "pikchr.c" | |
| 2808 | - yymsp[-3].minor.yy63 = yylhsminor.yy63; | |
| 2858 | +#line 721 "pikchr.y" | |
| 2859 | +{yylhsminor.yy187=yymsp[0].minor.yy187; yylhsminor.yy187.x -= yymsp[-3].minor.yy129;} | |
| 2860 | +#line 2860 "pikchr.c" | |
| 2861 | + yymsp[-3].minor.yy187 = yylhsminor.yy187; | |
| 2809 | 2862 | break; |
| 2810 | 2863 | case 76: /* position ::= expr RIGHT OF position */ |
| 2811 | -#line 706 "pikchr.y" | |
| 2812 | -{yylhsminor.yy63=yymsp[0].minor.yy63; yylhsminor.yy63.x += yymsp[-3].minor.yy21;} | |
| 2813 | -#line 2838 "pikchr.c" | |
| 2814 | - yymsp[-3].minor.yy63 = yylhsminor.yy63; | |
| 2864 | +#line 722 "pikchr.y" | |
| 2865 | +{yylhsminor.yy187=yymsp[0].minor.yy187; yylhsminor.yy187.x += yymsp[-3].minor.yy129;} | |
| 2866 | +#line 2866 "pikchr.c" | |
| 2867 | + yymsp[-3].minor.yy187 = yylhsminor.yy187; | |
| 2815 | 2868 | break; |
| 2816 | 2869 | case 77: /* position ::= expr ON HEADING EDGEPT OF position */ |
| 2817 | -#line 708 "pikchr.y" | |
| 2818 | -{yylhsminor.yy63 = pik_position_at_hdg(yymsp[-5].minor.yy21,&yymsp[-2].minor.yy0,yymsp[0].minor.yy63);} | |
| 2819 | -#line 2844 "pikchr.c" | |
| 2820 | - yymsp[-5].minor.yy63 = yylhsminor.yy63; | |
| 2870 | +#line 724 "pikchr.y" | |
| 2871 | +{yylhsminor.yy187 = pik_position_at_hdg(yymsp[-5].minor.yy129,&yymsp[-2].minor.yy0,yymsp[0].minor.yy187);} | |
| 2872 | +#line 2872 "pikchr.c" | |
| 2873 | + yymsp[-5].minor.yy187 = yylhsminor.yy187; | |
| 2821 | 2874 | break; |
| 2822 | 2875 | case 78: /* position ::= expr HEADING EDGEPT OF position */ |
| 2823 | -#line 710 "pikchr.y" | |
| 2824 | -{yylhsminor.yy63 = pik_position_at_hdg(yymsp[-4].minor.yy21,&yymsp[-2].minor.yy0,yymsp[0].minor.yy63);} | |
| 2825 | -#line 2850 "pikchr.c" | |
| 2826 | - yymsp[-4].minor.yy63 = yylhsminor.yy63; | |
| 2876 | +#line 726 "pikchr.y" | |
| 2877 | +{yylhsminor.yy187 = pik_position_at_hdg(yymsp[-4].minor.yy129,&yymsp[-2].minor.yy0,yymsp[0].minor.yy187);} | |
| 2878 | +#line 2878 "pikchr.c" | |
| 2879 | + yymsp[-4].minor.yy187 = yylhsminor.yy187; | |
| 2827 | 2880 | break; |
| 2828 | 2881 | case 79: /* position ::= expr EDGEPT OF position */ |
| 2829 | -#line 712 "pikchr.y" | |
| 2830 | -{yylhsminor.yy63 = pik_position_at_hdg(yymsp[-3].minor.yy21,&yymsp[-2].minor.yy0,yymsp[0].minor.yy63);} | |
| 2831 | -#line 2856 "pikchr.c" | |
| 2832 | - yymsp[-3].minor.yy63 = yylhsminor.yy63; | |
| 2882 | +#line 728 "pikchr.y" | |
| 2883 | +{yylhsminor.yy187 = pik_position_at_hdg(yymsp[-3].minor.yy129,&yymsp[-2].minor.yy0,yymsp[0].minor.yy187);} | |
| 2884 | +#line 2884 "pikchr.c" | |
| 2885 | + yymsp[-3].minor.yy187 = yylhsminor.yy187; | |
| 2833 | 2886 | break; |
| 2834 | 2887 | case 80: /* position ::= expr ON HEADING expr FROM position */ |
| 2835 | -#line 714 "pikchr.y" | |
| 2836 | -{yylhsminor.yy63 = pik_position_at_angle(yymsp[-5].minor.yy21,yymsp[-2].minor.yy21,yymsp[0].minor.yy63);} | |
| 2837 | -#line 2862 "pikchr.c" | |
| 2838 | - yymsp[-5].minor.yy63 = yylhsminor.yy63; | |
| 2888 | +#line 730 "pikchr.y" | |
| 2889 | +{yylhsminor.yy187 = pik_position_at_angle(yymsp[-5].minor.yy129,yymsp[-2].minor.yy129,yymsp[0].minor.yy187);} | |
| 2890 | +#line 2890 "pikchr.c" | |
| 2891 | + yymsp[-5].minor.yy187 = yylhsminor.yy187; | |
| 2839 | 2892 | break; |
| 2840 | 2893 | case 81: /* position ::= expr HEADING expr FROM position */ |
| 2841 | -#line 716 "pikchr.y" | |
| 2842 | -{yylhsminor.yy63 = pik_position_at_angle(yymsp[-4].minor.yy21,yymsp[-2].minor.yy21,yymsp[0].minor.yy63);} | |
| 2843 | -#line 2868 "pikchr.c" | |
| 2844 | - yymsp[-4].minor.yy63 = yylhsminor.yy63; | |
| 2894 | +#line 732 "pikchr.y" | |
| 2895 | +{yylhsminor.yy187 = pik_position_at_angle(yymsp[-4].minor.yy129,yymsp[-2].minor.yy129,yymsp[0].minor.yy187);} | |
| 2896 | +#line 2896 "pikchr.c" | |
| 2897 | + yymsp[-4].minor.yy187 = yylhsminor.yy187; | |
| 2845 | 2898 | break; |
| 2846 | 2899 | case 82: /* place ::= edge OF object */ |
| 2847 | -#line 728 "pikchr.y" | |
| 2848 | -{yylhsminor.yy63 = pik_place_of_elem(p,yymsp[0].minor.yy162,&yymsp[-2].minor.yy0);} | |
| 2849 | -#line 2874 "pikchr.c" | |
| 2850 | - yymsp[-2].minor.yy63 = yylhsminor.yy63; | |
| 2900 | +#line 744 "pikchr.y" | |
| 2901 | +{yylhsminor.yy187 = pik_place_of_elem(p,yymsp[0].minor.yy54,&yymsp[-2].minor.yy0);} | |
| 2902 | +#line 2902 "pikchr.c" | |
| 2903 | + yymsp[-2].minor.yy187 = yylhsminor.yy187; | |
| 2851 | 2904 | break; |
| 2852 | 2905 | case 83: /* place2 ::= object */ |
| 2853 | -#line 729 "pikchr.y" | |
| 2854 | -{yylhsminor.yy63 = pik_place_of_elem(p,yymsp[0].minor.yy162,0);} | |
| 2855 | -#line 2880 "pikchr.c" | |
| 2856 | - yymsp[0].minor.yy63 = yylhsminor.yy63; | |
| 2906 | +#line 745 "pikchr.y" | |
| 2907 | +{yylhsminor.yy187 = pik_place_of_elem(p,yymsp[0].minor.yy54,0);} | |
| 2908 | +#line 2908 "pikchr.c" | |
| 2909 | + yymsp[0].minor.yy187 = yylhsminor.yy187; | |
| 2857 | 2910 | break; |
| 2858 | 2911 | case 84: /* place2 ::= object DOT_E edge */ |
| 2859 | -#line 730 "pikchr.y" | |
| 2860 | -{yylhsminor.yy63 = pik_place_of_elem(p,yymsp[-2].minor.yy162,&yymsp[0].minor.yy0);} | |
| 2861 | -#line 2886 "pikchr.c" | |
| 2862 | - yymsp[-2].minor.yy63 = yylhsminor.yy63; | |
| 2912 | +#line 746 "pikchr.y" | |
| 2913 | +{yylhsminor.yy187 = pik_place_of_elem(p,yymsp[-2].minor.yy54,&yymsp[0].minor.yy0);} | |
| 2914 | +#line 2914 "pikchr.c" | |
| 2915 | + yymsp[-2].minor.yy187 = yylhsminor.yy187; | |
| 2863 | 2916 | break; |
| 2864 | 2917 | case 85: /* place2 ::= NTH VERTEX OF object */ |
| 2865 | -#line 731 "pikchr.y" | |
| 2866 | -{yylhsminor.yy63 = pik_nth_vertex(p,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,yymsp[0].minor.yy162);} | |
| 2867 | -#line 2892 "pikchr.c" | |
| 2868 | - yymsp[-3].minor.yy63 = yylhsminor.yy63; | |
| 2918 | +#line 747 "pikchr.y" | |
| 2919 | +{yylhsminor.yy187 = pik_nth_vertex(p,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,yymsp[0].minor.yy54);} | |
| 2920 | +#line 2920 "pikchr.c" | |
| 2921 | + yymsp[-3].minor.yy187 = yylhsminor.yy187; | |
| 2869 | 2922 | break; |
| 2870 | 2923 | case 86: /* object ::= nth */ |
| 2871 | -#line 743 "pikchr.y" | |
| 2872 | -{yylhsminor.yy162 = pik_find_nth(p,0,&yymsp[0].minor.yy0);} | |
| 2873 | -#line 2898 "pikchr.c" | |
| 2874 | - yymsp[0].minor.yy162 = yylhsminor.yy162; | |
| 2924 | +#line 759 "pikchr.y" | |
| 2925 | +{yylhsminor.yy54 = pik_find_nth(p,0,&yymsp[0].minor.yy0);} | |
| 2926 | +#line 2926 "pikchr.c" | |
| 2927 | + yymsp[0].minor.yy54 = yylhsminor.yy54; | |
| 2875 | 2928 | break; |
| 2876 | 2929 | case 87: /* object ::= nth OF|IN object */ |
| 2877 | -#line 744 "pikchr.y" | |
| 2878 | -{yylhsminor.yy162 = pik_find_nth(p,yymsp[0].minor.yy162,&yymsp[-2].minor.yy0);} | |
| 2879 | -#line 2904 "pikchr.c" | |
| 2880 | - yymsp[-2].minor.yy162 = yylhsminor.yy162; | |
| 2930 | +#line 760 "pikchr.y" | |
| 2931 | +{yylhsminor.yy54 = pik_find_nth(p,yymsp[0].minor.yy54,&yymsp[-2].minor.yy0);} | |
| 2932 | +#line 2932 "pikchr.c" | |
| 2933 | + yymsp[-2].minor.yy54 = yylhsminor.yy54; | |
| 2881 | 2934 | break; |
| 2882 | 2935 | case 88: /* objectname ::= THIS */ |
| 2883 | -#line 746 "pikchr.y" | |
| 2884 | -{yymsp[0].minor.yy162 = p->cur;} | |
| 2885 | -#line 2910 "pikchr.c" | |
| 2936 | +#line 762 "pikchr.y" | |
| 2937 | +{yymsp[0].minor.yy54 = p->cur;} | |
| 2938 | +#line 2938 "pikchr.c" | |
| 2886 | 2939 | break; |
| 2887 | 2940 | case 89: /* objectname ::= PLACENAME */ |
| 2888 | -#line 747 "pikchr.y" | |
| 2889 | -{yylhsminor.yy162 = pik_find_byname(p,0,&yymsp[0].minor.yy0);} | |
| 2890 | -#line 2915 "pikchr.c" | |
| 2891 | - yymsp[0].minor.yy162 = yylhsminor.yy162; | |
| 2941 | +#line 763 "pikchr.y" | |
| 2942 | +{yylhsminor.yy54 = pik_find_byname(p,0,&yymsp[0].minor.yy0);} | |
| 2943 | +#line 2943 "pikchr.c" | |
| 2944 | + yymsp[0].minor.yy54 = yylhsminor.yy54; | |
| 2892 | 2945 | break; |
| 2893 | 2946 | case 90: /* objectname ::= objectname DOT_U PLACENAME */ |
| 2894 | -#line 749 "pikchr.y" | |
| 2895 | -{yylhsminor.yy162 = pik_find_byname(p,yymsp[-2].minor.yy162,&yymsp[0].minor.yy0);} | |
| 2896 | -#line 2921 "pikchr.c" | |
| 2897 | - yymsp[-2].minor.yy162 = yylhsminor.yy162; | |
| 2947 | +#line 765 "pikchr.y" | |
| 2948 | +{yylhsminor.yy54 = pik_find_byname(p,yymsp[-2].minor.yy54,&yymsp[0].minor.yy0);} | |
| 2949 | +#line 2949 "pikchr.c" | |
| 2950 | + yymsp[-2].minor.yy54 = yylhsminor.yy54; | |
| 2898 | 2951 | break; |
| 2899 | 2952 | case 91: /* nth ::= NTH CLASSNAME */ |
| 2900 | -#line 751 "pikchr.y" | |
| 2953 | +#line 767 "pikchr.y" | |
| 2901 | 2954 | {yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = pik_nth_value(p,&yymsp[-1].minor.yy0); } |
| 2902 | -#line 2927 "pikchr.c" | |
| 2955 | +#line 2955 "pikchr.c" | |
| 2903 | 2956 | yymsp[-1].minor.yy0 = yylhsminor.yy0; |
| 2904 | 2957 | break; |
| 2905 | 2958 | case 92: /* nth ::= NTH LAST CLASSNAME */ |
| 2906 | -#line 752 "pikchr.y" | |
| 2959 | +#line 768 "pikchr.y" | |
| 2907 | 2960 | {yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -pik_nth_value(p,&yymsp[-2].minor.yy0); } |
| 2908 | -#line 2933 "pikchr.c" | |
| 2961 | +#line 2961 "pikchr.c" | |
| 2909 | 2962 | yymsp[-2].minor.yy0 = yylhsminor.yy0; |
| 2910 | 2963 | break; |
| 2911 | 2964 | case 93: /* nth ::= LAST CLASSNAME */ |
| 2912 | -#line 753 "pikchr.y" | |
| 2965 | +#line 769 "pikchr.y" | |
| 2913 | 2966 | {yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.eCode = -1;} |
| 2914 | -#line 2939 "pikchr.c" | |
| 2967 | +#line 2967 "pikchr.c" | |
| 2915 | 2968 | break; |
| 2916 | 2969 | case 94: /* nth ::= LAST */ |
| 2917 | -#line 754 "pikchr.y" | |
| 2970 | +#line 770 "pikchr.y" | |
| 2918 | 2971 | {yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -1;} |
| 2919 | -#line 2944 "pikchr.c" | |
| 2972 | +#line 2972 "pikchr.c" | |
| 2920 | 2973 | yymsp[0].minor.yy0 = yylhsminor.yy0; |
| 2921 | 2974 | break; |
| 2922 | 2975 | case 95: /* nth ::= NTH LB RB */ |
| 2923 | -#line 755 "pikchr.y" | |
| 2976 | +#line 771 "pikchr.y" | |
| 2924 | 2977 | {yylhsminor.yy0=yymsp[-1].minor.yy0; yylhsminor.yy0.eCode = pik_nth_value(p,&yymsp[-2].minor.yy0);} |
| 2925 | -#line 2950 "pikchr.c" | |
| 2978 | +#line 2978 "pikchr.c" | |
| 2926 | 2979 | yymsp[-2].minor.yy0 = yylhsminor.yy0; |
| 2927 | 2980 | break; |
| 2928 | 2981 | case 96: /* nth ::= NTH LAST LB RB */ |
| 2929 | -#line 756 "pikchr.y" | |
| 2982 | +#line 772 "pikchr.y" | |
| 2930 | 2983 | {yylhsminor.yy0=yymsp[-1].minor.yy0; yylhsminor.yy0.eCode = -pik_nth_value(p,&yymsp[-3].minor.yy0);} |
| 2931 | -#line 2956 "pikchr.c" | |
| 2984 | +#line 2984 "pikchr.c" | |
| 2932 | 2985 | yymsp[-3].minor.yy0 = yylhsminor.yy0; |
| 2933 | 2986 | break; |
| 2934 | 2987 | case 97: /* nth ::= LAST LB RB */ |
| 2935 | -#line 757 "pikchr.y" | |
| 2988 | +#line 773 "pikchr.y" | |
| 2936 | 2989 | {yymsp[-2].minor.yy0=yymsp[-1].minor.yy0; yymsp[-2].minor.yy0.eCode = -1; } |
| 2937 | -#line 2962 "pikchr.c" | |
| 2990 | +#line 2990 "pikchr.c" | |
| 2938 | 2991 | break; |
| 2939 | 2992 | case 98: /* expr ::= expr PLUS expr */ |
| 2940 | -#line 759 "pikchr.y" | |
| 2941 | -{yylhsminor.yy21=yymsp[-2].minor.yy21+yymsp[0].minor.yy21;} | |
| 2942 | -#line 2967 "pikchr.c" | |
| 2943 | - yymsp[-2].minor.yy21 = yylhsminor.yy21; | |
| 2993 | +#line 775 "pikchr.y" | |
| 2994 | +{yylhsminor.yy129=yymsp[-2].minor.yy129+yymsp[0].minor.yy129;} | |
| 2995 | +#line 2995 "pikchr.c" | |
| 2996 | + yymsp[-2].minor.yy129 = yylhsminor.yy129; | |
| 2944 | 2997 | break; |
| 2945 | 2998 | case 99: /* expr ::= expr MINUS expr */ |
| 2946 | -#line 760 "pikchr.y" | |
| 2947 | -{yylhsminor.yy21=yymsp[-2].minor.yy21-yymsp[0].minor.yy21;} | |
| 2948 | -#line 2973 "pikchr.c" | |
| 2949 | - yymsp[-2].minor.yy21 = yylhsminor.yy21; | |
| 2999 | +#line 776 "pikchr.y" | |
| 3000 | +{yylhsminor.yy129=yymsp[-2].minor.yy129-yymsp[0].minor.yy129;} | |
| 3001 | +#line 3001 "pikchr.c" | |
| 3002 | + yymsp[-2].minor.yy129 = yylhsminor.yy129; | |
| 2950 | 3003 | break; |
| 2951 | 3004 | case 100: /* expr ::= expr STAR expr */ |
| 2952 | -#line 761 "pikchr.y" | |
| 2953 | -{yylhsminor.yy21=yymsp[-2].minor.yy21*yymsp[0].minor.yy21;} | |
| 2954 | -#line 2979 "pikchr.c" | |
| 2955 | - yymsp[-2].minor.yy21 = yylhsminor.yy21; | |
| 3005 | +#line 777 "pikchr.y" | |
| 3006 | +{yylhsminor.yy129=yymsp[-2].minor.yy129*yymsp[0].minor.yy129;} | |
| 3007 | +#line 3007 "pikchr.c" | |
| 3008 | + yymsp[-2].minor.yy129 = yylhsminor.yy129; | |
| 2956 | 3009 | break; |
| 2957 | 3010 | case 101: /* expr ::= expr SLASH expr */ |
| 2958 | -#line 762 "pikchr.y" | |
| 3011 | +#line 778 "pikchr.y" | |
| 2959 | 3012 | { |
| 2960 | - if( yymsp[0].minor.yy21==0.0 ){ pik_error(p, &yymsp[-1].minor.yy0, "division by zero"); yylhsminor.yy21 = 0.0; } | |
| 2961 | - else{ yylhsminor.yy21 = yymsp[-2].minor.yy21/yymsp[0].minor.yy21; } | |
| 3013 | + if( yymsp[0].minor.yy129==0.0 ){ pik_error(p, &yymsp[-1].minor.yy0, "division by zero"); yylhsminor.yy129 = 0.0; } | |
| 3014 | + else{ yylhsminor.yy129 = yymsp[-2].minor.yy129/yymsp[0].minor.yy129; } | |
| 2962 | 3015 | } |
| 2963 | -#line 2988 "pikchr.c" | |
| 2964 | - yymsp[-2].minor.yy21 = yylhsminor.yy21; | |
| 3016 | +#line 3016 "pikchr.c" | |
| 3017 | + yymsp[-2].minor.yy129 = yylhsminor.yy129; | |
| 2965 | 3018 | break; |
| 2966 | 3019 | case 102: /* expr ::= MINUS expr */ |
| 2967 | -#line 766 "pikchr.y" | |
| 2968 | -{yymsp[-1].minor.yy21=-yymsp[0].minor.yy21;} | |
| 2969 | -#line 2994 "pikchr.c" | |
| 3020 | +#line 782 "pikchr.y" | |
| 3021 | +{yymsp[-1].minor.yy129=-yymsp[0].minor.yy129;} | |
| 3022 | +#line 3022 "pikchr.c" | |
| 2970 | 3023 | break; |
| 2971 | 3024 | case 103: /* expr ::= PLUS expr */ |
| 2972 | -#line 767 "pikchr.y" | |
| 2973 | -{yymsp[-1].minor.yy21=yymsp[0].minor.yy21;} | |
| 2974 | -#line 2999 "pikchr.c" | |
| 3025 | +#line 783 "pikchr.y" | |
| 3026 | +{yymsp[-1].minor.yy129=yymsp[0].minor.yy129;} | |
| 3027 | +#line 3027 "pikchr.c" | |
| 2975 | 3028 | break; |
| 2976 | 3029 | case 104: /* expr ::= LP expr RP */ |
| 2977 | -#line 768 "pikchr.y" | |
| 2978 | -{yymsp[-2].minor.yy21=yymsp[-1].minor.yy21;} | |
| 2979 | -#line 3004 "pikchr.c" | |
| 3030 | +#line 784 "pikchr.y" | |
| 3031 | +{yymsp[-2].minor.yy129=yymsp[-1].minor.yy129;} | |
| 3032 | +#line 3032 "pikchr.c" | |
| 2980 | 3033 | break; |
| 2981 | 3034 | case 105: /* expr ::= LP FILL|COLOR|THICKNESS RP */ |
| 2982 | -#line 769 "pikchr.y" | |
| 2983 | -{yymsp[-2].minor.yy21=pik_get_var(p,&yymsp[-1].minor.yy0);} | |
| 2984 | -#line 3009 "pikchr.c" | |
| 3035 | +#line 785 "pikchr.y" | |
| 3036 | +{yymsp[-2].minor.yy129=pik_get_var(p,&yymsp[-1].minor.yy0);} | |
| 3037 | +#line 3037 "pikchr.c" | |
| 2985 | 3038 | break; |
| 2986 | 3039 | case 106: /* expr ::= NUMBER */ |
| 2987 | -#line 770 "pikchr.y" | |
| 2988 | -{yylhsminor.yy21=pik_atof(&yymsp[0].minor.yy0);} | |
| 2989 | -#line 3014 "pikchr.c" | |
| 2990 | - yymsp[0].minor.yy21 = yylhsminor.yy21; | |
| 3040 | +#line 786 "pikchr.y" | |
| 3041 | +{yylhsminor.yy129=pik_atof(&yymsp[0].minor.yy0);} | |
| 3042 | +#line 3042 "pikchr.c" | |
| 3043 | + yymsp[0].minor.yy129 = yylhsminor.yy129; | |
| 2991 | 3044 | break; |
| 2992 | 3045 | case 107: /* expr ::= ID */ |
| 2993 | -#line 771 "pikchr.y" | |
| 2994 | -{yylhsminor.yy21=pik_get_var(p,&yymsp[0].minor.yy0);} | |
| 2995 | -#line 3020 "pikchr.c" | |
| 2996 | - yymsp[0].minor.yy21 = yylhsminor.yy21; | |
| 3046 | +#line 787 "pikchr.y" | |
| 3047 | +{yylhsminor.yy129=pik_get_var(p,&yymsp[0].minor.yy0);} | |
| 3048 | +#line 3048 "pikchr.c" | |
| 3049 | + yymsp[0].minor.yy129 = yylhsminor.yy129; | |
| 2997 | 3050 | break; |
| 2998 | 3051 | case 108: /* expr ::= FUNC1 LP expr RP */ |
| 2999 | -#line 772 "pikchr.y" | |
| 3000 | -{yylhsminor.yy21 = pik_func(p,&yymsp[-3].minor.yy0,yymsp[-1].minor.yy21,0.0);} | |
| 3001 | -#line 3026 "pikchr.c" | |
| 3002 | - yymsp[-3].minor.yy21 = yylhsminor.yy21; | |
| 3052 | +#line 788 "pikchr.y" | |
| 3053 | +{yylhsminor.yy129 = pik_func(p,&yymsp[-3].minor.yy0,yymsp[-1].minor.yy129,0.0);} | |
| 3054 | +#line 3054 "pikchr.c" | |
| 3055 | + yymsp[-3].minor.yy129 = yylhsminor.yy129; | |
| 3003 | 3056 | break; |
| 3004 | 3057 | case 109: /* expr ::= FUNC2 LP expr COMMA expr RP */ |
| 3005 | -#line 773 "pikchr.y" | |
| 3006 | -{yylhsminor.yy21 = pik_func(p,&yymsp[-5].minor.yy0,yymsp[-3].minor.yy21,yymsp[-1].minor.yy21);} | |
| 3007 | -#line 3032 "pikchr.c" | |
| 3008 | - yymsp[-5].minor.yy21 = yylhsminor.yy21; | |
| 3058 | +#line 789 "pikchr.y" | |
| 3059 | +{yylhsminor.yy129 = pik_func(p,&yymsp[-5].minor.yy0,yymsp[-3].minor.yy129,yymsp[-1].minor.yy129);} | |
| 3060 | +#line 3060 "pikchr.c" | |
| 3061 | + yymsp[-5].minor.yy129 = yylhsminor.yy129; | |
| 3009 | 3062 | break; |
| 3010 | 3063 | case 110: /* expr ::= DIST LP position COMMA position RP */ |
| 3011 | -#line 774 "pikchr.y" | |
| 3012 | -{yymsp[-5].minor.yy21 = pik_dist(&yymsp[-3].minor.yy63,&yymsp[-1].minor.yy63);} | |
| 3013 | -#line 3038 "pikchr.c" | |
| 3064 | +#line 790 "pikchr.y" | |
| 3065 | +{yymsp[-5].minor.yy129 = pik_dist(&yymsp[-3].minor.yy187,&yymsp[-1].minor.yy187);} | |
| 3066 | +#line 3066 "pikchr.c" | |
| 3014 | 3067 | break; |
| 3015 | 3068 | case 111: /* expr ::= place2 DOT_XY X */ |
| 3016 | -#line 775 "pikchr.y" | |
| 3017 | -{yylhsminor.yy21 = yymsp[-2].minor.yy63.x;} | |
| 3018 | -#line 3043 "pikchr.c" | |
| 3019 | - yymsp[-2].minor.yy21 = yylhsminor.yy21; | |
| 3069 | +#line 791 "pikchr.y" | |
| 3070 | +{yylhsminor.yy129 = yymsp[-2].minor.yy187.x;} | |
| 3071 | +#line 3071 "pikchr.c" | |
| 3072 | + yymsp[-2].minor.yy129 = yylhsminor.yy129; | |
| 3020 | 3073 | break; |
| 3021 | 3074 | case 112: /* expr ::= place2 DOT_XY Y */ |
| 3022 | -#line 776 "pikchr.y" | |
| 3023 | -{yylhsminor.yy21 = yymsp[-2].minor.yy63.y;} | |
| 3024 | -#line 3049 "pikchr.c" | |
| 3025 | - yymsp[-2].minor.yy21 = yylhsminor.yy21; | |
| 3075 | +#line 792 "pikchr.y" | |
| 3076 | +{yylhsminor.yy129 = yymsp[-2].minor.yy187.y;} | |
| 3077 | +#line 3077 "pikchr.c" | |
| 3078 | + yymsp[-2].minor.yy129 = yylhsminor.yy129; | |
| 3026 | 3079 | break; |
| 3027 | 3080 | case 113: /* expr ::= object DOT_L numproperty */ |
| 3028 | 3081 | case 114: /* expr ::= object DOT_L dashproperty */ yytestcase(yyruleno==114); |
| 3029 | 3082 | case 115: /* expr ::= object DOT_L colorproperty */ yytestcase(yyruleno==115); |
| 3030 | -#line 777 "pikchr.y" | |
| 3031 | -{yylhsminor.yy21=pik_property_of(yymsp[-2].minor.yy162,&yymsp[0].minor.yy0);} | |
| 3032 | -#line 3057 "pikchr.c" | |
| 3033 | - yymsp[-2].minor.yy21 = yylhsminor.yy21; | |
| 3083 | +#line 793 "pikchr.y" | |
| 3084 | +{yylhsminor.yy129=pik_property_of(yymsp[-2].minor.yy54,&yymsp[0].minor.yy0);} | |
| 3085 | +#line 3085 "pikchr.c" | |
| 3086 | + yymsp[-2].minor.yy129 = yylhsminor.yy129; | |
| 3034 | 3087 | break; |
| 3035 | 3088 | default: |
| 3036 | 3089 | /* (116) lvalue ::= ID */ yytestcase(yyruleno==116); |
| 3037 | 3090 | /* (117) lvalue ::= FILL */ yytestcase(yyruleno==117); |
| 3038 | 3091 | /* (118) lvalue ::= COLOR */ yytestcase(yyruleno==118); |
| @@ -3130,19 +3183,19 @@ | ||
| 3130 | 3183 | ){ |
| 3131 | 3184 | pik_parserARG_FETCH |
| 3132 | 3185 | pik_parserCTX_FETCH |
| 3133 | 3186 | #define TOKEN yyminor |
| 3134 | 3187 | /************ Begin %syntax_error code ****************************************/ |
| 3135 | -#line 537 "pikchr.y" | |
| 3188 | +#line 551 "pikchr.y" | |
| 3136 | 3189 | |
| 3137 | 3190 | if( TOKEN.z && TOKEN.z[0] ){ |
| 3138 | 3191 | pik_error(p, &TOKEN, "syntax error"); |
| 3139 | 3192 | }else{ |
| 3140 | 3193 | pik_error(p, 0, "syntax error"); |
| 3141 | 3194 | } |
| 3142 | 3195 | UNUSED_PARAMETER(yymajor); |
| 3143 | -#line 3168 "pikchr.c" | |
| 3196 | +#line 3196 "pikchr.c" | |
| 3144 | 3197 | /************ End %syntax_error code ******************************************/ |
| 3145 | 3198 | pik_parserARG_STORE /* Suppress warning about unused %extra_argument variable */ |
| 3146 | 3199 | pik_parserCTX_STORE |
| 3147 | 3200 | } |
| 3148 | 3201 | |
| @@ -3407,11 +3460,11 @@ | ||
| 3407 | 3460 | #else |
| 3408 | 3461 | (void)iToken; |
| 3409 | 3462 | return 0; |
| 3410 | 3463 | #endif |
| 3411 | 3464 | } |
| 3412 | -#line 782 "pikchr.y" | |
| 3465 | +#line 798 "pikchr.y" | |
| 3413 | 3466 | |
| 3414 | 3467 | |
| 3415 | 3468 | |
| 3416 | 3469 | /* Chart of the 148 official CSS color names with their |
| 3417 | 3470 | ** corresponding RGB values thru Color Module Level 4: |
| @@ -3634,42 +3687,57 @@ | ||
| 3634 | 3687 | ** than true arcs. Multiple reasons: (1) the legacy-PIC parameters |
| 3635 | 3688 | ** that control arcs are obscure and I could not figure out what they |
| 3636 | 3689 | ** mean based on available documentation. (2) Arcs are rarely used, |
| 3637 | 3690 | ** and so do not seem that important. |
| 3638 | 3691 | */ |
| 3639 | -static PPoint arcControlPoint(int cw, PPoint f, PPoint t, PNum rScale){ | |
| 3692 | +static PPoint arcControlPoint(int cw, PPoint f, PPoint t){ | |
| 3640 | 3693 | PPoint m; |
| 3641 | 3694 | PNum dx, dy; |
| 3642 | 3695 | m.x = 0.5*(f.x+t.x); |
| 3643 | 3696 | m.y = 0.5*(f.y+t.y); |
| 3644 | 3697 | dx = t.x - f.x; |
| 3645 | 3698 | dy = t.y - f.y; |
| 3646 | 3699 | if( cw ){ |
| 3647 | - m.x -= 0.5*rScale*dy; | |
| 3648 | - m.y += 0.5*rScale*dx; | |
| 3700 | + m.x -= 0.5*dy; | |
| 3701 | + m.y += 0.5*dx; | |
| 3649 | 3702 | }else{ |
| 3650 | - m.x += 0.5*rScale*dy; | |
| 3651 | - m.y -= 0.5*rScale*dx; | |
| 3703 | + m.x += 0.5*dy; | |
| 3704 | + m.y -= 0.5*dx; | |
| 3652 | 3705 | } |
| 3653 | 3706 | return m; |
| 3654 | 3707 | } |
| 3655 | 3708 | static void arcCheck(Pik *p, PObj *pObj){ |
| 3656 | - PPoint m; | |
| 3709 | + PPoint f, m, t; | |
| 3710 | + PNum sw; | |
| 3711 | + int i; | |
| 3657 | 3712 | if( p->nTPath>2 ){ |
| 3658 | 3713 | pik_error(p, &pObj->errTok, "arc geometry error"); |
| 3659 | 3714 | return; |
| 3660 | 3715 | } |
| 3661 | - m = arcControlPoint(pObj->cw, p->aTPath[0], p->aTPath[1], 0.5); | |
| 3662 | - pik_bbox_add_xy(&pObj->bbox, m.x, m.y); | |
| 3716 | + f = p->aTPath[0]; | |
| 3717 | + t = p->aTPath[1]; | |
| 3718 | + m = arcControlPoint(pObj->cw, f, t); | |
| 3719 | + sw = pObj->sw; | |
| 3720 | + for(i=1; i<16; i++){ | |
| 3721 | + PNum t1, t2, a, b, c, x, y; | |
| 3722 | + t1 = 0.0625*i; | |
| 3723 | + t2 = 1.0 - t1; | |
| 3724 | + a = t2*t2; | |
| 3725 | + b = 2*t1*t2; | |
| 3726 | + c = t1*t1; | |
| 3727 | + x = a*f.x + b*m.x + c*t.x; | |
| 3728 | + y = a*f.y + b*m.y + c*t.y; | |
| 3729 | + pik_bbox_addellipse(&pObj->bbox, x, y, sw, sw); | |
| 3730 | + } | |
| 3663 | 3731 | } |
| 3664 | 3732 | static void arcRender(Pik *p, PObj *pObj){ |
| 3665 | 3733 | PPoint f, m, t; |
| 3666 | 3734 | if( pObj->nPath<2 ) return; |
| 3667 | 3735 | if( pObj->sw<0.0 ) return; |
| 3668 | 3736 | f = pObj->aPath[0]; |
| 3669 | 3737 | t = pObj->aPath[1]; |
| 3670 | - m = arcControlPoint(pObj->cw,f,t,1.0); | |
| 3738 | + m = arcControlPoint(pObj->cw,f,t); | |
| 3671 | 3739 | if( pObj->larrow ){ |
| 3672 | 3740 | pik_draw_arrowhead(p,&m,&f,pObj); |
| 3673 | 3741 | } |
| 3674 | 3742 | if( pObj->rarrow ){ |
| 3675 | 3743 | pik_draw_arrowhead(p,&m,&t,pObj); |
| @@ -4338,11 +4406,11 @@ | ||
| 4338 | 4406 | static PPoint textOffset(Pik *p, PObj *pObj, int cp){ |
| 4339 | 4407 | /* Automatically slim-down the width and height of text |
| 4340 | 4408 | ** statements so that the bounding box tightly encloses the text, |
| 4341 | 4409 | ** then get boxOffset() to do the offset computation. |
| 4342 | 4410 | */ |
| 4343 | - pik_size_to_fit(p, &pObj->errTok,3); | |
| 4411 | + pik_size_to_fit(p, pObj, &pObj->errTok,3); | |
| 4344 | 4412 | return boxOffset(p, pObj, cp); |
| 4345 | 4413 | } |
| 4346 | 4414 | static void textRender(Pik *p, PObj *pObj){ |
| 4347 | 4415 | pik_append_txt(p, pObj, 0); |
| 4348 | 4416 | } |
| @@ -6347,16 +6415,15 @@ | ||
| 6347 | 6415 | ** |
| 6348 | 6416 | ** 1: Fit horizontally only |
| 6349 | 6417 | ** 2: Fit vertically only |
| 6350 | 6418 | ** 3: Fit both ways |
| 6351 | 6419 | */ |
| 6352 | -static void pik_size_to_fit(Pik *p, PToken *pFit, int eWhich){ | |
| 6353 | - PObj *pObj; | |
| 6420 | +static void pik_size_to_fit(Pik *p, PObj *pObj, PToken *pFit, int eWhich){ | |
| 6354 | 6421 | PNum w, h; |
| 6355 | 6422 | PBox bbox; |
| 6356 | 6423 | if( p->nErr ) return; |
| 6357 | - pObj = p->cur; | |
| 6424 | + if( pObj==0 ) pObj = p->cur; | |
| 6358 | 6425 | |
| 6359 | 6426 | if( pObj->nTxt==0 ){ |
| 6360 | 6427 | pik_error(0, pFit, "no text to fit to"); |
| 6361 | 6428 | return; |
| 6362 | 6429 | } |
| @@ -6495,13 +6562,13 @@ | ||
| 6495 | 6562 | unsigned int i; |
| 6496 | 6563 | mid = (first+last)/2; |
| 6497 | 6564 | zClr = aColor[mid].zName; |
| 6498 | 6565 | for(i=0; i<pId->n; i++){ |
| 6499 | 6566 | c1 = zClr[i]&0x7f; |
| 6500 | - if( isupper(c1) ) c1 = tolower(c1); | |
| 6567 | + if( IsUpper(c1) ) c1 = ToLower(c1); | |
| 6501 | 6568 | c2 = pId->z[i]&0x7f; |
| 6502 | - if( isupper(c2) ) c2 = tolower(c2); | |
| 6569 | + if( IsUpper(c2) ) c2 = ToLower(c2); | |
| 6503 | 6570 | c = c2 - c1; |
| 6504 | 6571 | if( c ) break; |
| 6505 | 6572 | } |
| 6506 | 6573 | if( c==0 && aColor[mid].zName[pId->n] ) c = -1; |
| 6507 | 6574 | if( c==0 ) return (double)aColor[mid].val; |
| @@ -6896,20 +6963,20 @@ | ||
| 6896 | 6963 | */ |
| 6897 | 6964 | if( pObj->h<=0.0 ){ |
| 6898 | 6965 | if( pObj->nTxt==0 ){ |
| 6899 | 6966 | pObj->h = 0.0; |
| 6900 | 6967 | }else if( pObj->w<=0.0 ){ |
| 6901 | - pik_size_to_fit(p, &pObj->errTok, 3); | |
| 6968 | + pik_size_to_fit(p, pObj, &pObj->errTok, 3); | |
| 6902 | 6969 | }else{ |
| 6903 | - pik_size_to_fit(p, &pObj->errTok, 2); | |
| 6970 | + pik_size_to_fit(p, pObj, &pObj->errTok, 2); | |
| 6904 | 6971 | } |
| 6905 | 6972 | } |
| 6906 | 6973 | if( pObj->w<=0.0 ){ |
| 6907 | 6974 | if( pObj->nTxt==0 ){ |
| 6908 | 6975 | pObj->w = 0.0; |
| 6909 | 6976 | }else{ |
| 6910 | - pik_size_to_fit(p, &pObj->errTok, 1); | |
| 6977 | + pik_size_to_fit(p, pObj, &pObj->errTok, 1); | |
| 6911 | 6978 | } |
| 6912 | 6979 | } |
| 6913 | 6980 | ofst = pik_elem_offset(p, pObj, pObj->eWith); |
| 6914 | 6981 | dx = (pObj->with.x - ofst.x) - pObj->ptAt.x; |
| 6915 | 6982 | dy = (pObj->with.y - ofst.y) - pObj->ptAt.y; |
| @@ -7235,11 +7302,12 @@ | ||
| 7235 | 7302 | pik_append_num(p, " width=\"", p->wSVG); |
| 7236 | 7303 | pik_append_num(p, "\" height=\"", p->hSVG); |
| 7237 | 7304 | pik_append(p, "\"", 1); |
| 7238 | 7305 | } |
| 7239 | 7306 | pik_append_dis(p, " viewBox=\"0 0 ",w,""); |
| 7240 | - pik_append_dis(p, " ",h,"\">\n"); | |
| 7307 | + pik_append_dis(p, " ",h,"\""); | |
| 7308 | + pik_append(p, " data-pikchr-date=\"" MANIFEST_ISODATE "\">\n", -1); | |
| 7241 | 7309 | pik_elist_render(p, pList); |
| 7242 | 7310 | pik_append(p,"</svg>\n", -1); |
| 7243 | 7311 | }else{ |
| 7244 | 7312 | p->wSVG = -1; |
| 7245 | 7313 | p->hSVG = -1; |
| @@ -7319,10 +7387,11 @@ | ||
| 7319 | 7387 | { "n", 1, T_EDGEPT, 0, CP_N }, |
| 7320 | 7388 | { "ne", 2, T_EDGEPT, 0, CP_NE }, |
| 7321 | 7389 | { "north", 5, T_EDGEPT, 0, CP_N }, |
| 7322 | 7390 | { "nw", 2, T_EDGEPT, 0, CP_NW }, |
| 7323 | 7391 | { "of", 2, T_OF, 0, 0 }, |
| 7392 | + { "pikchr_date",11, T_ISODATE, 0, 0, }, | |
| 7324 | 7393 | { "previous", 8, T_LAST, 0, 0, }, |
| 7325 | 7394 | { "print", 5, T_PRINT, 0, 0 }, |
| 7326 | 7395 | { "rad", 3, T_RADIUS, 0, 0 }, |
| 7327 | 7396 | { "radius", 6, T_RADIUS, 0, 0 }, |
| 7328 | 7397 | { "right", 5, T_RIGHT, DIR_RIGHT, CP_E }, |
| @@ -7605,11 +7674,11 @@ | ||
| 7605 | 7674 | } |
| 7606 | 7675 | default: { |
| 7607 | 7676 | c = z[0]; |
| 7608 | 7677 | if( c=='.' ){ |
| 7609 | 7678 | unsigned char c1 = z[1]; |
| 7610 | - if( islower(c1) ){ | |
| 7679 | + if( IsLower(c1) ){ | |
| 7611 | 7680 | const PikWord *pFound; |
| 7612 | 7681 | for(i=2; (c = z[i])>='a' && c<='z'; i++){} |
| 7613 | 7682 | pFound = pik_find_word((const char*)z+1, i-1, |
| 7614 | 7683 | pik_keywords, count(pik_keywords)); |
| 7615 | 7684 | if( pFound && (pFound->eEdge>0 || |
| @@ -7625,15 +7694,15 @@ | ||
| 7625 | 7694 | }else{ |
| 7626 | 7695 | /* Any other "dot" */ |
| 7627 | 7696 | pToken->eType = T_DOT_L; |
| 7628 | 7697 | } |
| 7629 | 7698 | return 1; |
| 7630 | - }else if( isdigit(c1) ){ | |
| 7699 | + }else if( IsDigit(c1) ){ | |
| 7631 | 7700 | i = 0; |
| 7632 | 7701 | /* no-op. Fall through to number handling */ |
| 7633 | - }else if( isupper(c1) ){ | |
| 7634 | - for(i=2; (c = z[i])!=0 && (isalnum(c) || c=='_'); i++){} | |
| 7702 | + }else if( IsUpper(c1) ){ | |
| 7703 | + for(i=2; (c = z[i])!=0 && (IsAlnum(c) || c=='_'); i++){} | |
| 7635 | 7704 | pToken->eType = T_DOT_U; |
| 7636 | 7705 | return 1; |
| 7637 | 7706 | }else{ |
| 7638 | 7707 | pToken->eType = T_ERROR; |
| 7639 | 7708 | return 1; |
| @@ -7644,11 +7713,11 @@ | ||
| 7644 | 7713 | int isInt = 1; |
| 7645 | 7714 | if( c!='.' ){ |
| 7646 | 7715 | nDigit = 1; |
| 7647 | 7716 | for(i=1; (c = z[i])>='0' && c<='9'; i++){ nDigit++; } |
| 7648 | 7717 | if( i==1 && (c=='x' || c=='X') ){ |
| 7649 | - for(i=2; (c = z[i])!=0 && isxdigit(c); i++){} | |
| 7718 | + for(i=2; (c = z[i])!=0 && IsXDigit(c); i++){} | |
| 7650 | 7719 | pToken->eType = T_NUMBER; |
| 7651 | 7720 | return i; |
| 7652 | 7721 | } |
| 7653 | 7722 | }else{ |
| 7654 | 7723 | isInt = 0; |
| @@ -7700,13 +7769,13 @@ | ||
| 7700 | 7769 | ){ |
| 7701 | 7770 | i += 2; |
| 7702 | 7771 | } |
| 7703 | 7772 | pToken->eType = T_NUMBER; |
| 7704 | 7773 | return i; |
| 7705 | - }else if( islower(c) ){ | |
| 7774 | + }else if( IsLower(c) ){ | |
| 7706 | 7775 | const PikWord *pFound; |
| 7707 | - for(i=1; (c = z[i])!=0 && (isalnum(c) || c=='_'); i++){} | |
| 7776 | + for(i=1; (c = z[i])!=0 && (IsAlnum(c) || c=='_'); i++){} | |
| 7708 | 7777 | pFound = pik_find_word((const char*)z, i, |
| 7709 | 7778 | pik_keywords, count(pik_keywords)); |
| 7710 | 7779 | if( pFound ){ |
| 7711 | 7780 | pToken->eType = pFound->eType; |
| 7712 | 7781 | pToken->eCode = pFound->eCode; |
| @@ -7719,19 +7788,19 @@ | ||
| 7719 | 7788 | }else{ |
| 7720 | 7789 | pToken->eType = T_ID; |
| 7721 | 7790 | } |
| 7722 | 7791 | return i; |
| 7723 | 7792 | }else if( c>='A' && c<='Z' ){ |
| 7724 | - for(i=1; (c = z[i])!=0 && (isalnum(c) || c=='_'); i++){} | |
| 7793 | + for(i=1; (c = z[i])!=0 && (IsAlnum(c) || c=='_'); i++){} | |
| 7725 | 7794 | pToken->eType = T_PLACENAME; |
| 7726 | 7795 | return i; |
| 7727 | - }else if( c=='$' && z[1]>='1' && z[1]<='9' && !isdigit(z[2]) ){ | |
| 7796 | + }else if( c=='$' && z[1]>='1' && z[1]<='9' && !IsDigit(z[2]) ){ | |
| 7728 | 7797 | pToken->eType = T_PARAMETER; |
| 7729 | 7798 | pToken->eCode = z[1] - '1'; |
| 7730 | 7799 | return 2; |
| 7731 | 7800 | }else if( c=='_' || c=='$' || c=='@' ){ |
| 7732 | - for(i=1; (c = z[i])!=0 && (isalnum(c) || c=='_'); i++){} | |
| 7801 | + for(i=1; (c = z[i])!=0 && (IsAlnum(c) || c=='_'); i++){} | |
| 7733 | 7802 | pToken->eType = T_ID; |
| 7734 | 7803 | return i; |
| 7735 | 7804 | }else{ |
| 7736 | 7805 | pToken->eType = T_ERROR; |
| 7737 | 7806 | return 1; |
| @@ -7814,12 +7883,12 @@ | ||
| 7814 | 7883 | /* Remove leading and trailing whitespace from each argument. |
| 7815 | 7884 | ** If what remains is one of $1, $2, ... $9 then transfer the |
| 7816 | 7885 | ** corresponding argument from the outer context */ |
| 7817 | 7886 | for(j=0; j<=nArg; j++){ |
| 7818 | 7887 | PToken *t = &args[j]; |
| 7819 | - while( t->n>0 && isspace(t->z[0]) ){ t->n--; t->z++; } | |
| 7820 | - while( t->n>0 && isspace(t->z[t->n-1]) ){ t->n--; } | |
| 7888 | + while( t->n>0 && IsSpace(t->z[0]) ){ t->n--; t->z++; } | |
| 7889 | + while( t->n>0 && IsSpace(t->z[t->n-1]) ){ t->n--; } | |
| 7821 | 7890 | if( t->n==2 && t->z[0]=='$' && t->z[1]>='1' && t->z[1]<='9' ){ |
| 7822 | 7891 | if( pOuter ) *t = pOuter[t->z[1]-'1']; |
| 7823 | 7892 | else t->n = 0; |
| 7824 | 7893 | } |
| 7825 | 7894 | } |
| @@ -7896,21 +7965,36 @@ | ||
| 7896 | 7965 | pMac->inUse = 0; |
| 7897 | 7966 | }else{ |
| 7898 | 7967 | #if 0 |
| 7899 | 7968 | printf("******** Token %s (%d): \"%.*s\" **************\n", |
| 7900 | 7969 | yyTokenName[token.eType], token.eType, |
| 7901 | - (int)(isspace(token.z[0]) ? 0 : sz), token.z); | |
| 7970 | + (int)(IsSpace(token.z[0]) ? 0 : sz), token.z); | |
| 7902 | 7971 | #endif |
| 7903 | 7972 | token.n = (unsigned short)(sz & 0xffff); |
| 7904 | 7973 | if( p->nToken++ > PIKCHR_TOKEN_LIMIT ){ |
| 7905 | 7974 | pik_error(p, &token, "script is too complex"); |
| 7906 | 7975 | break; |
| 7976 | + } | |
| 7977 | + if( token.eType==T_ISODATE ){ | |
| 7978 | + token.z = "\"" MANIFEST_ISODATE "\""; | |
| 7979 | + token.n = sizeof(MANIFEST_ISODATE)+1; | |
| 7980 | + token.eType = T_STRING; | |
| 7907 | 7981 | } |
| 7908 | 7982 | pik_parser(pParser, token.eType, token); |
| 7909 | 7983 | } |
| 7910 | 7984 | } |
| 7911 | 7985 | } |
| 7986 | + | |
| 7987 | +/* | |
| 7988 | +** Return the version name. | |
| 7989 | +*/ | |
| 7990 | +const char *pikchr_version(void) | |
| 7991 | + /* Emscripten workaround, else it chokes on the inlined version */; | |
| 7992 | + | |
| 7993 | +const char *pikchr_version(void){ | |
| 7994 | + return RELEASE_VERSION " " MANIFEST_ISODATE; | |
| 7995 | +} | |
| 7912 | 7996 | |
| 7913 | 7997 | /* |
| 7914 | 7998 | ** Parse the PIKCHR script contained in zText[]. Return a rendering. Or |
| 7915 | 7999 | ** if an error is encountered, return the error text. The error message |
| 7916 | 8000 | ** is HTML formatted. So regardless of what happens, the return text |
| @@ -8130,10 +8214,14 @@ | ||
| 8130 | 8214 | exit(1); |
| 8131 | 8215 | } |
| 8132 | 8216 | bSvgOnly = 1; |
| 8133 | 8217 | mFlags |= PIKCHR_PLAINTEXT_ERRORS; |
| 8134 | 8218 | }else |
| 8219 | + if( strcmp(z,"version")==0 || strcmp(z,"v")==0 ){ | |
| 8220 | + printf("pikchr %s\n", pikchr_version()); | |
| 8221 | + return 0; | |
| 8222 | + }else | |
| 8135 | 8223 | { |
| 8136 | 8224 | fprintf(stderr,"unknown option: \"%s\"\n", argv[i]); |
| 8137 | 8225 | usage(argv[0]); |
| 8138 | 8226 | } |
| 8139 | 8227 | continue; |
| @@ -8242,6 +8330,6 @@ | ||
| 8242 | 8330 | |
| 8243 | 8331 | |
| 8244 | 8332 | #endif /* PIKCHR_TCL */ |
| 8245 | 8333 | |
| 8246 | 8334 | |
| 8247 | -#line 8272 "pikchr.c" | |
| 8335 | +#line 8335 "pikchr.c" | |
| 8248 | 8336 |
| --- extsrc/pikchr.c | |
| +++ extsrc/pikchr.c | |
| @@ -1,8 +1,47 @@ | |
| 1 | /* This file is automatically generated by Lemon from input grammar |
| 2 | ** source file "pikchr.y". |
| 3 | */ |
| 4 | /* |
| 5 | ** Zero-Clause BSD license: |
| 6 | ** |
| 7 | ** Copyright (C) 2020-09-01 by D. Richard Hipp <[email protected]> |
| 8 | ** |
| @@ -125,10 +164,22 @@ | |
| 125 | #include <assert.h> |
| 126 | #define count(X) (sizeof(X)/sizeof(X[0])) |
| 127 | #ifndef M_PI |
| 128 | # define M_PI 3.1415926535897932385 |
| 129 | #endif |
| 130 | |
| 131 | /* Limit the number of tokens in a single script to avoid run-away |
| 132 | ** macro expansion attacks. See forum post |
| 133 | ** https://pikchr.org/home/forumpost/ef8684c6955a411a |
| 134 | */ |
| @@ -474,11 +525,11 @@ | |
| 474 | static void pik_bbox_addbox(PBox*,PBox*); |
| 475 | static void pik_bbox_add_xy(PBox*,PNum,PNum); |
| 476 | static void pik_bbox_addellipse(PBox*,PNum x,PNum y,PNum rx,PNum ry); |
| 477 | static void pik_add_txt(Pik*,PToken*,int); |
| 478 | static int pik_text_length(const PToken *pToken, const int isMonospace); |
| 479 | static void pik_size_to_fit(Pik*,PToken*,int); |
| 480 | static int pik_text_position(int,PToken*); |
| 481 | static PNum pik_property_of(PObj*,PToken*); |
| 482 | static PNum pik_func(Pik*,PToken*,PNum,PNum); |
| 483 | static PPoint pik_position_between(PNum x, PPoint p1, PPoint p2); |
| 484 | static PPoint pik_position_at_angle(PNum dist, PNum r, PPoint pt); |
| @@ -492,11 +543,11 @@ | |
| 492 | static PObj *pik_position_assert(Pik*,PPoint*,PToken*,PPoint*); |
| 493 | static PNum pik_dist(PPoint*,PPoint*); |
| 494 | static void pik_add_macro(Pik*,PToken *pId,PToken *pCode); |
| 495 | |
| 496 | |
| 497 | #line 523 "pikchr.c" |
| 498 | /**************** End of %include directives **********************************/ |
| 499 | /* These constants specify the various numeric values for terminal symbols. |
| 500 | ***************** Begin token definitions *************************************/ |
| 501 | #ifndef T_ID |
| 502 | #define T_ID 1 |
| @@ -522,84 +573,85 @@ | |
| 522 | #define T_COLOR 21 |
| 523 | #define T_THICKNESS 22 |
| 524 | #define T_PRINT 23 |
| 525 | #define T_STRING 24 |
| 526 | #define T_COMMA 25 |
| 527 | #define T_CLASSNAME 26 |
| 528 | #define T_LB 27 |
| 529 | #define T_RB 28 |
| 530 | #define T_UP 29 |
| 531 | #define T_DOWN 30 |
| 532 | #define T_LEFT 31 |
| 533 | #define T_RIGHT 32 |
| 534 | #define T_CLOSE 33 |
| 535 | #define T_CHOP 34 |
| 536 | #define T_FROM 35 |
| 537 | #define T_TO 36 |
| 538 | #define T_THEN 37 |
| 539 | #define T_HEADING 38 |
| 540 | #define T_GO 39 |
| 541 | #define T_AT 40 |
| 542 | #define T_WITH 41 |
| 543 | #define T_SAME 42 |
| 544 | #define T_AS 43 |
| 545 | #define T_FIT 44 |
| 546 | #define T_BEHIND 45 |
| 547 | #define T_UNTIL 46 |
| 548 | #define T_EVEN 47 |
| 549 | #define T_DOT_E 48 |
| 550 | #define T_HEIGHT 49 |
| 551 | #define T_WIDTH 50 |
| 552 | #define T_RADIUS 51 |
| 553 | #define T_DIAMETER 52 |
| 554 | #define T_DOTTED 53 |
| 555 | #define T_DASHED 54 |
| 556 | #define T_CW 55 |
| 557 | #define T_CCW 56 |
| 558 | #define T_LARROW 57 |
| 559 | #define T_RARROW 58 |
| 560 | #define T_LRARROW 59 |
| 561 | #define T_INVIS 60 |
| 562 | #define T_THICK 61 |
| 563 | #define T_THIN 62 |
| 564 | #define T_SOLID 63 |
| 565 | #define T_CENTER 64 |
| 566 | #define T_LJUST 65 |
| 567 | #define T_RJUST 66 |
| 568 | #define T_ABOVE 67 |
| 569 | #define T_BELOW 68 |
| 570 | #define T_ITALIC 69 |
| 571 | #define T_BOLD 70 |
| 572 | #define T_MONO 71 |
| 573 | #define T_ALIGNED 72 |
| 574 | #define T_BIG 73 |
| 575 | #define T_SMALL 74 |
| 576 | #define T_AND 75 |
| 577 | #define T_LT 76 |
| 578 | #define T_GT 77 |
| 579 | #define T_ON 78 |
| 580 | #define T_WAY 79 |
| 581 | #define T_BETWEEN 80 |
| 582 | #define T_THE 81 |
| 583 | #define T_NTH 82 |
| 584 | #define T_VERTEX 83 |
| 585 | #define T_TOP 84 |
| 586 | #define T_BOTTOM 85 |
| 587 | #define T_START 86 |
| 588 | #define T_END 87 |
| 589 | #define T_IN 88 |
| 590 | #define T_THIS 89 |
| 591 | #define T_DOT_U 90 |
| 592 | #define T_LAST 91 |
| 593 | #define T_NUMBER 92 |
| 594 | #define T_FUNC1 93 |
| 595 | #define T_FUNC2 94 |
| 596 | #define T_DIST 95 |
| 597 | #define T_DOT_XY 96 |
| 598 | #define T_X 97 |
| 599 | #define T_Y 98 |
| 600 | #define T_DOT_L 99 |
| 601 | #endif |
| 602 | /**************** End token definitions ***************************************/ |
| 603 | |
| 604 | /* The next sections is a series of control #defines. |
| 605 | ** various aspects of the generated parser. |
| @@ -660,22 +712,22 @@ | |
| 660 | #ifndef INTERFACE |
| 661 | # define INTERFACE 1 |
| 662 | #endif |
| 663 | /************* Begin control #defines *****************************************/ |
| 664 | #define YYCODETYPE unsigned char |
| 665 | #define YYNOCODE 136 |
| 666 | #define YYACTIONTYPE unsigned short int |
| 667 | #define pik_parserTOKENTYPE PToken |
| 668 | typedef union { |
| 669 | int yyinit; |
| 670 | pik_parserTOKENTYPE yy0; |
| 671 | PNum yy21; |
| 672 | PPoint yy63; |
| 673 | PRel yy72; |
| 674 | PObj* yy162; |
| 675 | short int yy188; |
| 676 | PList* yy235; |
| 677 | } YYMINORTYPE; |
| 678 | #ifndef YYSTACKDEPTH |
| 679 | #define YYSTACKDEPTH 100 |
| 680 | #endif |
| 681 | #define pik_parserARG_SDECL |
| @@ -693,21 +745,21 @@ | |
| 693 | #define pik_parserCTX_STORE yypParser->p=p; |
| 694 | #define YYFALLBACK 1 |
| 695 | #define YYNSTATE 164 |
| 696 | #define YYNRULE 156 |
| 697 | #define YYNRULE_WITH_ACTION 116 |
| 698 | #define YYNTOKEN 100 |
| 699 | #define YY_MAX_SHIFT 163 |
| 700 | #define YY_MIN_SHIFTREDUCE 287 |
| 701 | #define YY_MAX_SHIFTREDUCE 442 |
| 702 | #define YY_ERROR_ACTION 443 |
| 703 | #define YY_ACCEPT_ACTION 444 |
| 704 | #define YY_NO_ACTION 445 |
| 705 | #define YY_MIN_REDUCE 446 |
| 706 | #define YY_MAX_REDUCE 601 |
| 707 | #define YY_MIN_DSTRCTR 100 |
| 708 | #define YY_MAX_DSTRCTR 103 |
| 709 | /************* End control #defines *******************************************/ |
| 710 | #define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0]))) |
| 711 | |
| 712 | /* Define the yytestcase() macro to be a no-op if is not already defined |
| 713 | ** otherwise. |
| @@ -786,324 +838,322 @@ | |
| 786 | ** yy_reduce_ofst[] For each state, the offset into yy_action for |
| 787 | ** shifting non-terminals after a reduce. |
| 788 | ** yy_default[] Default action for each state. |
| 789 | ** |
| 790 | *********** Begin parsing tables **********************************************/ |
| 791 | #define YY_ACTTAB_COUNT (1313) |
| 792 | static const YYACTIONTYPE yy_action[] = { |
| 793 | /* 0 */ 575, 495, 161, 119, 25, 452, 29, 74, 129, 148, |
| 794 | /* 10 */ 575, 492, 161, 119, 453, 113, 120, 161, 119, 530, |
| 795 | /* 20 */ 427, 428, 339, 559, 81, 30, 560, 561, 575, 64, |
| 796 | /* 30 */ 63, 62, 61, 322, 323, 9, 8, 33, 149, 32, |
| 797 | /* 40 */ 7, 71, 127, 38, 335, 66, 48, 37, 28, 339, |
| 798 | /* 50 */ 339, 339, 339, 425, 426, 340, 341, 342, 343, 344, |
| 799 | /* 60 */ 345, 346, 347, 348, 474, 528, 161, 119, 577, 77, |
| 800 | /* 70 */ 577, 73, 306, 148, 474, 533, 161, 119, 112, 113, |
| 801 | /* 80 */ 120, 161, 119, 128, 427, 428, 339, 31, 81, 531, |
| 802 | /* 90 */ 161, 119, 474, 35, 330, 378, 158, 322, 323, 9, |
| 803 | /* 100 */ 8, 33, 149, 32, 7, 71, 127, 328, 335, 66, |
| 804 | /* 110 */ 579, 378, 158, 339, 339, 339, 339, 425, 426, 340, |
| 805 | /* 120 */ 341, 342, 343, 344, 345, 346, 347, 348, 394, 435, |
| 806 | /* 130 */ 46, 59, 60, 64, 63, 62, 61, 357, 36, 376, |
| 807 | /* 140 */ 54, 51, 2, 47, 403, 13, 297, 411, 412, 413, |
| 808 | /* 150 */ 414, 80, 162, 308, 79, 133, 310, 126, 441, 440, |
| 809 | /* 160 */ 118, 123, 83, 404, 405, 406, 408, 80, 84, 308, |
| 810 | /* 170 */ 79, 299, 411, 412, 413, 414, 118, 69, 350, 350, |
| 811 | /* 180 */ 350, 350, 350, 350, 350, 350, 350, 350, 350, 62, |
| 812 | /* 190 */ 61, 434, 64, 63, 62, 61, 313, 398, 399, 427, |
| 813 | /* 200 */ 428, 339, 380, 157, 64, 63, 62, 61, 122, 106, |
| 814 | /* 210 */ 535, 436, 437, 438, 439, 298, 375, 391, 117, 393, |
| 815 | /* 220 */ 155, 154, 153, 394, 435, 49, 59, 60, 339, 339, |
| 816 | /* 230 */ 339, 339, 425, 426, 376, 3, 4, 2, 64, 63, |
| 817 | /* 240 */ 62, 61, 156, 156, 156, 394, 379, 159, 59, 60, |
| 818 | /* 250 */ 76, 67, 535, 441, 440, 5, 102, 6, 535, 42, |
| 819 | /* 260 */ 131, 535, 69, 107, 301, 302, 303, 394, 305, 15, |
| 820 | /* 270 */ 59, 60, 120, 161, 119, 446, 463, 424, 376, 423, |
| 821 | /* 280 */ 1, 42, 397, 78, 78, 36, 434, 11, 394, 435, |
| 822 | /* 290 */ 356, 59, 60, 12, 152, 139, 432, 14, 16, 376, |
| 823 | /* 300 */ 18, 65, 2, 138, 106, 430, 436, 437, 438, 439, |
| 824 | /* 310 */ 44, 375, 19, 117, 393, 155, 154, 153, 441, 440, |
| 825 | /* 320 */ 142, 140, 64, 63, 62, 61, 106, 20, 68, 376, |
| 826 | /* 330 */ 359, 107, 23, 375, 45, 117, 393, 155, 154, 153, |
| 827 | /* 340 */ 120, 161, 119, 55, 463, 114, 26, 57, 106, 147, |
| 828 | /* 350 */ 146, 434, 569, 58, 392, 375, 43, 117, 393, 155, |
| 829 | /* 360 */ 154, 153, 152, 384, 64, 63, 62, 61, 382, 106, |
| 830 | /* 370 */ 383, 436, 437, 438, 439, 377, 375, 70, 117, 393, |
| 831 | /* 380 */ 155, 154, 153, 160, 39, 22, 21, 445, 142, 140, |
| 832 | /* 390 */ 64, 63, 62, 61, 24, 17, 145, 141, 431, 108, |
| 833 | /* 400 */ 445, 445, 445, 391, 445, 445, 375, 445, 117, 445, |
| 834 | /* 410 */ 445, 55, 74, 445, 148, 445, 445, 147, 146, 124, |
| 835 | /* 420 */ 113, 120, 161, 119, 43, 445, 445, 142, 140, 64, |
| 836 | /* 430 */ 63, 62, 61, 445, 394, 445, 445, 59, 60, 64, |
| 837 | /* 440 */ 63, 62, 61, 149, 445, 376, 445, 445, 42, 445, |
| 838 | /* 450 */ 55, 445, 391, 22, 21, 445, 147, 146, 445, 445, |
| 839 | /* 460 */ 52, 445, 24, 43, 145, 141, 431, 394, 445, 445, |
| 840 | /* 470 */ 59, 60, 64, 63, 62, 61, 445, 445, 376, 132, |
| 841 | /* 480 */ 130, 42, 445, 445, 445, 355, 156, 156, 156, 445, |
| 842 | /* 490 */ 445, 445, 22, 21, 445, 394, 473, 445, 59, 60, |
| 843 | /* 500 */ 445, 24, 445, 145, 141, 431, 376, 445, 107, 42, |
| 844 | /* 510 */ 64, 63, 62, 61, 445, 106, 445, 120, 161, 119, |
| 845 | /* 520 */ 445, 478, 375, 354, 117, 393, 155, 154, 153, 445, |
| 846 | /* 530 */ 394, 143, 473, 59, 60, 64, 63, 62, 61, 152, |
| 847 | /* 540 */ 445, 376, 445, 445, 42, 445, 445, 445, 106, 64, |
| 848 | /* 550 */ 63, 62, 61, 445, 445, 375, 50, 117, 393, 155, |
| 849 | /* 560 */ 154, 153, 445, 394, 144, 445, 59, 60, 445, 445, |
| 850 | /* 570 */ 53, 72, 445, 148, 376, 445, 106, 42, 125, 113, |
| 851 | /* 580 */ 120, 161, 119, 375, 445, 117, 393, 155, 154, 153, |
| 852 | /* 590 */ 394, 445, 445, 59, 60, 445, 445, 445, 445, 445, |
| 853 | /* 600 */ 445, 102, 149, 445, 42, 445, 74, 445, 148, 445, |
| 854 | /* 610 */ 445, 106, 445, 497, 113, 120, 161, 119, 375, 445, |
| 855 | /* 620 */ 117, 393, 155, 154, 153, 394, 445, 445, 59, 60, |
| 856 | /* 630 */ 445, 445, 88, 445, 445, 445, 376, 149, 445, 40, |
| 857 | /* 640 */ 445, 120, 161, 119, 106, 445, 445, 435, 110, 110, |
| 858 | /* 650 */ 445, 375, 445, 117, 393, 155, 154, 153, 394, 445, |
| 859 | /* 660 */ 445, 59, 60, 152, 85, 445, 445, 445, 445, 376, |
| 860 | /* 670 */ 445, 106, 41, 120, 161, 119, 441, 440, 375, 445, |
| 861 | /* 680 */ 117, 393, 155, 154, 153, 448, 454, 29, 445, 445, |
| 862 | /* 690 */ 74, 450, 148, 75, 88, 152, 445, 496, 113, 120, |
| 863 | /* 700 */ 161, 119, 163, 120, 161, 119, 106, 27, 445, 434, |
| 864 | /* 710 */ 111, 111, 445, 375, 445, 117, 393, 155, 154, 153, |
| 865 | /* 720 */ 445, 149, 445, 445, 445, 152, 74, 445, 148, 436, |
| 866 | /* 730 */ 437, 438, 439, 490, 113, 120, 161, 119, 445, 106, |
| 867 | /* 740 */ 121, 447, 454, 29, 445, 445, 375, 450, 117, 393, |
| 868 | /* 750 */ 155, 154, 153, 445, 445, 445, 445, 149, 163, 74, |
| 869 | /* 760 */ 445, 148, 444, 27, 445, 445, 484, 113, 120, 161, |
| 870 | /* 770 */ 119, 445, 445, 445, 74, 445, 148, 445, 445, 445, |
| 871 | /* 780 */ 445, 483, 113, 120, 161, 119, 74, 445, 148, 86, |
| 872 | /* 790 */ 149, 445, 445, 480, 113, 120, 161, 119, 120, 161, |
| 873 | /* 800 */ 119, 445, 74, 445, 148, 149, 445, 445, 445, 134, |
| 874 | /* 810 */ 113, 120, 161, 119, 74, 445, 148, 149, 445, 445, |
| 875 | /* 820 */ 152, 517, 113, 120, 161, 119, 88, 64, 63, 62, |
| 876 | /* 830 */ 61, 445, 445, 149, 445, 120, 161, 119, 445, 74, |
| 877 | /* 840 */ 396, 148, 475, 445, 445, 149, 137, 113, 120, 161, |
| 878 | /* 850 */ 119, 74, 445, 148, 445, 445, 445, 152, 525, 113, |
| 879 | /* 860 */ 120, 161, 119, 445, 74, 445, 148, 445, 445, 445, |
| 880 | /* 870 */ 149, 527, 113, 120, 161, 119, 445, 445, 445, 74, |
| 881 | /* 880 */ 445, 148, 149, 445, 445, 445, 524, 113, 120, 161, |
| 882 | /* 890 */ 119, 74, 445, 148, 98, 149, 445, 445, 526, 113, |
| 883 | /* 900 */ 120, 161, 119, 120, 161, 119, 445, 74, 445, 148, |
| 884 | /* 910 */ 149, 445, 445, 445, 523, 113, 120, 161, 119, 74, |
| 885 | /* 920 */ 445, 148, 149, 445, 445, 152, 522, 113, 120, 161, |
| 886 | /* 930 */ 119, 89, 64, 63, 62, 61, 445, 445, 149, 445, |
| 887 | /* 940 */ 120, 161, 119, 445, 74, 395, 148, 445, 445, 445, |
| 888 | /* 950 */ 149, 521, 113, 120, 161, 119, 74, 445, 148, 445, |
| 889 | /* 960 */ 445, 445, 152, 520, 113, 120, 161, 119, 445, 74, |
| 890 | /* 970 */ 445, 148, 445, 445, 445, 149, 519, 113, 120, 161, |
| 891 | /* 980 */ 119, 445, 445, 445, 74, 445, 148, 149, 445, 445, |
| 892 | /* 990 */ 445, 150, 113, 120, 161, 119, 74, 445, 148, 90, |
| 893 | /* 1000 */ 149, 445, 445, 151, 113, 120, 161, 119, 120, 161, |
| 894 | /* 1010 */ 119, 445, 74, 445, 148, 149, 445, 435, 445, 136, |
| 895 | /* 1020 */ 113, 120, 161, 119, 74, 445, 148, 149, 445, 445, |
| 896 | /* 1030 */ 152, 135, 113, 120, 161, 119, 64, 63, 62, 61, |
| 897 | /* 1040 */ 445, 445, 445, 149, 445, 445, 441, 440, 445, 88, |
| 898 | /* 1050 */ 445, 445, 445, 445, 445, 149, 445, 56, 120, 161, |
| 899 | /* 1060 */ 119, 88, 445, 445, 10, 479, 479, 445, 445, 445, |
| 900 | /* 1070 */ 120, 161, 119, 445, 445, 445, 445, 82, 445, 434, |
| 901 | /* 1080 */ 152, 445, 445, 445, 466, 445, 34, 109, 447, 454, |
| 902 | /* 1090 */ 29, 445, 152, 445, 450, 445, 445, 445, 107, 436, |
| 903 | /* 1100 */ 437, 438, 439, 87, 445, 163, 445, 120, 161, 119, |
| 904 | /* 1110 */ 27, 451, 120, 161, 119, 99, 445, 64, 63, 62, |
| 905 | /* 1120 */ 61, 445, 100, 445, 120, 161, 119, 101, 445, 152, |
| 906 | /* 1130 */ 391, 120, 161, 119, 152, 445, 120, 161, 119, 91, |
| 907 | /* 1140 */ 445, 445, 445, 445, 445, 445, 152, 445, 120, 161, |
| 908 | /* 1150 */ 119, 103, 445, 152, 92, 445, 445, 445, 152, 445, |
| 909 | /* 1160 */ 120, 161, 119, 120, 161, 119, 93, 445, 445, 104, |
| 910 | /* 1170 */ 152, 445, 445, 445, 445, 120, 161, 119, 120, 161, |
| 911 | /* 1180 */ 119, 445, 152, 445, 94, 152, 445, 445, 445, 445, |
| 912 | /* 1190 */ 445, 445, 105, 120, 161, 119, 445, 152, 445, 95, |
| 913 | /* 1200 */ 152, 120, 161, 119, 445, 445, 445, 96, 120, 161, |
| 914 | /* 1210 */ 119, 445, 445, 445, 445, 152, 120, 161, 119, 445, |
| 915 | /* 1220 */ 445, 445, 445, 152, 445, 445, 445, 445, 445, 445, |
| 916 | /* 1230 */ 152, 97, 445, 445, 549, 445, 445, 548, 152, 445, |
| 917 | /* 1240 */ 120, 161, 119, 120, 161, 119, 120, 161, 119, 445, |
| 918 | /* 1250 */ 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, |
| 919 | /* 1260 */ 445, 445, 152, 547, 445, 152, 546, 445, 152, 115, |
| 920 | /* 1270 */ 445, 445, 120, 161, 119, 120, 161, 119, 120, 161, |
| 921 | /* 1280 */ 119, 116, 445, 445, 445, 445, 445, 445, 445, 445, |
| 922 | /* 1290 */ 120, 161, 119, 445, 152, 445, 445, 152, 445, 445, |
| 923 | /* 1300 */ 152, 445, 445, 445, 445, 445, 445, 445, 445, 445, |
| 924 | /* 1310 */ 445, 445, 152, |
| 925 | }; |
| 926 | static const YYCODETYPE yy_lookahead[] = { |
| 927 | /* 0 */ 0, 113, 114, 115, 134, 102, 103, 104, 106, 106, |
| 928 | /* 10 */ 10, 113, 114, 115, 111, 112, 113, 114, 115, 106, |
| 929 | /* 20 */ 20, 21, 22, 105, 24, 126, 108, 109, 28, 4, |
| 930 | /* 30 */ 5, 6, 7, 33, 34, 35, 36, 37, 135, 39, |
| 931 | /* 40 */ 40, 41, 42, 105, 44, 45, 108, 109, 107, 49, |
| 932 | /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, |
| 933 | /* 60 */ 60, 61, 62, 63, 0, 113, 114, 115, 130, 131, |
| 934 | /* 70 */ 132, 104, 25, 106, 10, 113, 114, 115, 111, 112, |
| 935 | /* 80 */ 113, 114, 115, 106, 20, 21, 22, 128, 24, 113, |
| 936 | /* 90 */ 114, 115, 28, 129, 2, 26, 27, 33, 34, 35, |
| 937 | /* 100 */ 36, 37, 135, 39, 40, 41, 42, 2, 44, 45, |
| 938 | /* 110 */ 133, 26, 27, 49, 50, 51, 52, 53, 54, 55, |
| 939 | /* 120 */ 56, 57, 58, 59, 60, 61, 62, 63, 1, 2, |
| 940 | /* 130 */ 38, 4, 5, 4, 5, 6, 7, 17, 10, 12, |
| 941 | /* 140 */ 4, 5, 15, 38, 1, 25, 17, 29, 30, 31, |
| 942 | /* 150 */ 32, 24, 83, 26, 27, 12, 28, 14, 31, 32, |
| 943 | /* 160 */ 91, 18, 116, 20, 21, 22, 23, 24, 116, 26, |
| 944 | /* 170 */ 27, 19, 29, 30, 31, 32, 91, 3, 64, 65, |
| 945 | /* 180 */ 66, 67, 68, 69, 70, 71, 72, 73, 74, 6, |
| 946 | /* 190 */ 7, 64, 4, 5, 6, 7, 8, 97, 98, 20, |
| 947 | /* 200 */ 21, 22, 26, 27, 4, 5, 6, 7, 1, 82, |
| 948 | /* 210 */ 48, 84, 85, 86, 87, 17, 89, 17, 91, 92, |
| 949 | /* 220 */ 93, 94, 95, 1, 2, 25, 4, 5, 49, 50, |
| 950 | /* 230 */ 51, 52, 53, 54, 12, 16, 15, 15, 4, 5, |
| 951 | /* 240 */ 6, 7, 20, 21, 22, 1, 26, 27, 4, 5, |
| 952 | /* 250 */ 48, 43, 90, 31, 32, 40, 12, 40, 96, 15, |
| 953 | /* 260 */ 47, 99, 88, 104, 20, 21, 22, 1, 24, 35, |
| 954 | /* 270 */ 4, 5, 113, 114, 115, 0, 117, 41, 12, 41, |
| 955 | /* 280 */ 13, 15, 17, 124, 125, 10, 64, 25, 1, 2, |
| 956 | /* 290 */ 17, 4, 5, 75, 135, 81, 80, 3, 3, 12, |
| 957 | /* 300 */ 3, 99, 15, 79, 82, 80, 84, 85, 86, 87, |
| 958 | /* 310 */ 38, 89, 3, 91, 92, 93, 94, 95, 31, 32, |
| 959 | /* 320 */ 2, 3, 4, 5, 6, 7, 82, 3, 3, 12, |
| 960 | /* 330 */ 77, 104, 25, 89, 16, 91, 92, 93, 94, 95, |
| 961 | /* 340 */ 113, 114, 115, 25, 117, 96, 15, 15, 82, 31, |
| 962 | /* 350 */ 32, 64, 125, 15, 17, 89, 38, 91, 92, 93, |
| 963 | /* 360 */ 94, 95, 135, 28, 4, 5, 6, 7, 28, 82, |
| 964 | /* 370 */ 28, 84, 85, 86, 87, 12, 89, 3, 91, 92, |
| 965 | /* 380 */ 93, 94, 95, 90, 11, 67, 68, 136, 2, 3, |
| 966 | /* 390 */ 4, 5, 6, 7, 76, 35, 78, 79, 80, 82, |
| 967 | /* 400 */ 136, 136, 136, 17, 136, 136, 89, 136, 91, 136, |
| 968 | /* 410 */ 136, 25, 104, 136, 106, 136, 136, 31, 32, 111, |
| 969 | /* 420 */ 112, 113, 114, 115, 38, 136, 136, 2, 3, 4, |
| 970 | /* 430 */ 5, 6, 7, 136, 1, 136, 136, 4, 5, 4, |
| 971 | /* 440 */ 5, 6, 7, 135, 136, 12, 136, 136, 15, 136, |
| 972 | /* 450 */ 25, 136, 17, 67, 68, 136, 31, 32, 136, 136, |
| 973 | /* 460 */ 25, 136, 76, 38, 78, 79, 80, 1, 136, 136, |
| 974 | /* 470 */ 4, 5, 4, 5, 6, 7, 136, 136, 12, 46, |
| 975 | /* 480 */ 47, 15, 136, 136, 136, 17, 20, 21, 22, 136, |
| 976 | /* 490 */ 136, 136, 67, 68, 136, 1, 2, 136, 4, 5, |
| 977 | /* 500 */ 136, 76, 136, 78, 79, 80, 12, 136, 104, 15, |
| 978 | /* 510 */ 4, 5, 6, 7, 136, 82, 136, 113, 114, 115, |
| 979 | /* 520 */ 136, 117, 89, 17, 91, 92, 93, 94, 95, 136, |
| 980 | /* 530 */ 1, 2, 38, 4, 5, 4, 5, 6, 7, 135, |
| 981 | /* 540 */ 136, 12, 136, 136, 15, 136, 136, 136, 82, 4, |
| 982 | /* 550 */ 5, 6, 7, 136, 136, 89, 25, 91, 92, 93, |
| 983 | /* 560 */ 94, 95, 136, 1, 2, 136, 4, 5, 136, 136, |
| 984 | /* 570 */ 25, 104, 136, 106, 12, 136, 82, 15, 111, 112, |
| 985 | /* 580 */ 113, 114, 115, 89, 136, 91, 92, 93, 94, 95, |
| 986 | /* 590 */ 1, 136, 136, 4, 5, 136, 136, 136, 136, 136, |
| 987 | /* 600 */ 136, 12, 135, 136, 15, 136, 104, 136, 106, 136, |
| 988 | /* 610 */ 136, 82, 136, 111, 112, 113, 114, 115, 89, 136, |
| 989 | /* 620 */ 91, 92, 93, 94, 95, 1, 136, 136, 4, 5, |
| 990 | /* 630 */ 136, 136, 104, 136, 136, 136, 12, 135, 136, 15, |
| 991 | /* 640 */ 136, 113, 114, 115, 82, 136, 136, 2, 120, 121, |
| 992 | /* 650 */ 136, 89, 136, 91, 92, 93, 94, 95, 1, 136, |
| 993 | /* 660 */ 136, 4, 5, 135, 104, 136, 136, 136, 136, 12, |
| 994 | /* 670 */ 136, 82, 15, 113, 114, 115, 31, 32, 89, 136, |
| 995 | /* 680 */ 91, 92, 93, 94, 95, 101, 102, 103, 136, 136, |
| 996 | /* 690 */ 104, 107, 106, 48, 104, 135, 136, 111, 112, 113, |
| 997 | /* 700 */ 114, 115, 118, 113, 114, 115, 82, 123, 136, 64, |
| 998 | /* 710 */ 120, 121, 136, 89, 136, 91, 92, 93, 94, 95, |
| 999 | /* 720 */ 136, 135, 136, 136, 136, 135, 104, 136, 106, 84, |
| 1000 | /* 730 */ 85, 86, 87, 111, 112, 113, 114, 115, 136, 82, |
| 1001 | /* 740 */ 100, 101, 102, 103, 136, 136, 89, 107, 91, 92, |
| 1002 | /* 750 */ 93, 94, 95, 136, 136, 136, 136, 135, 118, 104, |
| 1003 | /* 760 */ 136, 106, 122, 123, 136, 136, 111, 112, 113, 114, |
| 1004 | /* 770 */ 115, 136, 136, 136, 104, 136, 106, 136, 136, 136, |
| 1005 | /* 780 */ 136, 111, 112, 113, 114, 115, 104, 136, 106, 104, |
| 1006 | /* 790 */ 135, 136, 136, 111, 112, 113, 114, 115, 113, 114, |
| 1007 | /* 800 */ 115, 136, 104, 136, 106, 135, 136, 136, 136, 111, |
| 1008 | /* 810 */ 112, 113, 114, 115, 104, 136, 106, 135, 136, 136, |
| 1009 | /* 820 */ 135, 111, 112, 113, 114, 115, 104, 4, 5, 6, |
| 1010 | /* 830 */ 7, 136, 136, 135, 136, 113, 114, 115, 136, 104, |
| 1011 | /* 840 */ 17, 106, 120, 136, 136, 135, 111, 112, 113, 114, |
| 1012 | /* 850 */ 115, 104, 136, 106, 136, 136, 136, 135, 111, 112, |
| 1013 | /* 860 */ 113, 114, 115, 136, 104, 136, 106, 136, 136, 136, |
| 1014 | /* 870 */ 135, 111, 112, 113, 114, 115, 136, 136, 136, 104, |
| 1015 | /* 880 */ 136, 106, 135, 136, 136, 136, 111, 112, 113, 114, |
| 1016 | /* 890 */ 115, 104, 136, 106, 104, 135, 136, 136, 111, 112, |
| 1017 | /* 900 */ 113, 114, 115, 113, 114, 115, 136, 104, 136, 106, |
| 1018 | /* 910 */ 135, 136, 136, 136, 111, 112, 113, 114, 115, 104, |
| 1019 | /* 920 */ 136, 106, 135, 136, 136, 135, 111, 112, 113, 114, |
| 1020 | /* 930 */ 115, 104, 4, 5, 6, 7, 136, 136, 135, 136, |
| 1021 | /* 940 */ 113, 114, 115, 136, 104, 17, 106, 136, 136, 136, |
| 1022 | /* 950 */ 135, 111, 112, 113, 114, 115, 104, 136, 106, 136, |
| 1023 | /* 960 */ 136, 136, 135, 111, 112, 113, 114, 115, 136, 104, |
| 1024 | /* 970 */ 136, 106, 136, 136, 136, 135, 111, 112, 113, 114, |
| 1025 | /* 980 */ 115, 136, 136, 136, 104, 136, 106, 135, 136, 136, |
| 1026 | /* 990 */ 136, 111, 112, 113, 114, 115, 104, 136, 106, 104, |
| 1027 | /* 1000 */ 135, 136, 136, 111, 112, 113, 114, 115, 113, 114, |
| 1028 | /* 1010 */ 115, 136, 104, 136, 106, 135, 136, 2, 136, 111, |
| 1029 | /* 1020 */ 112, 113, 114, 115, 104, 136, 106, 135, 136, 136, |
| 1030 | /* 1030 */ 135, 111, 112, 113, 114, 115, 4, 5, 6, 7, |
| 1031 | /* 1040 */ 136, 136, 136, 135, 136, 136, 31, 32, 136, 104, |
| 1032 | /* 1050 */ 136, 136, 136, 136, 136, 135, 136, 25, 113, 114, |
| 1033 | /* 1060 */ 115, 104, 136, 136, 119, 120, 121, 136, 136, 136, |
| 1034 | /* 1070 */ 113, 114, 115, 136, 136, 136, 136, 120, 136, 64, |
| 1035 | /* 1080 */ 135, 136, 136, 136, 127, 136, 129, 100, 101, 102, |
| 1036 | /* 1090 */ 103, 136, 135, 136, 107, 136, 136, 136, 104, 84, |
| 1037 | /* 1100 */ 85, 86, 87, 104, 136, 118, 136, 113, 114, 115, |
| 1038 | /* 1110 */ 123, 117, 113, 114, 115, 104, 136, 4, 5, 6, |
| 1039 | /* 1120 */ 7, 136, 104, 136, 113, 114, 115, 104, 136, 135, |
| 1040 | /* 1130 */ 17, 113, 114, 115, 135, 136, 113, 114, 115, 104, |
| 1041 | /* 1140 */ 136, 136, 136, 136, 136, 136, 135, 136, 113, 114, |
| 1042 | /* 1150 */ 115, 104, 136, 135, 104, 136, 136, 136, 135, 136, |
| 1043 | /* 1160 */ 113, 114, 115, 113, 114, 115, 104, 136, 136, 104, |
| 1044 | /* 1170 */ 135, 136, 136, 136, 136, 113, 114, 115, 113, 114, |
| 1045 | /* 1180 */ 115, 136, 135, 136, 104, 135, 136, 136, 136, 136, |
| 1046 | /* 1190 */ 136, 136, 104, 113, 114, 115, 136, 135, 136, 104, |
| 1047 | /* 1200 */ 135, 113, 114, 115, 136, 136, 136, 104, 113, 114, |
| 1048 | /* 1210 */ 115, 136, 136, 136, 136, 135, 113, 114, 115, 136, |
| 1049 | /* 1220 */ 136, 136, 136, 135, 136, 136, 136, 136, 136, 136, |
| 1050 | /* 1230 */ 135, 104, 136, 136, 104, 136, 136, 104, 135, 136, |
| 1051 | /* 1240 */ 113, 114, 115, 113, 114, 115, 113, 114, 115, 136, |
| 1052 | /* 1250 */ 136, 136, 136, 136, 136, 136, 136, 136, 136, 136, |
| 1053 | /* 1260 */ 136, 136, 135, 104, 136, 135, 104, 136, 135, 104, |
| 1054 | /* 1270 */ 136, 136, 113, 114, 115, 113, 114, 115, 113, 114, |
| 1055 | /* 1280 */ 115, 104, 136, 136, 136, 136, 136, 136, 136, 136, |
| 1056 | /* 1290 */ 113, 114, 115, 136, 135, 136, 136, 135, 136, 136, |
| 1057 | /* 1300 */ 135, 136, 136, 136, 136, 136, 136, 136, 136, 136, |
| 1058 | /* 1310 */ 136, 136, 135, 100, 100, 100, 100, 100, 100, 100, |
| 1059 | /* 1320 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, |
| 1060 | /* 1330 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, |
| 1061 | /* 1340 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, |
| 1062 | /* 1350 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, |
| 1063 | /* 1360 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, |
| 1064 | /* 1370 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, |
| 1065 | /* 1380 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, |
| 1066 | /* 1390 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, |
| 1067 | /* 1400 */ 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, |
| 1068 | /* 1410 */ 100, 100, 100, |
| 1069 | }; |
| 1070 | #define YY_SHIFT_COUNT (163) |
| 1071 | #define YY_SHIFT_MIN (0) |
| 1072 | #define YY_SHIFT_MAX (1113) |
| 1073 | static const unsigned short int yy_shift_ofst[] = { |
| 1074 | /* 0 */ 143, 127, 222, 287, 287, 287, 287, 287, 287, 287, |
| 1075 | /* 10 */ 287, 287, 287, 287, 287, 287, 287, 287, 287, 287, |
| 1076 | /* 20 */ 287, 287, 287, 287, 287, 287, 287, 244, 433, 266, |
| 1077 | /* 30 */ 244, 143, 494, 494, 0, 64, 143, 589, 266, 589, |
| 1078 | /* 40 */ 466, 466, 466, 529, 562, 266, 266, 266, 266, 266, |
| 1079 | /* 50 */ 266, 624, 266, 266, 657, 266, 266, 266, 266, 266, |
| 1080 | /* 60 */ 266, 266, 266, 266, 266, 179, 317, 317, 317, 317, |
| 1081 | /* 70 */ 317, 645, 318, 386, 425, 1015, 1015, 118, 47, 1313, |
| 1082 | /* 80 */ 1313, 1313, 1313, 114, 114, 200, 435, 129, 188, 234, |
| 1083 | /* 90 */ 360, 468, 531, 506, 545, 823, 1032, 928, 1113, 25, |
| 1084 | /* 100 */ 25, 25, 162, 25, 25, 25, 69, 25, 85, 128, |
| 1085 | /* 110 */ 92, 105, 120, 136, 100, 183, 183, 176, 220, 174, |
| 1086 | /* 120 */ 202, 275, 152, 207, 198, 219, 221, 208, 215, 217, |
| 1087 | /* 130 */ 236, 238, 213, 267, 265, 262, 218, 273, 216, 224, |
| 1088 | /* 140 */ 214, 225, 294, 295, 297, 272, 309, 324, 325, 249, |
| 1089 | /* 150 */ 253, 307, 249, 331, 332, 338, 337, 335, 340, 342, |
| 1090 | /* 160 */ 363, 293, 374, 373, |
| 1091 | }; |
| 1092 | #define YY_REDUCE_COUNT (82) |
| 1093 | #define YY_REDUCE_MIN (-130) |
| 1094 | #define YY_REDUCE_MAX (1177) |
| 1095 | static const short yy_reduce_ofst[] = { |
| 1096 | /* 0 */ 640, -97, -33, 308, 467, 502, 586, 622, 655, 670, |
| 1097 | /* 10 */ 682, 698, 710, 735, 747, 760, 775, 787, 803, 815, |
| 1098 | /* 20 */ 840, 852, 865, 880, 892, 908, 920, 159, 945, 957, |
| 1099 | /* 30 */ 227, 987, 528, 590, -62, -62, 584, 404, 722, 994, |
| 1100 | /* 40 */ 560, 685, 790, 827, 895, 999, 1011, 1018, 1023, 1035, |
| 1101 | /* 50 */ 1047, 1050, 1062, 1065, 1080, 1088, 1095, 1103, 1127, 1130, |
| 1102 | /* 60 */ 1133, 1159, 1162, 1165, 1177, -82, -112, -102, -48, -38, |
| 1103 | /* 70 */ -24, -23, -130, -130, -130, -98, -87, -59, -101, -41, |
| 1104 | /* 80 */ 46, 52, -36, |
| 1105 | }; |
| 1106 | static const YYACTIONTYPE yy_default[] = { |
| 1107 | /* 0 */ 449, 443, 443, 443, 443, 443, 443, 443, 443, 443, |
| 1108 | /* 10 */ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, |
| 1109 | /* 20 */ 443, 443, 443, 443, 443, 443, 443, 443, 473, 576, |
| @@ -1164,10 +1214,11 @@ | |
| 1164 | 0, /* COLOR => nothing */ |
| 1165 | 0, /* THICKNESS => nothing */ |
| 1166 | 0, /* PRINT => nothing */ |
| 1167 | 0, /* STRING => nothing */ |
| 1168 | 0, /* COMMA => nothing */ |
| 1169 | 0, /* CLASSNAME => nothing */ |
| 1170 | 0, /* LB => nothing */ |
| 1171 | 0, /* RB => nothing */ |
| 1172 | 0, /* UP => nothing */ |
| 1173 | 0, /* DOWN => nothing */ |
| @@ -1347,120 +1398,122 @@ | |
| 1347 | /* 21 */ "COLOR", |
| 1348 | /* 22 */ "THICKNESS", |
| 1349 | /* 23 */ "PRINT", |
| 1350 | /* 24 */ "STRING", |
| 1351 | /* 25 */ "COMMA", |
| 1352 | /* 26 */ "CLASSNAME", |
| 1353 | /* 27 */ "LB", |
| 1354 | /* 28 */ "RB", |
| 1355 | /* 29 */ "UP", |
| 1356 | /* 30 */ "DOWN", |
| 1357 | /* 31 */ "LEFT", |
| 1358 | /* 32 */ "RIGHT", |
| 1359 | /* 33 */ "CLOSE", |
| 1360 | /* 34 */ "CHOP", |
| 1361 | /* 35 */ "FROM", |
| 1362 | /* 36 */ "TO", |
| 1363 | /* 37 */ "THEN", |
| 1364 | /* 38 */ "HEADING", |
| 1365 | /* 39 */ "GO", |
| 1366 | /* 40 */ "AT", |
| 1367 | /* 41 */ "WITH", |
| 1368 | /* 42 */ "SAME", |
| 1369 | /* 43 */ "AS", |
| 1370 | /* 44 */ "FIT", |
| 1371 | /* 45 */ "BEHIND", |
| 1372 | /* 46 */ "UNTIL", |
| 1373 | /* 47 */ "EVEN", |
| 1374 | /* 48 */ "DOT_E", |
| 1375 | /* 49 */ "HEIGHT", |
| 1376 | /* 50 */ "WIDTH", |
| 1377 | /* 51 */ "RADIUS", |
| 1378 | /* 52 */ "DIAMETER", |
| 1379 | /* 53 */ "DOTTED", |
| 1380 | /* 54 */ "DASHED", |
| 1381 | /* 55 */ "CW", |
| 1382 | /* 56 */ "CCW", |
| 1383 | /* 57 */ "LARROW", |
| 1384 | /* 58 */ "RARROW", |
| 1385 | /* 59 */ "LRARROW", |
| 1386 | /* 60 */ "INVIS", |
| 1387 | /* 61 */ "THICK", |
| 1388 | /* 62 */ "THIN", |
| 1389 | /* 63 */ "SOLID", |
| 1390 | /* 64 */ "CENTER", |
| 1391 | /* 65 */ "LJUST", |
| 1392 | /* 66 */ "RJUST", |
| 1393 | /* 67 */ "ABOVE", |
| 1394 | /* 68 */ "BELOW", |
| 1395 | /* 69 */ "ITALIC", |
| 1396 | /* 70 */ "BOLD", |
| 1397 | /* 71 */ "MONO", |
| 1398 | /* 72 */ "ALIGNED", |
| 1399 | /* 73 */ "BIG", |
| 1400 | /* 74 */ "SMALL", |
| 1401 | /* 75 */ "AND", |
| 1402 | /* 76 */ "LT", |
| 1403 | /* 77 */ "GT", |
| 1404 | /* 78 */ "ON", |
| 1405 | /* 79 */ "WAY", |
| 1406 | /* 80 */ "BETWEEN", |
| 1407 | /* 81 */ "THE", |
| 1408 | /* 82 */ "NTH", |
| 1409 | /* 83 */ "VERTEX", |
| 1410 | /* 84 */ "TOP", |
| 1411 | /* 85 */ "BOTTOM", |
| 1412 | /* 86 */ "START", |
| 1413 | /* 87 */ "END", |
| 1414 | /* 88 */ "IN", |
| 1415 | /* 89 */ "THIS", |
| 1416 | /* 90 */ "DOT_U", |
| 1417 | /* 91 */ "LAST", |
| 1418 | /* 92 */ "NUMBER", |
| 1419 | /* 93 */ "FUNC1", |
| 1420 | /* 94 */ "FUNC2", |
| 1421 | /* 95 */ "DIST", |
| 1422 | /* 96 */ "DOT_XY", |
| 1423 | /* 97 */ "X", |
| 1424 | /* 98 */ "Y", |
| 1425 | /* 99 */ "DOT_L", |
| 1426 | /* 100 */ "statement_list", |
| 1427 | /* 101 */ "statement", |
| 1428 | /* 102 */ "unnamed_statement", |
| 1429 | /* 103 */ "basetype", |
| 1430 | /* 104 */ "expr", |
| 1431 | /* 105 */ "numproperty", |
| 1432 | /* 106 */ "edge", |
| 1433 | /* 107 */ "direction", |
| 1434 | /* 108 */ "dashproperty", |
| 1435 | /* 109 */ "colorproperty", |
| 1436 | /* 110 */ "locproperty", |
| 1437 | /* 111 */ "position", |
| 1438 | /* 112 */ "place", |
| 1439 | /* 113 */ "object", |
| 1440 | /* 114 */ "objectname", |
| 1441 | /* 115 */ "nth", |
| 1442 | /* 116 */ "textposition", |
| 1443 | /* 117 */ "rvalue", |
| 1444 | /* 118 */ "lvalue", |
| 1445 | /* 119 */ "even", |
| 1446 | /* 120 */ "relexpr", |
| 1447 | /* 121 */ "optrelexpr", |
| 1448 | /* 122 */ "document", |
| 1449 | /* 123 */ "print", |
| 1450 | /* 124 */ "prlist", |
| 1451 | /* 125 */ "pritem", |
| 1452 | /* 126 */ "prsep", |
| 1453 | /* 127 */ "attribute_list", |
| 1454 | /* 128 */ "savelist", |
| 1455 | /* 129 */ "alist", |
| 1456 | /* 130 */ "attribute", |
| 1457 | /* 131 */ "go", |
| 1458 | /* 132 */ "boolproperty", |
| 1459 | /* 133 */ "withclause", |
| 1460 | /* 134 */ "between", |
| 1461 | /* 135 */ "place2", |
| 1462 | }; |
| 1463 | #endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */ |
| 1464 | |
| 1465 | #ifndef NDEBUG |
| 1466 | /* For tracing reduce actions, the names of all rules are required. |
| @@ -1743,24 +1796,24 @@ | |
| 1743 | ** Note: during a reduce, the only symbols destroyed are those |
| 1744 | ** which appear on the RHS of the rule, but which are *not* used |
| 1745 | ** inside the C code. |
| 1746 | */ |
| 1747 | /********* Begin destructor definitions ***************************************/ |
| 1748 | case 100: /* statement_list */ |
| 1749 | { |
| 1750 | #line 511 "pikchr.y" |
| 1751 | pik_elist_free(p,(yypminor->yy235)); |
| 1752 | #line 1777 "pikchr.c" |
| 1753 | } |
| 1754 | break; |
| 1755 | case 101: /* statement */ |
| 1756 | case 102: /* unnamed_statement */ |
| 1757 | case 103: /* basetype */ |
| 1758 | { |
| 1759 | #line 513 "pikchr.y" |
| 1760 | pik_elem_free(p,(yypminor->yy162)); |
| 1761 | #line 1786 "pikchr.c" |
| 1762 | } |
| 1763 | break; |
| 1764 | /********* End destructor definitions *****************************************/ |
| 1765 | default: break; /* If no destructor action specified: do nothing */ |
| 1766 | } |
| @@ -1991,14 +2044,14 @@ | |
| 1991 | #endif |
| 1992 | while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser); |
| 1993 | /* Here code is inserted which will execute if the parser |
| 1994 | ** stack every overflows */ |
| 1995 | /******** Begin %stack_overflow code ******************************************/ |
| 1996 | #line 545 "pikchr.y" |
| 1997 | |
| 1998 | pik_error(p, 0, "parser stack overflow"); |
| 1999 | #line 2024 "pikchr.c" |
| 2000 | /******** End %stack_overflow code ********************************************/ |
| 2001 | pik_parserARG_STORE /* Suppress warning about unused %extra_argument var */ |
| 2002 | pik_parserCTX_STORE |
| 2003 | } |
| 2004 | |
| @@ -2060,166 +2113,166 @@ | |
| 2060 | } |
| 2061 | |
| 2062 | /* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side |
| 2063 | ** of that rule */ |
| 2064 | static const YYCODETYPE yyRuleInfoLhs[] = { |
| 2065 | 122, /* (0) document ::= statement_list */ |
| 2066 | 100, /* (1) statement_list ::= statement */ |
| 2067 | 100, /* (2) statement_list ::= statement_list EOL statement */ |
| 2068 | 101, /* (3) statement ::= */ |
| 2069 | 101, /* (4) statement ::= direction */ |
| 2070 | 101, /* (5) statement ::= lvalue ASSIGN rvalue */ |
| 2071 | 101, /* (6) statement ::= PLACENAME COLON unnamed_statement */ |
| 2072 | 101, /* (7) statement ::= PLACENAME COLON position */ |
| 2073 | 101, /* (8) statement ::= unnamed_statement */ |
| 2074 | 101, /* (9) statement ::= print prlist */ |
| 2075 | 101, /* (10) statement ::= ASSERT LP expr EQ expr RP */ |
| 2076 | 101, /* (11) statement ::= ASSERT LP position EQ position RP */ |
| 2077 | 101, /* (12) statement ::= DEFINE ID CODEBLOCK */ |
| 2078 | 117, /* (13) rvalue ::= PLACENAME */ |
| 2079 | 125, /* (14) pritem ::= FILL */ |
| 2080 | 125, /* (15) pritem ::= COLOR */ |
| 2081 | 125, /* (16) pritem ::= THICKNESS */ |
| 2082 | 125, /* (17) pritem ::= rvalue */ |
| 2083 | 125, /* (18) pritem ::= STRING */ |
| 2084 | 126, /* (19) prsep ::= COMMA */ |
| 2085 | 102, /* (20) unnamed_statement ::= basetype attribute_list */ |
| 2086 | 103, /* (21) basetype ::= CLASSNAME */ |
| 2087 | 103, /* (22) basetype ::= STRING textposition */ |
| 2088 | 103, /* (23) basetype ::= LB savelist statement_list RB */ |
| 2089 | 128, /* (24) savelist ::= */ |
| 2090 | 120, /* (25) relexpr ::= expr */ |
| 2091 | 120, /* (26) relexpr ::= expr PERCENT */ |
| 2092 | 121, /* (27) optrelexpr ::= */ |
| 2093 | 127, /* (28) attribute_list ::= relexpr alist */ |
| 2094 | 130, /* (29) attribute ::= numproperty relexpr */ |
| 2095 | 130, /* (30) attribute ::= dashproperty expr */ |
| 2096 | 130, /* (31) attribute ::= dashproperty */ |
| 2097 | 130, /* (32) attribute ::= colorproperty rvalue */ |
| 2098 | 130, /* (33) attribute ::= go direction optrelexpr */ |
| 2099 | 130, /* (34) attribute ::= go direction even position */ |
| 2100 | 130, /* (35) attribute ::= CLOSE */ |
| 2101 | 130, /* (36) attribute ::= CHOP */ |
| 2102 | 130, /* (37) attribute ::= FROM position */ |
| 2103 | 130, /* (38) attribute ::= TO position */ |
| 2104 | 130, /* (39) attribute ::= THEN */ |
| 2105 | 130, /* (40) attribute ::= THEN optrelexpr HEADING expr */ |
| 2106 | 130, /* (41) attribute ::= THEN optrelexpr EDGEPT */ |
| 2107 | 130, /* (42) attribute ::= GO optrelexpr HEADING expr */ |
| 2108 | 130, /* (43) attribute ::= GO optrelexpr EDGEPT */ |
| 2109 | 130, /* (44) attribute ::= AT position */ |
| 2110 | 130, /* (45) attribute ::= SAME */ |
| 2111 | 130, /* (46) attribute ::= SAME AS object */ |
| 2112 | 130, /* (47) attribute ::= STRING textposition */ |
| 2113 | 130, /* (48) attribute ::= FIT */ |
| 2114 | 130, /* (49) attribute ::= BEHIND object */ |
| 2115 | 133, /* (50) withclause ::= DOT_E edge AT position */ |
| 2116 | 133, /* (51) withclause ::= edge AT position */ |
| 2117 | 105, /* (52) numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */ |
| 2118 | 132, /* (53) boolproperty ::= CW */ |
| 2119 | 132, /* (54) boolproperty ::= CCW */ |
| 2120 | 132, /* (55) boolproperty ::= LARROW */ |
| 2121 | 132, /* (56) boolproperty ::= RARROW */ |
| 2122 | 132, /* (57) boolproperty ::= LRARROW */ |
| 2123 | 132, /* (58) boolproperty ::= INVIS */ |
| 2124 | 132, /* (59) boolproperty ::= THICK */ |
| 2125 | 132, /* (60) boolproperty ::= THIN */ |
| 2126 | 132, /* (61) boolproperty ::= SOLID */ |
| 2127 | 116, /* (62) textposition ::= */ |
| 2128 | 116, /* (63) textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL */ |
| 2129 | 111, /* (64) position ::= expr COMMA expr */ |
| 2130 | 111, /* (65) position ::= place PLUS expr COMMA expr */ |
| 2131 | 111, /* (66) position ::= place MINUS expr COMMA expr */ |
| 2132 | 111, /* (67) position ::= place PLUS LP expr COMMA expr RP */ |
| 2133 | 111, /* (68) position ::= place MINUS LP expr COMMA expr RP */ |
| 2134 | 111, /* (69) position ::= LP position COMMA position RP */ |
| 2135 | 111, /* (70) position ::= LP position RP */ |
| 2136 | 111, /* (71) position ::= expr between position AND position */ |
| 2137 | 111, /* (72) position ::= expr LT position COMMA position GT */ |
| 2138 | 111, /* (73) position ::= expr ABOVE position */ |
| 2139 | 111, /* (74) position ::= expr BELOW position */ |
| 2140 | 111, /* (75) position ::= expr LEFT OF position */ |
| 2141 | 111, /* (76) position ::= expr RIGHT OF position */ |
| 2142 | 111, /* (77) position ::= expr ON HEADING EDGEPT OF position */ |
| 2143 | 111, /* (78) position ::= expr HEADING EDGEPT OF position */ |
| 2144 | 111, /* (79) position ::= expr EDGEPT OF position */ |
| 2145 | 111, /* (80) position ::= expr ON HEADING expr FROM position */ |
| 2146 | 111, /* (81) position ::= expr HEADING expr FROM position */ |
| 2147 | 112, /* (82) place ::= edge OF object */ |
| 2148 | 135, /* (83) place2 ::= object */ |
| 2149 | 135, /* (84) place2 ::= object DOT_E edge */ |
| 2150 | 135, /* (85) place2 ::= NTH VERTEX OF object */ |
| 2151 | 113, /* (86) object ::= nth */ |
| 2152 | 113, /* (87) object ::= nth OF|IN object */ |
| 2153 | 114, /* (88) objectname ::= THIS */ |
| 2154 | 114, /* (89) objectname ::= PLACENAME */ |
| 2155 | 114, /* (90) objectname ::= objectname DOT_U PLACENAME */ |
| 2156 | 115, /* (91) nth ::= NTH CLASSNAME */ |
| 2157 | 115, /* (92) nth ::= NTH LAST CLASSNAME */ |
| 2158 | 115, /* (93) nth ::= LAST CLASSNAME */ |
| 2159 | 115, /* (94) nth ::= LAST */ |
| 2160 | 115, /* (95) nth ::= NTH LB RB */ |
| 2161 | 115, /* (96) nth ::= NTH LAST LB RB */ |
| 2162 | 115, /* (97) nth ::= LAST LB RB */ |
| 2163 | 104, /* (98) expr ::= expr PLUS expr */ |
| 2164 | 104, /* (99) expr ::= expr MINUS expr */ |
| 2165 | 104, /* (100) expr ::= expr STAR expr */ |
| 2166 | 104, /* (101) expr ::= expr SLASH expr */ |
| 2167 | 104, /* (102) expr ::= MINUS expr */ |
| 2168 | 104, /* (103) expr ::= PLUS expr */ |
| 2169 | 104, /* (104) expr ::= LP expr RP */ |
| 2170 | 104, /* (105) expr ::= LP FILL|COLOR|THICKNESS RP */ |
| 2171 | 104, /* (106) expr ::= NUMBER */ |
| 2172 | 104, /* (107) expr ::= ID */ |
| 2173 | 104, /* (108) expr ::= FUNC1 LP expr RP */ |
| 2174 | 104, /* (109) expr ::= FUNC2 LP expr COMMA expr RP */ |
| 2175 | 104, /* (110) expr ::= DIST LP position COMMA position RP */ |
| 2176 | 104, /* (111) expr ::= place2 DOT_XY X */ |
| 2177 | 104, /* (112) expr ::= place2 DOT_XY Y */ |
| 2178 | 104, /* (113) expr ::= object DOT_L numproperty */ |
| 2179 | 104, /* (114) expr ::= object DOT_L dashproperty */ |
| 2180 | 104, /* (115) expr ::= object DOT_L colorproperty */ |
| 2181 | 118, /* (116) lvalue ::= ID */ |
| 2182 | 118, /* (117) lvalue ::= FILL */ |
| 2183 | 118, /* (118) lvalue ::= COLOR */ |
| 2184 | 118, /* (119) lvalue ::= THICKNESS */ |
| 2185 | 117, /* (120) rvalue ::= expr */ |
| 2186 | 123, /* (121) print ::= PRINT */ |
| 2187 | 124, /* (122) prlist ::= pritem */ |
| 2188 | 124, /* (123) prlist ::= prlist prsep pritem */ |
| 2189 | 107, /* (124) direction ::= UP */ |
| 2190 | 107, /* (125) direction ::= DOWN */ |
| 2191 | 107, /* (126) direction ::= LEFT */ |
| 2192 | 107, /* (127) direction ::= RIGHT */ |
| 2193 | 121, /* (128) optrelexpr ::= relexpr */ |
| 2194 | 127, /* (129) attribute_list ::= alist */ |
| 2195 | 129, /* (130) alist ::= */ |
| 2196 | 129, /* (131) alist ::= alist attribute */ |
| 2197 | 130, /* (132) attribute ::= boolproperty */ |
| 2198 | 130, /* (133) attribute ::= WITH withclause */ |
| 2199 | 131, /* (134) go ::= GO */ |
| 2200 | 131, /* (135) go ::= */ |
| 2201 | 119, /* (136) even ::= UNTIL EVEN WITH */ |
| 2202 | 119, /* (137) even ::= EVEN WITH */ |
| 2203 | 108, /* (138) dashproperty ::= DOTTED */ |
| 2204 | 108, /* (139) dashproperty ::= DASHED */ |
| 2205 | 109, /* (140) colorproperty ::= FILL */ |
| 2206 | 109, /* (141) colorproperty ::= COLOR */ |
| 2207 | 111, /* (142) position ::= place */ |
| 2208 | 134, /* (143) between ::= WAY BETWEEN */ |
| 2209 | 134, /* (144) between ::= BETWEEN */ |
| 2210 | 134, /* (145) between ::= OF THE WAY BETWEEN */ |
| 2211 | 112, /* (146) place ::= place2 */ |
| 2212 | 106, /* (147) edge ::= CENTER */ |
| 2213 | 106, /* (148) edge ::= EDGEPT */ |
| 2214 | 106, /* (149) edge ::= TOP */ |
| 2215 | 106, /* (150) edge ::= BOTTOM */ |
| 2216 | 106, /* (151) edge ::= START */ |
| 2217 | 106, /* (152) edge ::= END */ |
| 2218 | 106, /* (153) edge ::= RIGHT */ |
| 2219 | 106, /* (154) edge ::= LEFT */ |
| 2220 | 113, /* (155) object ::= objectname */ |
| 2221 | }; |
| 2222 | |
| 2223 | /* For rule J, yyRuleInfoNRhs[J] contains the negative of the number |
| 2224 | ** of symbols on the right-hand side of that rule. */ |
| 2225 | static const signed char yyRuleInfoNRhs[] = { |
| @@ -2419,620 +2472,620 @@ | |
| 2419 | ** break; |
| 2420 | */ |
| 2421 | /********** Begin reduce actions **********************************************/ |
| 2422 | YYMINORTYPE yylhsminor; |
| 2423 | case 0: /* document ::= statement_list */ |
| 2424 | #line 549 "pikchr.y" |
| 2425 | {pik_render(p,yymsp[0].minor.yy235);} |
| 2426 | #line 2451 "pikchr.c" |
| 2427 | break; |
| 2428 | case 1: /* statement_list ::= statement */ |
| 2429 | #line 552 "pikchr.y" |
| 2430 | { yylhsminor.yy235 = pik_elist_append(p,0,yymsp[0].minor.yy162); } |
| 2431 | #line 2456 "pikchr.c" |
| 2432 | yymsp[0].minor.yy235 = yylhsminor.yy235; |
| 2433 | break; |
| 2434 | case 2: /* statement_list ::= statement_list EOL statement */ |
| 2435 | #line 554 "pikchr.y" |
| 2436 | { yylhsminor.yy235 = pik_elist_append(p,yymsp[-2].minor.yy235,yymsp[0].minor.yy162); } |
| 2437 | #line 2462 "pikchr.c" |
| 2438 | yymsp[-2].minor.yy235 = yylhsminor.yy235; |
| 2439 | break; |
| 2440 | case 3: /* statement ::= */ |
| 2441 | #line 557 "pikchr.y" |
| 2442 | { yymsp[1].minor.yy162 = 0; } |
| 2443 | #line 2468 "pikchr.c" |
| 2444 | break; |
| 2445 | case 4: /* statement ::= direction */ |
| 2446 | #line 558 "pikchr.y" |
| 2447 | { pik_set_direction(p,yymsp[0].minor.yy0.eCode); yylhsminor.yy162=0; } |
| 2448 | #line 2473 "pikchr.c" |
| 2449 | yymsp[0].minor.yy162 = yylhsminor.yy162; |
| 2450 | break; |
| 2451 | case 5: /* statement ::= lvalue ASSIGN rvalue */ |
| 2452 | #line 559 "pikchr.y" |
| 2453 | {pik_set_var(p,&yymsp[-2].minor.yy0,yymsp[0].minor.yy21,&yymsp[-1].minor.yy0); yylhsminor.yy162=0;} |
| 2454 | #line 2479 "pikchr.c" |
| 2455 | yymsp[-2].minor.yy162 = yylhsminor.yy162; |
| 2456 | break; |
| 2457 | case 6: /* statement ::= PLACENAME COLON unnamed_statement */ |
| 2458 | #line 561 "pikchr.y" |
| 2459 | { yylhsminor.yy162 = yymsp[0].minor.yy162; pik_elem_setname(p,yymsp[0].minor.yy162,&yymsp[-2].minor.yy0); } |
| 2460 | #line 2485 "pikchr.c" |
| 2461 | yymsp[-2].minor.yy162 = yylhsminor.yy162; |
| 2462 | break; |
| 2463 | case 7: /* statement ::= PLACENAME COLON position */ |
| 2464 | #line 563 "pikchr.y" |
| 2465 | { yylhsminor.yy162 = pik_elem_new(p,0,0,0); |
| 2466 | if(yylhsminor.yy162){ yylhsminor.yy162->ptAt = yymsp[0].minor.yy63; pik_elem_setname(p,yylhsminor.yy162,&yymsp[-2].minor.yy0); }} |
| 2467 | #line 2492 "pikchr.c" |
| 2468 | yymsp[-2].minor.yy162 = yylhsminor.yy162; |
| 2469 | break; |
| 2470 | case 8: /* statement ::= unnamed_statement */ |
| 2471 | #line 565 "pikchr.y" |
| 2472 | {yylhsminor.yy162 = yymsp[0].minor.yy162;} |
| 2473 | #line 2498 "pikchr.c" |
| 2474 | yymsp[0].minor.yy162 = yylhsminor.yy162; |
| 2475 | break; |
| 2476 | case 9: /* statement ::= print prlist */ |
| 2477 | #line 566 "pikchr.y" |
| 2478 | {pik_append(p,"<br>\n",5); yymsp[-1].minor.yy162=0;} |
| 2479 | #line 2504 "pikchr.c" |
| 2480 | break; |
| 2481 | case 10: /* statement ::= ASSERT LP expr EQ expr RP */ |
| 2482 | #line 571 "pikchr.y" |
| 2483 | {yymsp[-5].minor.yy162=pik_assert(p,yymsp[-3].minor.yy21,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy21);} |
| 2484 | #line 2509 "pikchr.c" |
| 2485 | break; |
| 2486 | case 11: /* statement ::= ASSERT LP position EQ position RP */ |
| 2487 | #line 573 "pikchr.y" |
| 2488 | {yymsp[-5].minor.yy162=pik_position_assert(p,&yymsp[-3].minor.yy63,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy63);} |
| 2489 | #line 2514 "pikchr.c" |
| 2490 | break; |
| 2491 | case 12: /* statement ::= DEFINE ID CODEBLOCK */ |
| 2492 | #line 574 "pikchr.y" |
| 2493 | {yymsp[-2].minor.yy162=0; pik_add_macro(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);} |
| 2494 | #line 2519 "pikchr.c" |
| 2495 | break; |
| 2496 | case 13: /* rvalue ::= PLACENAME */ |
| 2497 | #line 585 "pikchr.y" |
| 2498 | {yylhsminor.yy21 = pik_lookup_color(p,&yymsp[0].minor.yy0);} |
| 2499 | #line 2524 "pikchr.c" |
| 2500 | yymsp[0].minor.yy21 = yylhsminor.yy21; |
| 2501 | break; |
| 2502 | case 14: /* pritem ::= FILL */ |
| 2503 | case 15: /* pritem ::= COLOR */ yytestcase(yyruleno==15); |
| 2504 | case 16: /* pritem ::= THICKNESS */ yytestcase(yyruleno==16); |
| 2505 | #line 590 "pikchr.y" |
| 2506 | {pik_append_num(p,"",pik_value(p,yymsp[0].minor.yy0.z,yymsp[0].minor.yy0.n,0));} |
| 2507 | #line 2532 "pikchr.c" |
| 2508 | break; |
| 2509 | case 17: /* pritem ::= rvalue */ |
| 2510 | #line 593 "pikchr.y" |
| 2511 | {pik_append_num(p,"",yymsp[0].minor.yy21);} |
| 2512 | #line 2537 "pikchr.c" |
| 2513 | break; |
| 2514 | case 18: /* pritem ::= STRING */ |
| 2515 | #line 594 "pikchr.y" |
| 2516 | {pik_append_text(p,yymsp[0].minor.yy0.z+1,yymsp[0].minor.yy0.n-2,0);} |
| 2517 | #line 2542 "pikchr.c" |
| 2518 | break; |
| 2519 | case 19: /* prsep ::= COMMA */ |
| 2520 | #line 595 "pikchr.y" |
| 2521 | {pik_append(p, " ", 1);} |
| 2522 | #line 2547 "pikchr.c" |
| 2523 | break; |
| 2524 | case 20: /* unnamed_statement ::= basetype attribute_list */ |
| 2525 | #line 598 "pikchr.y" |
| 2526 | {yylhsminor.yy162 = yymsp[-1].minor.yy162; pik_after_adding_attributes(p,yylhsminor.yy162);} |
| 2527 | #line 2552 "pikchr.c" |
| 2528 | yymsp[-1].minor.yy162 = yylhsminor.yy162; |
| 2529 | break; |
| 2530 | case 21: /* basetype ::= CLASSNAME */ |
| 2531 | #line 600 "pikchr.y" |
| 2532 | {yylhsminor.yy162 = pik_elem_new(p,&yymsp[0].minor.yy0,0,0); } |
| 2533 | #line 2558 "pikchr.c" |
| 2534 | yymsp[0].minor.yy162 = yylhsminor.yy162; |
| 2535 | break; |
| 2536 | case 22: /* basetype ::= STRING textposition */ |
| 2537 | #line 602 "pikchr.y" |
| 2538 | {yymsp[-1].minor.yy0.eCode = yymsp[0].minor.yy188; yylhsminor.yy162 = pik_elem_new(p,0,&yymsp[-1].minor.yy0,0); } |
| 2539 | #line 2564 "pikchr.c" |
| 2540 | yymsp[-1].minor.yy162 = yylhsminor.yy162; |
| 2541 | break; |
| 2542 | case 23: /* basetype ::= LB savelist statement_list RB */ |
| 2543 | #line 604 "pikchr.y" |
| 2544 | { p->list = yymsp[-2].minor.yy235; yymsp[-3].minor.yy162 = pik_elem_new(p,0,0,yymsp[-1].minor.yy235); if(yymsp[-3].minor.yy162) yymsp[-3].minor.yy162->errTok = yymsp[0].minor.yy0; } |
| 2545 | #line 2570 "pikchr.c" |
| 2546 | break; |
| 2547 | case 24: /* savelist ::= */ |
| 2548 | #line 609 "pikchr.y" |
| 2549 | {yymsp[1].minor.yy235 = p->list; p->list = 0;} |
| 2550 | #line 2575 "pikchr.c" |
| 2551 | break; |
| 2552 | case 25: /* relexpr ::= expr */ |
| 2553 | #line 616 "pikchr.y" |
| 2554 | {yylhsminor.yy72.rAbs = yymsp[0].minor.yy21; yylhsminor.yy72.rRel = 0;} |
| 2555 | #line 2580 "pikchr.c" |
| 2556 | yymsp[0].minor.yy72 = yylhsminor.yy72; |
| 2557 | break; |
| 2558 | case 26: /* relexpr ::= expr PERCENT */ |
| 2559 | #line 617 "pikchr.y" |
| 2560 | {yylhsminor.yy72.rAbs = 0; yylhsminor.yy72.rRel = yymsp[-1].minor.yy21/100;} |
| 2561 | #line 2586 "pikchr.c" |
| 2562 | yymsp[-1].minor.yy72 = yylhsminor.yy72; |
| 2563 | break; |
| 2564 | case 27: /* optrelexpr ::= */ |
| 2565 | #line 619 "pikchr.y" |
| 2566 | {yymsp[1].minor.yy72.rAbs = 0; yymsp[1].minor.yy72.rRel = 1.0;} |
| 2567 | #line 2592 "pikchr.c" |
| 2568 | break; |
| 2569 | case 28: /* attribute_list ::= relexpr alist */ |
| 2570 | #line 621 "pikchr.y" |
| 2571 | {pik_add_direction(p,0,&yymsp[-1].minor.yy72);} |
| 2572 | #line 2597 "pikchr.c" |
| 2573 | break; |
| 2574 | case 29: /* attribute ::= numproperty relexpr */ |
| 2575 | #line 625 "pikchr.y" |
| 2576 | { pik_set_numprop(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy72); } |
| 2577 | #line 2602 "pikchr.c" |
| 2578 | break; |
| 2579 | case 30: /* attribute ::= dashproperty expr */ |
| 2580 | #line 626 "pikchr.y" |
| 2581 | { pik_set_dashed(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy21); } |
| 2582 | #line 2607 "pikchr.c" |
| 2583 | break; |
| 2584 | case 31: /* attribute ::= dashproperty */ |
| 2585 | #line 627 "pikchr.y" |
| 2586 | { pik_set_dashed(p,&yymsp[0].minor.yy0,0); } |
| 2587 | #line 2612 "pikchr.c" |
| 2588 | break; |
| 2589 | case 32: /* attribute ::= colorproperty rvalue */ |
| 2590 | #line 628 "pikchr.y" |
| 2591 | { pik_set_clrprop(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy21); } |
| 2592 | #line 2617 "pikchr.c" |
| 2593 | break; |
| 2594 | case 33: /* attribute ::= go direction optrelexpr */ |
| 2595 | #line 629 "pikchr.y" |
| 2596 | { pik_add_direction(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy72);} |
| 2597 | #line 2622 "pikchr.c" |
| 2598 | break; |
| 2599 | case 34: /* attribute ::= go direction even position */ |
| 2600 | #line 630 "pikchr.y" |
| 2601 | {pik_evenwith(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy63);} |
| 2602 | #line 2627 "pikchr.c" |
| 2603 | break; |
| 2604 | case 35: /* attribute ::= CLOSE */ |
| 2605 | #line 631 "pikchr.y" |
| 2606 | { pik_close_path(p,&yymsp[0].minor.yy0); } |
| 2607 | #line 2632 "pikchr.c" |
| 2608 | break; |
| 2609 | case 36: /* attribute ::= CHOP */ |
| 2610 | #line 632 "pikchr.y" |
| 2611 | { p->cur->bChop = 1; } |
| 2612 | #line 2637 "pikchr.c" |
| 2613 | break; |
| 2614 | case 37: /* attribute ::= FROM position */ |
| 2615 | #line 633 "pikchr.y" |
| 2616 | { pik_set_from(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy63); } |
| 2617 | #line 2642 "pikchr.c" |
| 2618 | break; |
| 2619 | case 38: /* attribute ::= TO position */ |
| 2620 | #line 634 "pikchr.y" |
| 2621 | { pik_add_to(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy63); } |
| 2622 | #line 2647 "pikchr.c" |
| 2623 | break; |
| 2624 | case 39: /* attribute ::= THEN */ |
| 2625 | #line 635 "pikchr.y" |
| 2626 | { pik_then(p, &yymsp[0].minor.yy0, p->cur); } |
| 2627 | #line 2652 "pikchr.c" |
| 2628 | break; |
| 2629 | case 40: /* attribute ::= THEN optrelexpr HEADING expr */ |
| 2630 | case 42: /* attribute ::= GO optrelexpr HEADING expr */ yytestcase(yyruleno==42); |
| 2631 | #line 637 "pikchr.y" |
| 2632 | {pik_move_hdg(p,&yymsp[-2].minor.yy72,&yymsp[-1].minor.yy0,yymsp[0].minor.yy21,0,&yymsp[-3].minor.yy0);} |
| 2633 | #line 2658 "pikchr.c" |
| 2634 | break; |
| 2635 | case 41: /* attribute ::= THEN optrelexpr EDGEPT */ |
| 2636 | case 43: /* attribute ::= GO optrelexpr EDGEPT */ yytestcase(yyruleno==43); |
| 2637 | #line 638 "pikchr.y" |
| 2638 | {pik_move_hdg(p,&yymsp[-1].minor.yy72,0,0,&yymsp[0].minor.yy0,&yymsp[-2].minor.yy0);} |
| 2639 | #line 2664 "pikchr.c" |
| 2640 | break; |
| 2641 | case 44: /* attribute ::= AT position */ |
| 2642 | #line 643 "pikchr.y" |
| 2643 | { pik_set_at(p,0,&yymsp[0].minor.yy63,&yymsp[-1].minor.yy0); } |
| 2644 | #line 2669 "pikchr.c" |
| 2645 | break; |
| 2646 | case 45: /* attribute ::= SAME */ |
| 2647 | #line 645 "pikchr.y" |
| 2648 | {pik_same(p,0,&yymsp[0].minor.yy0);} |
| 2649 | #line 2674 "pikchr.c" |
| 2650 | break; |
| 2651 | case 46: /* attribute ::= SAME AS object */ |
| 2652 | #line 646 "pikchr.y" |
| 2653 | {pik_same(p,yymsp[0].minor.yy162,&yymsp[-2].minor.yy0);} |
| 2654 | #line 2679 "pikchr.c" |
| 2655 | break; |
| 2656 | case 47: /* attribute ::= STRING textposition */ |
| 2657 | #line 647 "pikchr.y" |
| 2658 | {pik_add_txt(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy188);} |
| 2659 | #line 2684 "pikchr.c" |
| 2660 | break; |
| 2661 | case 48: /* attribute ::= FIT */ |
| 2662 | #line 648 "pikchr.y" |
| 2663 | {pik_size_to_fit(p,&yymsp[0].minor.yy0,3); } |
| 2664 | #line 2689 "pikchr.c" |
| 2665 | break; |
| 2666 | case 49: /* attribute ::= BEHIND object */ |
| 2667 | #line 649 "pikchr.y" |
| 2668 | {pik_behind(p,yymsp[0].minor.yy162);} |
| 2669 | #line 2694 "pikchr.c" |
| 2670 | break; |
| 2671 | case 50: /* withclause ::= DOT_E edge AT position */ |
| 2672 | case 51: /* withclause ::= edge AT position */ yytestcase(yyruleno==51); |
| 2673 | #line 657 "pikchr.y" |
| 2674 | { pik_set_at(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy63,&yymsp[-1].minor.yy0); } |
| 2675 | #line 2700 "pikchr.c" |
| 2676 | break; |
| 2677 | case 52: /* numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */ |
| 2678 | #line 661 "pikchr.y" |
| 2679 | {yylhsminor.yy0 = yymsp[0].minor.yy0;} |
| 2680 | #line 2705 "pikchr.c" |
| 2681 | yymsp[0].minor.yy0 = yylhsminor.yy0; |
| 2682 | break; |
| 2683 | case 53: /* boolproperty ::= CW */ |
| 2684 | #line 672 "pikchr.y" |
| 2685 | {p->cur->cw = 1;} |
| 2686 | #line 2711 "pikchr.c" |
| 2687 | break; |
| 2688 | case 54: /* boolproperty ::= CCW */ |
| 2689 | #line 673 "pikchr.y" |
| 2690 | {p->cur->cw = 0;} |
| 2691 | #line 2716 "pikchr.c" |
| 2692 | break; |
| 2693 | case 55: /* boolproperty ::= LARROW */ |
| 2694 | #line 674 "pikchr.y" |
| 2695 | {p->cur->larrow=1; p->cur->rarrow=0; } |
| 2696 | #line 2721 "pikchr.c" |
| 2697 | break; |
| 2698 | case 56: /* boolproperty ::= RARROW */ |
| 2699 | #line 675 "pikchr.y" |
| 2700 | {p->cur->larrow=0; p->cur->rarrow=1; } |
| 2701 | #line 2726 "pikchr.c" |
| 2702 | break; |
| 2703 | case 57: /* boolproperty ::= LRARROW */ |
| 2704 | #line 676 "pikchr.y" |
| 2705 | {p->cur->larrow=1; p->cur->rarrow=1; } |
| 2706 | #line 2731 "pikchr.c" |
| 2707 | break; |
| 2708 | case 58: /* boolproperty ::= INVIS */ |
| 2709 | #line 677 "pikchr.y" |
| 2710 | {p->cur->sw = -0.00001;} |
| 2711 | #line 2736 "pikchr.c" |
| 2712 | break; |
| 2713 | case 59: /* boolproperty ::= THICK */ |
| 2714 | #line 678 "pikchr.y" |
| 2715 | {p->cur->sw *= 1.5;} |
| 2716 | #line 2741 "pikchr.c" |
| 2717 | break; |
| 2718 | case 60: /* boolproperty ::= THIN */ |
| 2719 | #line 679 "pikchr.y" |
| 2720 | {p->cur->sw *= 0.67;} |
| 2721 | #line 2746 "pikchr.c" |
| 2722 | break; |
| 2723 | case 61: /* boolproperty ::= SOLID */ |
| 2724 | #line 680 "pikchr.y" |
| 2725 | {p->cur->sw = pik_value(p,"thickness",9,0); |
| 2726 | p->cur->dotted = p->cur->dashed = 0.0;} |
| 2727 | #line 2752 "pikchr.c" |
| 2728 | break; |
| 2729 | case 62: /* textposition ::= */ |
| 2730 | #line 683 "pikchr.y" |
| 2731 | {yymsp[1].minor.yy188 = 0;} |
| 2732 | #line 2757 "pikchr.c" |
| 2733 | break; |
| 2734 | case 63: /* textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL */ |
| 2735 | #line 686 "pikchr.y" |
| 2736 | {yylhsminor.yy188 = (short int)pik_text_position(yymsp[-1].minor.yy188,&yymsp[0].minor.yy0);} |
| 2737 | #line 2762 "pikchr.c" |
| 2738 | yymsp[-1].minor.yy188 = yylhsminor.yy188; |
| 2739 | break; |
| 2740 | case 64: /* position ::= expr COMMA expr */ |
| 2741 | #line 689 "pikchr.y" |
| 2742 | {yylhsminor.yy63.x=yymsp[-2].minor.yy21; yylhsminor.yy63.y=yymsp[0].minor.yy21;} |
| 2743 | #line 2768 "pikchr.c" |
| 2744 | yymsp[-2].minor.yy63 = yylhsminor.yy63; |
| 2745 | break; |
| 2746 | case 65: /* position ::= place PLUS expr COMMA expr */ |
| 2747 | #line 691 "pikchr.y" |
| 2748 | {yylhsminor.yy63.x=yymsp[-4].minor.yy63.x+yymsp[-2].minor.yy21; yylhsminor.yy63.y=yymsp[-4].minor.yy63.y+yymsp[0].minor.yy21;} |
| 2749 | #line 2774 "pikchr.c" |
| 2750 | yymsp[-4].minor.yy63 = yylhsminor.yy63; |
| 2751 | break; |
| 2752 | case 66: /* position ::= place MINUS expr COMMA expr */ |
| 2753 | #line 692 "pikchr.y" |
| 2754 | {yylhsminor.yy63.x=yymsp[-4].minor.yy63.x-yymsp[-2].minor.yy21; yylhsminor.yy63.y=yymsp[-4].minor.yy63.y-yymsp[0].minor.yy21;} |
| 2755 | #line 2780 "pikchr.c" |
| 2756 | yymsp[-4].minor.yy63 = yylhsminor.yy63; |
| 2757 | break; |
| 2758 | case 67: /* position ::= place PLUS LP expr COMMA expr RP */ |
| 2759 | #line 694 "pikchr.y" |
| 2760 | {yylhsminor.yy63.x=yymsp[-6].minor.yy63.x+yymsp[-3].minor.yy21; yylhsminor.yy63.y=yymsp[-6].minor.yy63.y+yymsp[-1].minor.yy21;} |
| 2761 | #line 2786 "pikchr.c" |
| 2762 | yymsp[-6].minor.yy63 = yylhsminor.yy63; |
| 2763 | break; |
| 2764 | case 68: /* position ::= place MINUS LP expr COMMA expr RP */ |
| 2765 | #line 696 "pikchr.y" |
| 2766 | {yylhsminor.yy63.x=yymsp[-6].minor.yy63.x-yymsp[-3].minor.yy21; yylhsminor.yy63.y=yymsp[-6].minor.yy63.y-yymsp[-1].minor.yy21;} |
| 2767 | #line 2792 "pikchr.c" |
| 2768 | yymsp[-6].minor.yy63 = yylhsminor.yy63; |
| 2769 | break; |
| 2770 | case 69: /* position ::= LP position COMMA position RP */ |
| 2771 | #line 697 "pikchr.y" |
| 2772 | {yymsp[-4].minor.yy63.x=yymsp[-3].minor.yy63.x; yymsp[-4].minor.yy63.y=yymsp[-1].minor.yy63.y;} |
| 2773 | #line 2798 "pikchr.c" |
| 2774 | break; |
| 2775 | case 70: /* position ::= LP position RP */ |
| 2776 | #line 698 "pikchr.y" |
| 2777 | {yymsp[-2].minor.yy63=yymsp[-1].minor.yy63;} |
| 2778 | #line 2803 "pikchr.c" |
| 2779 | break; |
| 2780 | case 71: /* position ::= expr between position AND position */ |
| 2781 | #line 700 "pikchr.y" |
| 2782 | {yylhsminor.yy63 = pik_position_between(yymsp[-4].minor.yy21,yymsp[-2].minor.yy63,yymsp[0].minor.yy63);} |
| 2783 | #line 2808 "pikchr.c" |
| 2784 | yymsp[-4].minor.yy63 = yylhsminor.yy63; |
| 2785 | break; |
| 2786 | case 72: /* position ::= expr LT position COMMA position GT */ |
| 2787 | #line 702 "pikchr.y" |
| 2788 | {yylhsminor.yy63 = pik_position_between(yymsp[-5].minor.yy21,yymsp[-3].minor.yy63,yymsp[-1].minor.yy63);} |
| 2789 | #line 2814 "pikchr.c" |
| 2790 | yymsp[-5].minor.yy63 = yylhsminor.yy63; |
| 2791 | break; |
| 2792 | case 73: /* position ::= expr ABOVE position */ |
| 2793 | #line 703 "pikchr.y" |
| 2794 | {yylhsminor.yy63=yymsp[0].minor.yy63; yylhsminor.yy63.y += yymsp[-2].minor.yy21;} |
| 2795 | #line 2820 "pikchr.c" |
| 2796 | yymsp[-2].minor.yy63 = yylhsminor.yy63; |
| 2797 | break; |
| 2798 | case 74: /* position ::= expr BELOW position */ |
| 2799 | #line 704 "pikchr.y" |
| 2800 | {yylhsminor.yy63=yymsp[0].minor.yy63; yylhsminor.yy63.y -= yymsp[-2].minor.yy21;} |
| 2801 | #line 2826 "pikchr.c" |
| 2802 | yymsp[-2].minor.yy63 = yylhsminor.yy63; |
| 2803 | break; |
| 2804 | case 75: /* position ::= expr LEFT OF position */ |
| 2805 | #line 705 "pikchr.y" |
| 2806 | {yylhsminor.yy63=yymsp[0].minor.yy63; yylhsminor.yy63.x -= yymsp[-3].minor.yy21;} |
| 2807 | #line 2832 "pikchr.c" |
| 2808 | yymsp[-3].minor.yy63 = yylhsminor.yy63; |
| 2809 | break; |
| 2810 | case 76: /* position ::= expr RIGHT OF position */ |
| 2811 | #line 706 "pikchr.y" |
| 2812 | {yylhsminor.yy63=yymsp[0].minor.yy63; yylhsminor.yy63.x += yymsp[-3].minor.yy21;} |
| 2813 | #line 2838 "pikchr.c" |
| 2814 | yymsp[-3].minor.yy63 = yylhsminor.yy63; |
| 2815 | break; |
| 2816 | case 77: /* position ::= expr ON HEADING EDGEPT OF position */ |
| 2817 | #line 708 "pikchr.y" |
| 2818 | {yylhsminor.yy63 = pik_position_at_hdg(yymsp[-5].minor.yy21,&yymsp[-2].minor.yy0,yymsp[0].minor.yy63);} |
| 2819 | #line 2844 "pikchr.c" |
| 2820 | yymsp[-5].minor.yy63 = yylhsminor.yy63; |
| 2821 | break; |
| 2822 | case 78: /* position ::= expr HEADING EDGEPT OF position */ |
| 2823 | #line 710 "pikchr.y" |
| 2824 | {yylhsminor.yy63 = pik_position_at_hdg(yymsp[-4].minor.yy21,&yymsp[-2].minor.yy0,yymsp[0].minor.yy63);} |
| 2825 | #line 2850 "pikchr.c" |
| 2826 | yymsp[-4].minor.yy63 = yylhsminor.yy63; |
| 2827 | break; |
| 2828 | case 79: /* position ::= expr EDGEPT OF position */ |
| 2829 | #line 712 "pikchr.y" |
| 2830 | {yylhsminor.yy63 = pik_position_at_hdg(yymsp[-3].minor.yy21,&yymsp[-2].minor.yy0,yymsp[0].minor.yy63);} |
| 2831 | #line 2856 "pikchr.c" |
| 2832 | yymsp[-3].minor.yy63 = yylhsminor.yy63; |
| 2833 | break; |
| 2834 | case 80: /* position ::= expr ON HEADING expr FROM position */ |
| 2835 | #line 714 "pikchr.y" |
| 2836 | {yylhsminor.yy63 = pik_position_at_angle(yymsp[-5].minor.yy21,yymsp[-2].minor.yy21,yymsp[0].minor.yy63);} |
| 2837 | #line 2862 "pikchr.c" |
| 2838 | yymsp[-5].minor.yy63 = yylhsminor.yy63; |
| 2839 | break; |
| 2840 | case 81: /* position ::= expr HEADING expr FROM position */ |
| 2841 | #line 716 "pikchr.y" |
| 2842 | {yylhsminor.yy63 = pik_position_at_angle(yymsp[-4].minor.yy21,yymsp[-2].minor.yy21,yymsp[0].minor.yy63);} |
| 2843 | #line 2868 "pikchr.c" |
| 2844 | yymsp[-4].minor.yy63 = yylhsminor.yy63; |
| 2845 | break; |
| 2846 | case 82: /* place ::= edge OF object */ |
| 2847 | #line 728 "pikchr.y" |
| 2848 | {yylhsminor.yy63 = pik_place_of_elem(p,yymsp[0].minor.yy162,&yymsp[-2].minor.yy0);} |
| 2849 | #line 2874 "pikchr.c" |
| 2850 | yymsp[-2].minor.yy63 = yylhsminor.yy63; |
| 2851 | break; |
| 2852 | case 83: /* place2 ::= object */ |
| 2853 | #line 729 "pikchr.y" |
| 2854 | {yylhsminor.yy63 = pik_place_of_elem(p,yymsp[0].minor.yy162,0);} |
| 2855 | #line 2880 "pikchr.c" |
| 2856 | yymsp[0].minor.yy63 = yylhsminor.yy63; |
| 2857 | break; |
| 2858 | case 84: /* place2 ::= object DOT_E edge */ |
| 2859 | #line 730 "pikchr.y" |
| 2860 | {yylhsminor.yy63 = pik_place_of_elem(p,yymsp[-2].minor.yy162,&yymsp[0].minor.yy0);} |
| 2861 | #line 2886 "pikchr.c" |
| 2862 | yymsp[-2].minor.yy63 = yylhsminor.yy63; |
| 2863 | break; |
| 2864 | case 85: /* place2 ::= NTH VERTEX OF object */ |
| 2865 | #line 731 "pikchr.y" |
| 2866 | {yylhsminor.yy63 = pik_nth_vertex(p,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,yymsp[0].minor.yy162);} |
| 2867 | #line 2892 "pikchr.c" |
| 2868 | yymsp[-3].minor.yy63 = yylhsminor.yy63; |
| 2869 | break; |
| 2870 | case 86: /* object ::= nth */ |
| 2871 | #line 743 "pikchr.y" |
| 2872 | {yylhsminor.yy162 = pik_find_nth(p,0,&yymsp[0].minor.yy0);} |
| 2873 | #line 2898 "pikchr.c" |
| 2874 | yymsp[0].minor.yy162 = yylhsminor.yy162; |
| 2875 | break; |
| 2876 | case 87: /* object ::= nth OF|IN object */ |
| 2877 | #line 744 "pikchr.y" |
| 2878 | {yylhsminor.yy162 = pik_find_nth(p,yymsp[0].minor.yy162,&yymsp[-2].minor.yy0);} |
| 2879 | #line 2904 "pikchr.c" |
| 2880 | yymsp[-2].minor.yy162 = yylhsminor.yy162; |
| 2881 | break; |
| 2882 | case 88: /* objectname ::= THIS */ |
| 2883 | #line 746 "pikchr.y" |
| 2884 | {yymsp[0].minor.yy162 = p->cur;} |
| 2885 | #line 2910 "pikchr.c" |
| 2886 | break; |
| 2887 | case 89: /* objectname ::= PLACENAME */ |
| 2888 | #line 747 "pikchr.y" |
| 2889 | {yylhsminor.yy162 = pik_find_byname(p,0,&yymsp[0].minor.yy0);} |
| 2890 | #line 2915 "pikchr.c" |
| 2891 | yymsp[0].minor.yy162 = yylhsminor.yy162; |
| 2892 | break; |
| 2893 | case 90: /* objectname ::= objectname DOT_U PLACENAME */ |
| 2894 | #line 749 "pikchr.y" |
| 2895 | {yylhsminor.yy162 = pik_find_byname(p,yymsp[-2].minor.yy162,&yymsp[0].minor.yy0);} |
| 2896 | #line 2921 "pikchr.c" |
| 2897 | yymsp[-2].minor.yy162 = yylhsminor.yy162; |
| 2898 | break; |
| 2899 | case 91: /* nth ::= NTH CLASSNAME */ |
| 2900 | #line 751 "pikchr.y" |
| 2901 | {yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = pik_nth_value(p,&yymsp[-1].minor.yy0); } |
| 2902 | #line 2927 "pikchr.c" |
| 2903 | yymsp[-1].minor.yy0 = yylhsminor.yy0; |
| 2904 | break; |
| 2905 | case 92: /* nth ::= NTH LAST CLASSNAME */ |
| 2906 | #line 752 "pikchr.y" |
| 2907 | {yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -pik_nth_value(p,&yymsp[-2].minor.yy0); } |
| 2908 | #line 2933 "pikchr.c" |
| 2909 | yymsp[-2].minor.yy0 = yylhsminor.yy0; |
| 2910 | break; |
| 2911 | case 93: /* nth ::= LAST CLASSNAME */ |
| 2912 | #line 753 "pikchr.y" |
| 2913 | {yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.eCode = -1;} |
| 2914 | #line 2939 "pikchr.c" |
| 2915 | break; |
| 2916 | case 94: /* nth ::= LAST */ |
| 2917 | #line 754 "pikchr.y" |
| 2918 | {yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -1;} |
| 2919 | #line 2944 "pikchr.c" |
| 2920 | yymsp[0].minor.yy0 = yylhsminor.yy0; |
| 2921 | break; |
| 2922 | case 95: /* nth ::= NTH LB RB */ |
| 2923 | #line 755 "pikchr.y" |
| 2924 | {yylhsminor.yy0=yymsp[-1].minor.yy0; yylhsminor.yy0.eCode = pik_nth_value(p,&yymsp[-2].minor.yy0);} |
| 2925 | #line 2950 "pikchr.c" |
| 2926 | yymsp[-2].minor.yy0 = yylhsminor.yy0; |
| 2927 | break; |
| 2928 | case 96: /* nth ::= NTH LAST LB RB */ |
| 2929 | #line 756 "pikchr.y" |
| 2930 | {yylhsminor.yy0=yymsp[-1].minor.yy0; yylhsminor.yy0.eCode = -pik_nth_value(p,&yymsp[-3].minor.yy0);} |
| 2931 | #line 2956 "pikchr.c" |
| 2932 | yymsp[-3].minor.yy0 = yylhsminor.yy0; |
| 2933 | break; |
| 2934 | case 97: /* nth ::= LAST LB RB */ |
| 2935 | #line 757 "pikchr.y" |
| 2936 | {yymsp[-2].minor.yy0=yymsp[-1].minor.yy0; yymsp[-2].minor.yy0.eCode = -1; } |
| 2937 | #line 2962 "pikchr.c" |
| 2938 | break; |
| 2939 | case 98: /* expr ::= expr PLUS expr */ |
| 2940 | #line 759 "pikchr.y" |
| 2941 | {yylhsminor.yy21=yymsp[-2].minor.yy21+yymsp[0].minor.yy21;} |
| 2942 | #line 2967 "pikchr.c" |
| 2943 | yymsp[-2].minor.yy21 = yylhsminor.yy21; |
| 2944 | break; |
| 2945 | case 99: /* expr ::= expr MINUS expr */ |
| 2946 | #line 760 "pikchr.y" |
| 2947 | {yylhsminor.yy21=yymsp[-2].minor.yy21-yymsp[0].minor.yy21;} |
| 2948 | #line 2973 "pikchr.c" |
| 2949 | yymsp[-2].minor.yy21 = yylhsminor.yy21; |
| 2950 | break; |
| 2951 | case 100: /* expr ::= expr STAR expr */ |
| 2952 | #line 761 "pikchr.y" |
| 2953 | {yylhsminor.yy21=yymsp[-2].minor.yy21*yymsp[0].minor.yy21;} |
| 2954 | #line 2979 "pikchr.c" |
| 2955 | yymsp[-2].minor.yy21 = yylhsminor.yy21; |
| 2956 | break; |
| 2957 | case 101: /* expr ::= expr SLASH expr */ |
| 2958 | #line 762 "pikchr.y" |
| 2959 | { |
| 2960 | if( yymsp[0].minor.yy21==0.0 ){ pik_error(p, &yymsp[-1].minor.yy0, "division by zero"); yylhsminor.yy21 = 0.0; } |
| 2961 | else{ yylhsminor.yy21 = yymsp[-2].minor.yy21/yymsp[0].minor.yy21; } |
| 2962 | } |
| 2963 | #line 2988 "pikchr.c" |
| 2964 | yymsp[-2].minor.yy21 = yylhsminor.yy21; |
| 2965 | break; |
| 2966 | case 102: /* expr ::= MINUS expr */ |
| 2967 | #line 766 "pikchr.y" |
| 2968 | {yymsp[-1].minor.yy21=-yymsp[0].minor.yy21;} |
| 2969 | #line 2994 "pikchr.c" |
| 2970 | break; |
| 2971 | case 103: /* expr ::= PLUS expr */ |
| 2972 | #line 767 "pikchr.y" |
| 2973 | {yymsp[-1].minor.yy21=yymsp[0].minor.yy21;} |
| 2974 | #line 2999 "pikchr.c" |
| 2975 | break; |
| 2976 | case 104: /* expr ::= LP expr RP */ |
| 2977 | #line 768 "pikchr.y" |
| 2978 | {yymsp[-2].minor.yy21=yymsp[-1].minor.yy21;} |
| 2979 | #line 3004 "pikchr.c" |
| 2980 | break; |
| 2981 | case 105: /* expr ::= LP FILL|COLOR|THICKNESS RP */ |
| 2982 | #line 769 "pikchr.y" |
| 2983 | {yymsp[-2].minor.yy21=pik_get_var(p,&yymsp[-1].minor.yy0);} |
| 2984 | #line 3009 "pikchr.c" |
| 2985 | break; |
| 2986 | case 106: /* expr ::= NUMBER */ |
| 2987 | #line 770 "pikchr.y" |
| 2988 | {yylhsminor.yy21=pik_atof(&yymsp[0].minor.yy0);} |
| 2989 | #line 3014 "pikchr.c" |
| 2990 | yymsp[0].minor.yy21 = yylhsminor.yy21; |
| 2991 | break; |
| 2992 | case 107: /* expr ::= ID */ |
| 2993 | #line 771 "pikchr.y" |
| 2994 | {yylhsminor.yy21=pik_get_var(p,&yymsp[0].minor.yy0);} |
| 2995 | #line 3020 "pikchr.c" |
| 2996 | yymsp[0].minor.yy21 = yylhsminor.yy21; |
| 2997 | break; |
| 2998 | case 108: /* expr ::= FUNC1 LP expr RP */ |
| 2999 | #line 772 "pikchr.y" |
| 3000 | {yylhsminor.yy21 = pik_func(p,&yymsp[-3].minor.yy0,yymsp[-1].minor.yy21,0.0);} |
| 3001 | #line 3026 "pikchr.c" |
| 3002 | yymsp[-3].minor.yy21 = yylhsminor.yy21; |
| 3003 | break; |
| 3004 | case 109: /* expr ::= FUNC2 LP expr COMMA expr RP */ |
| 3005 | #line 773 "pikchr.y" |
| 3006 | {yylhsminor.yy21 = pik_func(p,&yymsp[-5].minor.yy0,yymsp[-3].minor.yy21,yymsp[-1].minor.yy21);} |
| 3007 | #line 3032 "pikchr.c" |
| 3008 | yymsp[-5].minor.yy21 = yylhsminor.yy21; |
| 3009 | break; |
| 3010 | case 110: /* expr ::= DIST LP position COMMA position RP */ |
| 3011 | #line 774 "pikchr.y" |
| 3012 | {yymsp[-5].minor.yy21 = pik_dist(&yymsp[-3].minor.yy63,&yymsp[-1].minor.yy63);} |
| 3013 | #line 3038 "pikchr.c" |
| 3014 | break; |
| 3015 | case 111: /* expr ::= place2 DOT_XY X */ |
| 3016 | #line 775 "pikchr.y" |
| 3017 | {yylhsminor.yy21 = yymsp[-2].minor.yy63.x;} |
| 3018 | #line 3043 "pikchr.c" |
| 3019 | yymsp[-2].minor.yy21 = yylhsminor.yy21; |
| 3020 | break; |
| 3021 | case 112: /* expr ::= place2 DOT_XY Y */ |
| 3022 | #line 776 "pikchr.y" |
| 3023 | {yylhsminor.yy21 = yymsp[-2].minor.yy63.y;} |
| 3024 | #line 3049 "pikchr.c" |
| 3025 | yymsp[-2].minor.yy21 = yylhsminor.yy21; |
| 3026 | break; |
| 3027 | case 113: /* expr ::= object DOT_L numproperty */ |
| 3028 | case 114: /* expr ::= object DOT_L dashproperty */ yytestcase(yyruleno==114); |
| 3029 | case 115: /* expr ::= object DOT_L colorproperty */ yytestcase(yyruleno==115); |
| 3030 | #line 777 "pikchr.y" |
| 3031 | {yylhsminor.yy21=pik_property_of(yymsp[-2].minor.yy162,&yymsp[0].minor.yy0);} |
| 3032 | #line 3057 "pikchr.c" |
| 3033 | yymsp[-2].minor.yy21 = yylhsminor.yy21; |
| 3034 | break; |
| 3035 | default: |
| 3036 | /* (116) lvalue ::= ID */ yytestcase(yyruleno==116); |
| 3037 | /* (117) lvalue ::= FILL */ yytestcase(yyruleno==117); |
| 3038 | /* (118) lvalue ::= COLOR */ yytestcase(yyruleno==118); |
| @@ -3130,19 +3183,19 @@ | |
| 3130 | ){ |
| 3131 | pik_parserARG_FETCH |
| 3132 | pik_parserCTX_FETCH |
| 3133 | #define TOKEN yyminor |
| 3134 | /************ Begin %syntax_error code ****************************************/ |
| 3135 | #line 537 "pikchr.y" |
| 3136 | |
| 3137 | if( TOKEN.z && TOKEN.z[0] ){ |
| 3138 | pik_error(p, &TOKEN, "syntax error"); |
| 3139 | }else{ |
| 3140 | pik_error(p, 0, "syntax error"); |
| 3141 | } |
| 3142 | UNUSED_PARAMETER(yymajor); |
| 3143 | #line 3168 "pikchr.c" |
| 3144 | /************ End %syntax_error code ******************************************/ |
| 3145 | pik_parserARG_STORE /* Suppress warning about unused %extra_argument variable */ |
| 3146 | pik_parserCTX_STORE |
| 3147 | } |
| 3148 | |
| @@ -3407,11 +3460,11 @@ | |
| 3407 | #else |
| 3408 | (void)iToken; |
| 3409 | return 0; |
| 3410 | #endif |
| 3411 | } |
| 3412 | #line 782 "pikchr.y" |
| 3413 | |
| 3414 | |
| 3415 | |
| 3416 | /* Chart of the 148 official CSS color names with their |
| 3417 | ** corresponding RGB values thru Color Module Level 4: |
| @@ -3634,42 +3687,57 @@ | |
| 3634 | ** than true arcs. Multiple reasons: (1) the legacy-PIC parameters |
| 3635 | ** that control arcs are obscure and I could not figure out what they |
| 3636 | ** mean based on available documentation. (2) Arcs are rarely used, |
| 3637 | ** and so do not seem that important. |
| 3638 | */ |
| 3639 | static PPoint arcControlPoint(int cw, PPoint f, PPoint t, PNum rScale){ |
| 3640 | PPoint m; |
| 3641 | PNum dx, dy; |
| 3642 | m.x = 0.5*(f.x+t.x); |
| 3643 | m.y = 0.5*(f.y+t.y); |
| 3644 | dx = t.x - f.x; |
| 3645 | dy = t.y - f.y; |
| 3646 | if( cw ){ |
| 3647 | m.x -= 0.5*rScale*dy; |
| 3648 | m.y += 0.5*rScale*dx; |
| 3649 | }else{ |
| 3650 | m.x += 0.5*rScale*dy; |
| 3651 | m.y -= 0.5*rScale*dx; |
| 3652 | } |
| 3653 | return m; |
| 3654 | } |
| 3655 | static void arcCheck(Pik *p, PObj *pObj){ |
| 3656 | PPoint m; |
| 3657 | if( p->nTPath>2 ){ |
| 3658 | pik_error(p, &pObj->errTok, "arc geometry error"); |
| 3659 | return; |
| 3660 | } |
| 3661 | m = arcControlPoint(pObj->cw, p->aTPath[0], p->aTPath[1], 0.5); |
| 3662 | pik_bbox_add_xy(&pObj->bbox, m.x, m.y); |
| 3663 | } |
| 3664 | static void arcRender(Pik *p, PObj *pObj){ |
| 3665 | PPoint f, m, t; |
| 3666 | if( pObj->nPath<2 ) return; |
| 3667 | if( pObj->sw<0.0 ) return; |
| 3668 | f = pObj->aPath[0]; |
| 3669 | t = pObj->aPath[1]; |
| 3670 | m = arcControlPoint(pObj->cw,f,t,1.0); |
| 3671 | if( pObj->larrow ){ |
| 3672 | pik_draw_arrowhead(p,&m,&f,pObj); |
| 3673 | } |
| 3674 | if( pObj->rarrow ){ |
| 3675 | pik_draw_arrowhead(p,&m,&t,pObj); |
| @@ -4338,11 +4406,11 @@ | |
| 4338 | static PPoint textOffset(Pik *p, PObj *pObj, int cp){ |
| 4339 | /* Automatically slim-down the width and height of text |
| 4340 | ** statements so that the bounding box tightly encloses the text, |
| 4341 | ** then get boxOffset() to do the offset computation. |
| 4342 | */ |
| 4343 | pik_size_to_fit(p, &pObj->errTok,3); |
| 4344 | return boxOffset(p, pObj, cp); |
| 4345 | } |
| 4346 | static void textRender(Pik *p, PObj *pObj){ |
| 4347 | pik_append_txt(p, pObj, 0); |
| 4348 | } |
| @@ -6347,16 +6415,15 @@ | |
| 6347 | ** |
| 6348 | ** 1: Fit horizontally only |
| 6349 | ** 2: Fit vertically only |
| 6350 | ** 3: Fit both ways |
| 6351 | */ |
| 6352 | static void pik_size_to_fit(Pik *p, PToken *pFit, int eWhich){ |
| 6353 | PObj *pObj; |
| 6354 | PNum w, h; |
| 6355 | PBox bbox; |
| 6356 | if( p->nErr ) return; |
| 6357 | pObj = p->cur; |
| 6358 | |
| 6359 | if( pObj->nTxt==0 ){ |
| 6360 | pik_error(0, pFit, "no text to fit to"); |
| 6361 | return; |
| 6362 | } |
| @@ -6495,13 +6562,13 @@ | |
| 6495 | unsigned int i; |
| 6496 | mid = (first+last)/2; |
| 6497 | zClr = aColor[mid].zName; |
| 6498 | for(i=0; i<pId->n; i++){ |
| 6499 | c1 = zClr[i]&0x7f; |
| 6500 | if( isupper(c1) ) c1 = tolower(c1); |
| 6501 | c2 = pId->z[i]&0x7f; |
| 6502 | if( isupper(c2) ) c2 = tolower(c2); |
| 6503 | c = c2 - c1; |
| 6504 | if( c ) break; |
| 6505 | } |
| 6506 | if( c==0 && aColor[mid].zName[pId->n] ) c = -1; |
| 6507 | if( c==0 ) return (double)aColor[mid].val; |
| @@ -6896,20 +6963,20 @@ | |
| 6896 | */ |
| 6897 | if( pObj->h<=0.0 ){ |
| 6898 | if( pObj->nTxt==0 ){ |
| 6899 | pObj->h = 0.0; |
| 6900 | }else if( pObj->w<=0.0 ){ |
| 6901 | pik_size_to_fit(p, &pObj->errTok, 3); |
| 6902 | }else{ |
| 6903 | pik_size_to_fit(p, &pObj->errTok, 2); |
| 6904 | } |
| 6905 | } |
| 6906 | if( pObj->w<=0.0 ){ |
| 6907 | if( pObj->nTxt==0 ){ |
| 6908 | pObj->w = 0.0; |
| 6909 | }else{ |
| 6910 | pik_size_to_fit(p, &pObj->errTok, 1); |
| 6911 | } |
| 6912 | } |
| 6913 | ofst = pik_elem_offset(p, pObj, pObj->eWith); |
| 6914 | dx = (pObj->with.x - ofst.x) - pObj->ptAt.x; |
| 6915 | dy = (pObj->with.y - ofst.y) - pObj->ptAt.y; |
| @@ -7235,11 +7302,12 @@ | |
| 7235 | pik_append_num(p, " width=\"", p->wSVG); |
| 7236 | pik_append_num(p, "\" height=\"", p->hSVG); |
| 7237 | pik_append(p, "\"", 1); |
| 7238 | } |
| 7239 | pik_append_dis(p, " viewBox=\"0 0 ",w,""); |
| 7240 | pik_append_dis(p, " ",h,"\">\n"); |
| 7241 | pik_elist_render(p, pList); |
| 7242 | pik_append(p,"</svg>\n", -1); |
| 7243 | }else{ |
| 7244 | p->wSVG = -1; |
| 7245 | p->hSVG = -1; |
| @@ -7319,10 +7387,11 @@ | |
| 7319 | { "n", 1, T_EDGEPT, 0, CP_N }, |
| 7320 | { "ne", 2, T_EDGEPT, 0, CP_NE }, |
| 7321 | { "north", 5, T_EDGEPT, 0, CP_N }, |
| 7322 | { "nw", 2, T_EDGEPT, 0, CP_NW }, |
| 7323 | { "of", 2, T_OF, 0, 0 }, |
| 7324 | { "previous", 8, T_LAST, 0, 0, }, |
| 7325 | { "print", 5, T_PRINT, 0, 0 }, |
| 7326 | { "rad", 3, T_RADIUS, 0, 0 }, |
| 7327 | { "radius", 6, T_RADIUS, 0, 0 }, |
| 7328 | { "right", 5, T_RIGHT, DIR_RIGHT, CP_E }, |
| @@ -7605,11 +7674,11 @@ | |
| 7605 | } |
| 7606 | default: { |
| 7607 | c = z[0]; |
| 7608 | if( c=='.' ){ |
| 7609 | unsigned char c1 = z[1]; |
| 7610 | if( islower(c1) ){ |
| 7611 | const PikWord *pFound; |
| 7612 | for(i=2; (c = z[i])>='a' && c<='z'; i++){} |
| 7613 | pFound = pik_find_word((const char*)z+1, i-1, |
| 7614 | pik_keywords, count(pik_keywords)); |
| 7615 | if( pFound && (pFound->eEdge>0 || |
| @@ -7625,15 +7694,15 @@ | |
| 7625 | }else{ |
| 7626 | /* Any other "dot" */ |
| 7627 | pToken->eType = T_DOT_L; |
| 7628 | } |
| 7629 | return 1; |
| 7630 | }else if( isdigit(c1) ){ |
| 7631 | i = 0; |
| 7632 | /* no-op. Fall through to number handling */ |
| 7633 | }else if( isupper(c1) ){ |
| 7634 | for(i=2; (c = z[i])!=0 && (isalnum(c) || c=='_'); i++){} |
| 7635 | pToken->eType = T_DOT_U; |
| 7636 | return 1; |
| 7637 | }else{ |
| 7638 | pToken->eType = T_ERROR; |
| 7639 | return 1; |
| @@ -7644,11 +7713,11 @@ | |
| 7644 | int isInt = 1; |
| 7645 | if( c!='.' ){ |
| 7646 | nDigit = 1; |
| 7647 | for(i=1; (c = z[i])>='0' && c<='9'; i++){ nDigit++; } |
| 7648 | if( i==1 && (c=='x' || c=='X') ){ |
| 7649 | for(i=2; (c = z[i])!=0 && isxdigit(c); i++){} |
| 7650 | pToken->eType = T_NUMBER; |
| 7651 | return i; |
| 7652 | } |
| 7653 | }else{ |
| 7654 | isInt = 0; |
| @@ -7700,13 +7769,13 @@ | |
| 7700 | ){ |
| 7701 | i += 2; |
| 7702 | } |
| 7703 | pToken->eType = T_NUMBER; |
| 7704 | return i; |
| 7705 | }else if( islower(c) ){ |
| 7706 | const PikWord *pFound; |
| 7707 | for(i=1; (c = z[i])!=0 && (isalnum(c) || c=='_'); i++){} |
| 7708 | pFound = pik_find_word((const char*)z, i, |
| 7709 | pik_keywords, count(pik_keywords)); |
| 7710 | if( pFound ){ |
| 7711 | pToken->eType = pFound->eType; |
| 7712 | pToken->eCode = pFound->eCode; |
| @@ -7719,19 +7788,19 @@ | |
| 7719 | }else{ |
| 7720 | pToken->eType = T_ID; |
| 7721 | } |
| 7722 | return i; |
| 7723 | }else if( c>='A' && c<='Z' ){ |
| 7724 | for(i=1; (c = z[i])!=0 && (isalnum(c) || c=='_'); i++){} |
| 7725 | pToken->eType = T_PLACENAME; |
| 7726 | return i; |
| 7727 | }else if( c=='$' && z[1]>='1' && z[1]<='9' && !isdigit(z[2]) ){ |
| 7728 | pToken->eType = T_PARAMETER; |
| 7729 | pToken->eCode = z[1] - '1'; |
| 7730 | return 2; |
| 7731 | }else if( c=='_' || c=='$' || c=='@' ){ |
| 7732 | for(i=1; (c = z[i])!=0 && (isalnum(c) || c=='_'); i++){} |
| 7733 | pToken->eType = T_ID; |
| 7734 | return i; |
| 7735 | }else{ |
| 7736 | pToken->eType = T_ERROR; |
| 7737 | return 1; |
| @@ -7814,12 +7883,12 @@ | |
| 7814 | /* Remove leading and trailing whitespace from each argument. |
| 7815 | ** If what remains is one of $1, $2, ... $9 then transfer the |
| 7816 | ** corresponding argument from the outer context */ |
| 7817 | for(j=0; j<=nArg; j++){ |
| 7818 | PToken *t = &args[j]; |
| 7819 | while( t->n>0 && isspace(t->z[0]) ){ t->n--; t->z++; } |
| 7820 | while( t->n>0 && isspace(t->z[t->n-1]) ){ t->n--; } |
| 7821 | if( t->n==2 && t->z[0]=='$' && t->z[1]>='1' && t->z[1]<='9' ){ |
| 7822 | if( pOuter ) *t = pOuter[t->z[1]-'1']; |
| 7823 | else t->n = 0; |
| 7824 | } |
| 7825 | } |
| @@ -7896,21 +7965,36 @@ | |
| 7896 | pMac->inUse = 0; |
| 7897 | }else{ |
| 7898 | #if 0 |
| 7899 | printf("******** Token %s (%d): \"%.*s\" **************\n", |
| 7900 | yyTokenName[token.eType], token.eType, |
| 7901 | (int)(isspace(token.z[0]) ? 0 : sz), token.z); |
| 7902 | #endif |
| 7903 | token.n = (unsigned short)(sz & 0xffff); |
| 7904 | if( p->nToken++ > PIKCHR_TOKEN_LIMIT ){ |
| 7905 | pik_error(p, &token, "script is too complex"); |
| 7906 | break; |
| 7907 | } |
| 7908 | pik_parser(pParser, token.eType, token); |
| 7909 | } |
| 7910 | } |
| 7911 | } |
| 7912 | |
| 7913 | /* |
| 7914 | ** Parse the PIKCHR script contained in zText[]. Return a rendering. Or |
| 7915 | ** if an error is encountered, return the error text. The error message |
| 7916 | ** is HTML formatted. So regardless of what happens, the return text |
| @@ -8130,10 +8214,14 @@ | |
| 8130 | exit(1); |
| 8131 | } |
| 8132 | bSvgOnly = 1; |
| 8133 | mFlags |= PIKCHR_PLAINTEXT_ERRORS; |
| 8134 | }else |
| 8135 | { |
| 8136 | fprintf(stderr,"unknown option: \"%s\"\n", argv[i]); |
| 8137 | usage(argv[0]); |
| 8138 | } |
| 8139 | continue; |
| @@ -8242,6 +8330,6 @@ | |
| 8242 | |
| 8243 | |
| 8244 | #endif /* PIKCHR_TCL */ |
| 8245 | |
| 8246 | |
| 8247 | #line 8272 "pikchr.c" |
| 8248 |
| --- extsrc/pikchr.c | |
| +++ extsrc/pikchr.c | |
| @@ -1,8 +1,47 @@ | |
| 1 | /* This file is automatically generated by Lemon from input grammar |
| 2 | ** source file "pikchr.y". |
| 3 | */ |
| 4 | /* |
| 5 | ** 2000-05-29 |
| 6 | ** |
| 7 | ** The author disclaims copyright to this source code. In place of |
| 8 | ** a legal notice, here is a blessing: |
| 9 | ** |
| 10 | ** May you do good and not evil. |
| 11 | ** May you find forgiveness for yourself and forgive others. |
| 12 | ** May you share freely, never taking more than you give. |
| 13 | ** |
| 14 | ************************************************************************* |
| 15 | ** Driver template for the LEMON parser generator. |
| 16 | ** |
| 17 | ** The "lemon" program processes an LALR(1) input grammar file, then uses |
| 18 | ** this template to construct a parser. The "lemon" program inserts text |
| 19 | ** at each "%%" line. Also, any "P-a-r-s-e" identifier prefix (without the |
| 20 | ** interstitial "-" characters) contained in this template is changed into |
| 21 | ** the value of the %name directive from the grammar. Otherwise, the content |
| 22 | ** of this template is copied straight through into the generate parser |
| 23 | ** source file. |
| 24 | ** |
| 25 | ** The following is the concatenation of all %include directives from the |
| 26 | ** input grammar file: |
| 27 | */ |
| 28 | /************ Begin %include sections from the grammar ************************/ |
| 29 | #line 1 "VERSION.h" |
| 30 | #define MANIFEST_UUID "8a43b020141f772a0ac45291a7fd73041d2efba5e3665c6bd2f334ad9b2e9845" |
| 31 | #define MANIFEST_VERSION "[8a43b02014]" |
| 32 | #define MANIFEST_DATE "2025-03-19 16:19:43" |
| 33 | #define MANIFEST_YEAR "2025" |
| 34 | #define MANIFEST_ISODATE "20250319161943" |
| 35 | #define MANIFEST_NUMERIC_DATE 20250319 |
| 36 | #define MANIFEST_NUMERIC_TIME 161943 |
| 37 | #define RELEASE_VERSION "1.0" |
| 38 | #define RELEASE_VERSION_NUMBER 10000 |
| 39 | #define RELEASE_RESOURCE_VERSION 1,0,0,0 |
| 40 | #define COMPILER "gcc-13.3.0" |
| 41 | #line 2 "pikchr.y" |
| 42 | |
| 43 | /* |
| 44 | ** Zero-Clause BSD license: |
| 45 | ** |
| 46 | ** Copyright (C) 2020-09-01 by D. Richard Hipp <[email protected]> |
| 47 | ** |
| @@ -125,10 +164,22 @@ | |
| 164 | #include <assert.h> |
| 165 | #define count(X) (sizeof(X)/sizeof(X[0])) |
| 166 | #ifndef M_PI |
| 167 | # define M_PI 3.1415926535897932385 |
| 168 | #endif |
| 169 | |
| 170 | /* |
| 171 | ** Typesafe version of ctype.h macros. Cygwin requires this, I'm told. |
| 172 | */ |
| 173 | #define IsUpper(X) isupper((unsigned char)(X)) |
| 174 | #define IsLower(X) islower((unsigned char)(X)) |
| 175 | #define ToLower(X) tolower((unsigned char)(X)) |
| 176 | #define IsDigit(X) isdigit((unsigned char)(X)) |
| 177 | #define IsXDigit(X) isxdigit((unsigned char)(X)) |
| 178 | #define IsSpace(X) isspace((unsigned char)(X)) |
| 179 | #define IsAlnum(X) isalnum((unsigned char)(X)) |
| 180 | |
| 181 | |
| 182 | /* Limit the number of tokens in a single script to avoid run-away |
| 183 | ** macro expansion attacks. See forum post |
| 184 | ** https://pikchr.org/home/forumpost/ef8684c6955a411a |
| 185 | */ |
| @@ -474,11 +525,11 @@ | |
| 525 | static void pik_bbox_addbox(PBox*,PBox*); |
| 526 | static void pik_bbox_add_xy(PBox*,PNum,PNum); |
| 527 | static void pik_bbox_addellipse(PBox*,PNum x,PNum y,PNum rx,PNum ry); |
| 528 | static void pik_add_txt(Pik*,PToken*,int); |
| 529 | static int pik_text_length(const PToken *pToken, const int isMonospace); |
| 530 | static void pik_size_to_fit(Pik*,PObj*,PToken*,int); |
| 531 | static int pik_text_position(int,PToken*); |
| 532 | static PNum pik_property_of(PObj*,PToken*); |
| 533 | static PNum pik_func(Pik*,PToken*,PNum,PNum); |
| 534 | static PPoint pik_position_between(PNum x, PPoint p1, PPoint p2); |
| 535 | static PPoint pik_position_at_angle(PNum dist, PNum r, PPoint pt); |
| @@ -492,11 +543,11 @@ | |
| 543 | static PObj *pik_position_assert(Pik*,PPoint*,PToken*,PPoint*); |
| 544 | static PNum pik_dist(PPoint*,PPoint*); |
| 545 | static void pik_add_macro(Pik*,PToken *pId,PToken *pCode); |
| 546 | |
| 547 | |
| 548 | #line 549 "pikchr.c" |
| 549 | /**************** End of %include directives **********************************/ |
| 550 | /* These constants specify the various numeric values for terminal symbols. |
| 551 | ***************** Begin token definitions *************************************/ |
| 552 | #ifndef T_ID |
| 553 | #define T_ID 1 |
| @@ -522,84 +573,85 @@ | |
| 573 | #define T_COLOR 21 |
| 574 | #define T_THICKNESS 22 |
| 575 | #define T_PRINT 23 |
| 576 | #define T_STRING 24 |
| 577 | #define T_COMMA 25 |
| 578 | #define T_ISODATE 26 |
| 579 | #define T_CLASSNAME 27 |
| 580 | #define T_LB 28 |
| 581 | #define T_RB 29 |
| 582 | #define T_UP 30 |
| 583 | #define T_DOWN 31 |
| 584 | #define T_LEFT 32 |
| 585 | #define T_RIGHT 33 |
| 586 | #define T_CLOSE 34 |
| 587 | #define T_CHOP 35 |
| 588 | #define T_FROM 36 |
| 589 | #define T_TO 37 |
| 590 | #define T_THEN 38 |
| 591 | #define T_HEADING 39 |
| 592 | #define T_GO 40 |
| 593 | #define T_AT 41 |
| 594 | #define T_WITH 42 |
| 595 | #define T_SAME 43 |
| 596 | #define T_AS 44 |
| 597 | #define T_FIT 45 |
| 598 | #define T_BEHIND 46 |
| 599 | #define T_UNTIL 47 |
| 600 | #define T_EVEN 48 |
| 601 | #define T_DOT_E 49 |
| 602 | #define T_HEIGHT 50 |
| 603 | #define T_WIDTH 51 |
| 604 | #define T_RADIUS 52 |
| 605 | #define T_DIAMETER 53 |
| 606 | #define T_DOTTED 54 |
| 607 | #define T_DASHED 55 |
| 608 | #define T_CW 56 |
| 609 | #define T_CCW 57 |
| 610 | #define T_LARROW 58 |
| 611 | #define T_RARROW 59 |
| 612 | #define T_LRARROW 60 |
| 613 | #define T_INVIS 61 |
| 614 | #define T_THICK 62 |
| 615 | #define T_THIN 63 |
| 616 | #define T_SOLID 64 |
| 617 | #define T_CENTER 65 |
| 618 | #define T_LJUST 66 |
| 619 | #define T_RJUST 67 |
| 620 | #define T_ABOVE 68 |
| 621 | #define T_BELOW 69 |
| 622 | #define T_ITALIC 70 |
| 623 | #define T_BOLD 71 |
| 624 | #define T_MONO 72 |
| 625 | #define T_ALIGNED 73 |
| 626 | #define T_BIG 74 |
| 627 | #define T_SMALL 75 |
| 628 | #define T_AND 76 |
| 629 | #define T_LT 77 |
| 630 | #define T_GT 78 |
| 631 | #define T_ON 79 |
| 632 | #define T_WAY 80 |
| 633 | #define T_BETWEEN 81 |
| 634 | #define T_THE 82 |
| 635 | #define T_NTH 83 |
| 636 | #define T_VERTEX 84 |
| 637 | #define T_TOP 85 |
| 638 | #define T_BOTTOM 86 |
| 639 | #define T_START 87 |
| 640 | #define T_END 88 |
| 641 | #define T_IN 89 |
| 642 | #define T_THIS 90 |
| 643 | #define T_DOT_U 91 |
| 644 | #define T_LAST 92 |
| 645 | #define T_NUMBER 93 |
| 646 | #define T_FUNC1 94 |
| 647 | #define T_FUNC2 95 |
| 648 | #define T_DIST 96 |
| 649 | #define T_DOT_XY 97 |
| 650 | #define T_X 98 |
| 651 | #define T_Y 99 |
| 652 | #define T_DOT_L 100 |
| 653 | #endif |
| 654 | /**************** End token definitions ***************************************/ |
| 655 | |
| 656 | /* The next sections is a series of control #defines. |
| 657 | ** various aspects of the generated parser. |
| @@ -660,22 +712,22 @@ | |
| 712 | #ifndef INTERFACE |
| 713 | # define INTERFACE 1 |
| 714 | #endif |
| 715 | /************* Begin control #defines *****************************************/ |
| 716 | #define YYCODETYPE unsigned char |
| 717 | #define YYNOCODE 138 |
| 718 | #define YYACTIONTYPE unsigned short int |
| 719 | #define pik_parserTOKENTYPE PToken |
| 720 | typedef union { |
| 721 | int yyinit; |
| 722 | pik_parserTOKENTYPE yy0; |
| 723 | PList* yy23; |
| 724 | PRel yy28; |
| 725 | PObj* yy54; |
| 726 | PNum yy129; |
| 727 | PPoint yy187; |
| 728 | short int yy272; |
| 729 | } YYMINORTYPE; |
| 730 | #ifndef YYSTACKDEPTH |
| 731 | #define YYSTACKDEPTH 100 |
| 732 | #endif |
| 733 | #define pik_parserARG_SDECL |
| @@ -693,21 +745,21 @@ | |
| 745 | #define pik_parserCTX_STORE yypParser->p=p; |
| 746 | #define YYFALLBACK 1 |
| 747 | #define YYNSTATE 164 |
| 748 | #define YYNRULE 156 |
| 749 | #define YYNRULE_WITH_ACTION 116 |
| 750 | #define YYNTOKEN 101 |
| 751 | #define YY_MAX_SHIFT 163 |
| 752 | #define YY_MIN_SHIFTREDUCE 287 |
| 753 | #define YY_MAX_SHIFTREDUCE 442 |
| 754 | #define YY_ERROR_ACTION 443 |
| 755 | #define YY_ACCEPT_ACTION 444 |
| 756 | #define YY_NO_ACTION 445 |
| 757 | #define YY_MIN_REDUCE 446 |
| 758 | #define YY_MAX_REDUCE 601 |
| 759 | #define YY_MIN_DSTRCTR 101 |
| 760 | #define YY_MAX_DSTRCTR 104 |
| 761 | /************* End control #defines *******************************************/ |
| 762 | #define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0]))) |
| 763 | |
| 764 | /* Define the yytestcase() macro to be a no-op if is not already defined |
| 765 | ** otherwise. |
| @@ -786,324 +838,322 @@ | |
| 838 | ** yy_reduce_ofst[] For each state, the offset into yy_action for |
| 839 | ** shifting non-terminals after a reduce. |
| 840 | ** yy_default[] Default action for each state. |
| 841 | ** |
| 842 | *********** Begin parsing tables **********************************************/ |
| 843 | #define YY_ACTTAB_COUNT (1305) |
| 844 | static const YYACTIONTYPE yy_action[] = { |
| 845 | /* 0 */ 575, 495, 161, 119, 25, 452, 29, 74, 129, 148, |
| 846 | /* 10 */ 575, 64, 63, 62, 61, 453, 113, 120, 161, 119, |
| 847 | /* 20 */ 427, 428, 339, 357, 81, 121, 447, 454, 29, 575, |
| 848 | /* 30 */ 530, 13, 50, 450, 322, 323, 9, 8, 33, 149, |
| 849 | /* 40 */ 32, 7, 71, 127, 163, 335, 66, 28, 444, 27, |
| 850 | /* 50 */ 339, 339, 339, 339, 425, 426, 340, 341, 342, 343, |
| 851 | /* 60 */ 344, 345, 346, 347, 348, 474, 64, 63, 62, 61, |
| 852 | /* 70 */ 54, 51, 73, 306, 148, 474, 492, 161, 119, 297, |
| 853 | /* 80 */ 112, 113, 120, 161, 119, 427, 428, 339, 30, 81, |
| 854 | /* 90 */ 109, 447, 454, 29, 474, 528, 161, 119, 450, 322, |
| 855 | /* 100 */ 323, 9, 8, 33, 149, 32, 7, 71, 127, 163, |
| 856 | /* 110 */ 335, 66, 535, 36, 27, 339, 339, 339, 339, 425, |
| 857 | /* 120 */ 426, 340, 341, 342, 343, 344, 345, 346, 347, 348, |
| 858 | /* 130 */ 394, 435, 310, 59, 60, 64, 63, 62, 61, 313, |
| 859 | /* 140 */ 74, 376, 148, 69, 2, 533, 161, 119, 124, 113, |
| 860 | /* 150 */ 120, 161, 119, 80, 535, 31, 308, 79, 83, 107, |
| 861 | /* 160 */ 535, 441, 440, 535, 394, 435, 299, 59, 60, 120, |
| 862 | /* 170 */ 161, 119, 149, 463, 376, 376, 330, 84, 2, 122, |
| 863 | /* 180 */ 78, 78, 38, 156, 156, 156, 48, 37, 559, 328, |
| 864 | /* 190 */ 128, 152, 560, 561, 434, 441, 440, 350, 350, 350, |
| 865 | /* 200 */ 350, 350, 350, 350, 350, 350, 350, 350, 577, 77, |
| 866 | /* 210 */ 577, 35, 106, 46, 436, 437, 438, 439, 579, 375, |
| 867 | /* 220 */ 298, 117, 393, 155, 154, 153, 47, 4, 434, 69, |
| 868 | /* 230 */ 394, 435, 3, 59, 60, 411, 412, 413, 414, 398, |
| 869 | /* 240 */ 399, 376, 62, 61, 2, 108, 106, 5, 436, 437, |
| 870 | /* 250 */ 438, 439, 375, 375, 117, 117, 393, 155, 154, 153, |
| 871 | /* 260 */ 76, 441, 440, 67, 6, 142, 140, 64, 63, 62, |
| 872 | /* 270 */ 61, 380, 157, 424, 427, 428, 339, 379, 159, 45, |
| 873 | /* 280 */ 423, 72, 131, 148, 531, 161, 119, 1, 55, 125, |
| 874 | /* 290 */ 113, 120, 161, 119, 434, 147, 146, 64, 63, 62, |
| 875 | /* 300 */ 61, 397, 43, 11, 339, 339, 339, 339, 425, 426, |
| 876 | /* 310 */ 355, 65, 106, 149, 436, 437, 438, 439, 74, 375, |
| 877 | /* 320 */ 148, 117, 393, 155, 154, 153, 497, 113, 120, 161, |
| 878 | /* 330 */ 119, 22, 21, 12, 142, 140, 64, 63, 62, 61, |
| 879 | /* 340 */ 24, 356, 145, 141, 431, 64, 63, 62, 61, 391, |
| 880 | /* 350 */ 149, 448, 454, 29, 378, 158, 85, 55, 450, 394, |
| 881 | /* 360 */ 432, 138, 59, 60, 147, 146, 120, 161, 119, 163, |
| 882 | /* 370 */ 102, 43, 139, 42, 27, 430, 14, 15, 301, 302, |
| 883 | /* 380 */ 303, 446, 305, 16, 44, 74, 18, 148, 152, 19, |
| 884 | /* 390 */ 20, 36, 68, 496, 113, 120, 161, 119, 114, 359, |
| 885 | /* 400 */ 22, 21, 23, 142, 140, 64, 63, 62, 61, 24, |
| 886 | /* 410 */ 107, 145, 141, 431, 26, 57, 377, 149, 58, 118, |
| 887 | /* 420 */ 120, 161, 119, 392, 463, 384, 55, 64, 63, 62, |
| 888 | /* 430 */ 61, 382, 569, 147, 146, 160, 383, 435, 39, 70, |
| 889 | /* 440 */ 43, 106, 152, 445, 445, 88, 445, 445, 375, 445, |
| 890 | /* 450 */ 117, 393, 155, 154, 153, 120, 161, 119, 445, 17, |
| 891 | /* 460 */ 445, 10, 479, 479, 445, 445, 435, 441, 440, 22, |
| 892 | /* 470 */ 21, 445, 403, 64, 63, 62, 61, 152, 24, 445, |
| 893 | /* 480 */ 145, 141, 431, 133, 75, 126, 354, 445, 445, 123, |
| 894 | /* 490 */ 445, 404, 405, 406, 408, 80, 441, 440, 308, 79, |
| 895 | /* 500 */ 434, 411, 412, 413, 414, 394, 445, 445, 59, 60, |
| 896 | /* 510 */ 64, 63, 62, 61, 445, 445, 376, 445, 445, 42, |
| 897 | /* 520 */ 436, 437, 438, 439, 156, 156, 156, 394, 445, 434, |
| 898 | /* 530 */ 59, 60, 64, 63, 62, 61, 445, 445, 376, 445, |
| 899 | /* 540 */ 445, 42, 445, 394, 473, 391, 59, 60, 445, 436, |
| 900 | /* 550 */ 437, 438, 439, 49, 376, 445, 74, 42, 148, 445, |
| 901 | /* 560 */ 88, 445, 445, 445, 490, 113, 120, 161, 119, 445, |
| 902 | /* 570 */ 120, 161, 119, 132, 130, 394, 143, 475, 59, 60, |
| 903 | /* 580 */ 445, 473, 64, 63, 62, 61, 376, 106, 149, 42, |
| 904 | /* 590 */ 445, 445, 152, 445, 375, 391, 117, 393, 155, 154, |
| 905 | /* 600 */ 153, 394, 144, 52, 59, 60, 445, 445, 445, 106, |
| 906 | /* 610 */ 445, 445, 376, 445, 445, 42, 375, 445, 117, 393, |
| 907 | /* 620 */ 155, 154, 153, 445, 445, 106, 64, 63, 62, 61, |
| 908 | /* 630 */ 445, 445, 375, 445, 117, 393, 155, 154, 153, 394, |
| 909 | /* 640 */ 445, 445, 59, 60, 88, 445, 445, 53, 445, 445, |
| 910 | /* 650 */ 376, 445, 445, 42, 120, 161, 119, 106, 445, 445, |
| 911 | /* 660 */ 445, 110, 110, 445, 375, 445, 117, 393, 155, 154, |
| 912 | /* 670 */ 153, 394, 445, 445, 59, 60, 152, 107, 445, 445, |
| 913 | /* 680 */ 445, 445, 102, 106, 445, 42, 445, 120, 161, 119, |
| 914 | /* 690 */ 375, 451, 117, 393, 155, 154, 153, 394, 445, 445, |
| 915 | /* 700 */ 59, 60, 64, 63, 62, 61, 445, 445, 376, 152, |
| 916 | /* 710 */ 445, 40, 445, 394, 445, 396, 59, 60, 445, 445, |
| 917 | /* 720 */ 445, 106, 445, 445, 376, 88, 445, 41, 375, 445, |
| 918 | /* 730 */ 117, 393, 155, 154, 153, 120, 161, 119, 74, 445, |
| 919 | /* 740 */ 148, 445, 111, 111, 107, 445, 484, 113, 120, 161, |
| 920 | /* 750 */ 119, 445, 445, 106, 120, 161, 119, 152, 478, 445, |
| 921 | /* 760 */ 375, 86, 117, 393, 155, 154, 153, 445, 445, 445, |
| 922 | /* 770 */ 149, 120, 161, 119, 445, 445, 152, 445, 445, 106, |
| 923 | /* 780 */ 445, 64, 63, 62, 61, 445, 375, 445, 117, 393, |
| 924 | /* 790 */ 155, 154, 153, 152, 395, 106, 64, 63, 62, 61, |
| 925 | /* 800 */ 98, 445, 375, 445, 117, 393, 155, 154, 153, 445, |
| 926 | /* 810 */ 120, 161, 119, 445, 74, 445, 148, 56, 445, 74, |
| 927 | /* 820 */ 445, 148, 483, 113, 120, 161, 119, 480, 113, 120, |
| 928 | /* 830 */ 161, 119, 152, 74, 445, 148, 445, 89, 445, 445, |
| 929 | /* 840 */ 445, 134, 113, 120, 161, 119, 149, 120, 161, 119, |
| 930 | /* 850 */ 445, 149, 74, 445, 148, 445, 445, 445, 378, 158, |
| 931 | /* 860 */ 517, 113, 120, 161, 119, 149, 74, 445, 148, 152, |
| 932 | /* 870 */ 445, 74, 445, 148, 137, 113, 120, 161, 119, 525, |
| 933 | /* 880 */ 113, 120, 161, 119, 149, 74, 445, 148, 64, 63, |
| 934 | /* 890 */ 62, 61, 445, 527, 113, 120, 161, 119, 149, 445, |
| 935 | /* 900 */ 445, 391, 445, 149, 445, 445, 445, 445, 445, 445, |
| 936 | /* 910 */ 74, 445, 148, 445, 445, 162, 445, 149, 524, 113, |
| 937 | /* 920 */ 120, 161, 119, 118, 445, 74, 445, 148, 445, 445, |
| 938 | /* 930 */ 445, 445, 445, 526, 113, 120, 161, 119, 445, 74, |
| 939 | /* 940 */ 445, 148, 149, 445, 445, 445, 445, 523, 113, 120, |
| 940 | /* 950 */ 161, 119, 74, 445, 148, 445, 445, 149, 445, 445, |
| 941 | /* 960 */ 522, 113, 120, 161, 119, 445, 74, 445, 148, 445, |
| 942 | /* 970 */ 445, 149, 445, 445, 521, 113, 120, 161, 119, 74, |
| 943 | /* 980 */ 445, 148, 445, 445, 149, 445, 445, 520, 113, 120, |
| 944 | /* 990 */ 161, 119, 445, 74, 445, 148, 445, 445, 149, 445, |
| 945 | /* 1000 */ 445, 519, 113, 120, 161, 119, 445, 445, 445, 445, |
| 946 | /* 1010 */ 445, 149, 445, 445, 445, 445, 445, 445, 74, 445, |
| 947 | /* 1020 */ 148, 445, 445, 445, 445, 149, 150, 113, 120, 161, |
| 948 | /* 1030 */ 119, 74, 445, 148, 445, 445, 445, 445, 445, 151, |
| 949 | /* 1040 */ 113, 120, 161, 119, 445, 74, 445, 148, 445, 445, |
| 950 | /* 1050 */ 149, 445, 445, 136, 113, 120, 161, 119, 74, 445, |
| 951 | /* 1060 */ 148, 445, 445, 149, 445, 445, 135, 113, 120, 161, |
| 952 | /* 1070 */ 119, 445, 88, 445, 445, 445, 445, 149, 445, 445, |
| 953 | /* 1080 */ 445, 90, 120, 161, 119, 445, 445, 445, 445, 82, |
| 954 | /* 1090 */ 149, 120, 161, 119, 445, 87, 466, 445, 34, 99, |
| 955 | /* 1100 */ 445, 445, 445, 445, 152, 120, 161, 119, 100, 120, |
| 956 | /* 1110 */ 161, 119, 445, 152, 445, 445, 445, 445, 120, 161, |
| 957 | /* 1120 */ 119, 445, 445, 445, 101, 445, 445, 152, 445, 445, |
| 958 | /* 1130 */ 445, 152, 91, 445, 120, 161, 119, 103, 445, 445, |
| 959 | /* 1140 */ 152, 445, 120, 161, 119, 445, 445, 120, 161, 119, |
| 960 | /* 1150 */ 445, 92, 445, 445, 445, 445, 152, 445, 445, 445, |
| 961 | /* 1160 */ 93, 120, 161, 119, 152, 445, 104, 445, 445, 152, |
| 962 | /* 1170 */ 120, 161, 119, 445, 94, 445, 120, 161, 119, 445, |
| 963 | /* 1180 */ 445, 445, 445, 152, 120, 161, 119, 445, 445, 105, |
| 964 | /* 1190 */ 445, 445, 152, 445, 445, 445, 445, 95, 152, 120, |
| 965 | /* 1200 */ 161, 119, 96, 445, 445, 97, 152, 120, 161, 119, |
| 966 | /* 1210 */ 445, 445, 120, 161, 119, 120, 161, 119, 445, 445, |
| 967 | /* 1220 */ 445, 152, 445, 445, 445, 445, 445, 445, 445, 152, |
| 968 | /* 1230 */ 549, 445, 445, 548, 152, 445, 445, 152, 547, 445, |
| 969 | /* 1240 */ 120, 161, 119, 120, 161, 119, 546, 445, 120, 161, |
| 970 | /* 1250 */ 119, 445, 445, 445, 445, 445, 120, 161, 119, 445, |
| 971 | /* 1260 */ 445, 445, 152, 445, 445, 152, 445, 445, 445, 115, |
| 972 | /* 1270 */ 152, 445, 116, 445, 445, 445, 445, 445, 152, 120, |
| 973 | /* 1280 */ 161, 119, 120, 161, 119, 445, 445, 445, 445, 445, |
| 974 | /* 1290 */ 445, 445, 445, 445, 445, 445, 445, 445, 445, 445, |
| 975 | /* 1300 */ 445, 152, 445, 445, 152, |
| 976 | }; |
| 977 | static const YYCODETYPE yy_lookahead[] = { |
| 978 | /* 0 */ 0, 115, 116, 117, 136, 103, 104, 105, 107, 107, |
| 979 | /* 10 */ 10, 4, 5, 6, 7, 113, 114, 115, 116, 117, |
| 980 | /* 20 */ 20, 21, 22, 17, 24, 101, 102, 103, 104, 29, |
| 981 | /* 30 */ 107, 25, 25, 109, 34, 35, 36, 37, 38, 137, |
| 982 | /* 40 */ 40, 41, 42, 43, 120, 45, 46, 109, 124, 125, |
| 983 | /* 50 */ 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, |
| 984 | /* 60 */ 60, 61, 62, 63, 64, 0, 4, 5, 6, 7, |
| 985 | /* 70 */ 4, 5, 105, 25, 107, 10, 115, 116, 117, 17, |
| 986 | /* 80 */ 113, 114, 115, 116, 117, 20, 21, 22, 128, 24, |
| 987 | /* 90 */ 101, 102, 103, 104, 29, 115, 116, 117, 109, 34, |
| 988 | /* 100 */ 35, 36, 37, 38, 137, 40, 41, 42, 43, 120, |
| 989 | /* 110 */ 45, 46, 49, 10, 125, 50, 51, 52, 53, 54, |
| 990 | /* 120 */ 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, |
| 991 | /* 130 */ 1, 2, 29, 4, 5, 4, 5, 6, 7, 8, |
| 992 | /* 140 */ 105, 12, 107, 3, 15, 115, 116, 117, 113, 114, |
| 993 | /* 150 */ 115, 116, 117, 24, 91, 130, 27, 28, 118, 105, |
| 994 | /* 160 */ 97, 32, 33, 100, 1, 2, 19, 4, 5, 115, |
| 995 | /* 170 */ 116, 117, 137, 119, 12, 12, 2, 118, 15, 1, |
| 996 | /* 180 */ 126, 127, 106, 20, 21, 22, 110, 111, 106, 2, |
| 997 | /* 190 */ 107, 137, 110, 111, 65, 32, 33, 65, 66, 67, |
| 998 | /* 200 */ 68, 69, 70, 71, 72, 73, 74, 75, 132, 133, |
| 999 | /* 210 */ 134, 131, 83, 39, 85, 86, 87, 88, 135, 90, |
| 1000 | /* 220 */ 17, 92, 93, 94, 95, 96, 39, 15, 65, 89, |
| 1001 | /* 230 */ 1, 2, 16, 4, 5, 30, 31, 32, 33, 98, |
| 1002 | /* 240 */ 99, 12, 6, 7, 15, 83, 83, 41, 85, 86, |
| 1003 | /* 250 */ 87, 88, 90, 90, 92, 92, 93, 94, 95, 96, |
| 1004 | /* 260 */ 49, 32, 33, 44, 41, 2, 3, 4, 5, 6, |
| 1005 | /* 270 */ 7, 27, 28, 42, 20, 21, 22, 27, 28, 16, |
| 1006 | /* 280 */ 42, 105, 48, 107, 115, 116, 117, 13, 25, 113, |
| 1007 | /* 290 */ 114, 115, 116, 117, 65, 32, 33, 4, 5, 6, |
| 1008 | /* 300 */ 7, 17, 39, 25, 50, 51, 52, 53, 54, 55, |
| 1009 | /* 310 */ 17, 100, 83, 137, 85, 86, 87, 88, 105, 90, |
| 1010 | /* 320 */ 107, 92, 93, 94, 95, 96, 113, 114, 115, 116, |
| 1011 | /* 330 */ 117, 68, 69, 76, 2, 3, 4, 5, 6, 7, |
| 1012 | /* 340 */ 77, 17, 79, 80, 81, 4, 5, 6, 7, 17, |
| 1013 | /* 350 */ 137, 102, 103, 104, 27, 28, 105, 25, 109, 1, |
| 1014 | /* 360 */ 81, 80, 4, 5, 32, 33, 115, 116, 117, 120, |
| 1015 | /* 370 */ 12, 39, 82, 15, 125, 81, 3, 36, 20, 21, |
| 1016 | /* 380 */ 22, 0, 24, 3, 39, 105, 3, 107, 137, 3, |
| 1017 | /* 390 */ 3, 10, 3, 113, 114, 115, 116, 117, 97, 78, |
| 1018 | /* 400 */ 68, 69, 25, 2, 3, 4, 5, 6, 7, 77, |
| 1019 | /* 410 */ 105, 79, 80, 81, 15, 15, 12, 137, 15, 92, |
| 1020 | /* 420 */ 115, 116, 117, 17, 119, 29, 25, 4, 5, 6, |
| 1021 | /* 430 */ 7, 29, 127, 32, 33, 91, 29, 2, 11, 3, |
| 1022 | /* 440 */ 39, 83, 137, 138, 138, 105, 138, 138, 90, 138, |
| 1023 | /* 450 */ 92, 93, 94, 95, 96, 115, 116, 117, 138, 36, |
| 1024 | /* 460 */ 138, 121, 122, 123, 138, 138, 2, 32, 33, 68, |
| 1025 | /* 470 */ 69, 138, 1, 4, 5, 6, 7, 137, 77, 138, |
| 1026 | /* 480 */ 79, 80, 81, 12, 49, 14, 17, 138, 138, 18, |
| 1027 | /* 490 */ 138, 20, 21, 22, 23, 24, 32, 33, 27, 28, |
| 1028 | /* 500 */ 65, 30, 31, 32, 33, 1, 138, 138, 4, 5, |
| 1029 | /* 510 */ 4, 5, 6, 7, 138, 138, 12, 138, 138, 15, |
| 1030 | /* 520 */ 85, 86, 87, 88, 20, 21, 22, 1, 138, 65, |
| 1031 | /* 530 */ 4, 5, 4, 5, 6, 7, 138, 138, 12, 138, |
| 1032 | /* 540 */ 138, 15, 138, 1, 2, 17, 4, 5, 138, 85, |
| 1033 | /* 550 */ 86, 87, 88, 25, 12, 138, 105, 15, 107, 138, |
| 1034 | /* 560 */ 105, 138, 138, 138, 113, 114, 115, 116, 117, 138, |
| 1035 | /* 570 */ 115, 116, 117, 47, 48, 1, 2, 122, 4, 5, |
| 1036 | /* 580 */ 138, 39, 4, 5, 6, 7, 12, 83, 137, 15, |
| 1037 | /* 590 */ 138, 138, 137, 138, 90, 17, 92, 93, 94, 95, |
| 1038 | /* 600 */ 96, 1, 2, 25, 4, 5, 138, 138, 138, 83, |
| 1039 | /* 610 */ 138, 138, 12, 138, 138, 15, 90, 138, 92, 93, |
| 1040 | /* 620 */ 94, 95, 96, 138, 138, 83, 4, 5, 6, 7, |
| 1041 | /* 630 */ 138, 138, 90, 138, 92, 93, 94, 95, 96, 1, |
| 1042 | /* 640 */ 138, 138, 4, 5, 105, 138, 138, 25, 138, 138, |
| 1043 | /* 650 */ 12, 138, 138, 15, 115, 116, 117, 83, 138, 138, |
| 1044 | /* 660 */ 138, 122, 123, 138, 90, 138, 92, 93, 94, 95, |
| 1045 | /* 670 */ 96, 1, 138, 138, 4, 5, 137, 105, 138, 138, |
| 1046 | /* 680 */ 138, 138, 12, 83, 138, 15, 138, 115, 116, 117, |
| 1047 | /* 690 */ 90, 119, 92, 93, 94, 95, 96, 1, 138, 138, |
| 1048 | /* 700 */ 4, 5, 4, 5, 6, 7, 138, 138, 12, 137, |
| 1049 | /* 710 */ 138, 15, 138, 1, 138, 17, 4, 5, 138, 138, |
| 1050 | /* 720 */ 138, 83, 138, 138, 12, 105, 138, 15, 90, 138, |
| 1051 | /* 730 */ 92, 93, 94, 95, 96, 115, 116, 117, 105, 138, |
| 1052 | /* 740 */ 107, 138, 122, 123, 105, 138, 113, 114, 115, 116, |
| 1053 | /* 750 */ 117, 138, 138, 83, 115, 116, 117, 137, 119, 138, |
| 1054 | /* 760 */ 90, 105, 92, 93, 94, 95, 96, 138, 138, 138, |
| 1055 | /* 770 */ 137, 115, 116, 117, 138, 138, 137, 138, 138, 83, |
| 1056 | /* 780 */ 138, 4, 5, 6, 7, 138, 90, 138, 92, 93, |
| 1057 | /* 790 */ 94, 95, 96, 137, 17, 83, 4, 5, 6, 7, |
| 1058 | /* 800 */ 105, 138, 90, 138, 92, 93, 94, 95, 96, 138, |
| 1059 | /* 810 */ 115, 116, 117, 138, 105, 138, 107, 25, 138, 105, |
| 1060 | /* 820 */ 138, 107, 113, 114, 115, 116, 117, 113, 114, 115, |
| 1061 | /* 830 */ 116, 117, 137, 105, 138, 107, 138, 105, 138, 138, |
| 1062 | /* 840 */ 138, 113, 114, 115, 116, 117, 137, 115, 116, 117, |
| 1063 | /* 850 */ 138, 137, 105, 138, 107, 138, 138, 138, 27, 28, |
| 1064 | /* 860 */ 113, 114, 115, 116, 117, 137, 105, 138, 107, 137, |
| 1065 | /* 870 */ 138, 105, 138, 107, 113, 114, 115, 116, 117, 113, |
| 1066 | /* 880 */ 114, 115, 116, 117, 137, 105, 138, 107, 4, 5, |
| 1067 | /* 890 */ 6, 7, 138, 113, 114, 115, 116, 117, 137, 138, |
| 1068 | /* 900 */ 138, 17, 138, 137, 138, 138, 138, 138, 138, 138, |
| 1069 | /* 910 */ 105, 138, 107, 138, 138, 84, 138, 137, 113, 114, |
| 1070 | /* 920 */ 115, 116, 117, 92, 138, 105, 138, 107, 138, 138, |
| 1071 | /* 930 */ 138, 138, 138, 113, 114, 115, 116, 117, 138, 105, |
| 1072 | /* 940 */ 138, 107, 137, 138, 138, 138, 138, 113, 114, 115, |
| 1073 | /* 950 */ 116, 117, 105, 138, 107, 138, 138, 137, 138, 138, |
| 1074 | /* 960 */ 113, 114, 115, 116, 117, 138, 105, 138, 107, 138, |
| 1075 | /* 970 */ 138, 137, 138, 138, 113, 114, 115, 116, 117, 105, |
| 1076 | /* 980 */ 138, 107, 138, 138, 137, 138, 138, 113, 114, 115, |
| 1077 | /* 990 */ 116, 117, 138, 105, 138, 107, 138, 138, 137, 138, |
| 1078 | /* 1000 */ 138, 113, 114, 115, 116, 117, 138, 138, 138, 138, |
| 1079 | /* 1010 */ 138, 137, 138, 138, 138, 138, 138, 138, 105, 138, |
| 1080 | /* 1020 */ 107, 138, 138, 138, 138, 137, 113, 114, 115, 116, |
| 1081 | /* 1030 */ 117, 105, 138, 107, 138, 138, 138, 138, 138, 113, |
| 1082 | /* 1040 */ 114, 115, 116, 117, 138, 105, 138, 107, 138, 138, |
| 1083 | /* 1050 */ 137, 138, 138, 113, 114, 115, 116, 117, 105, 138, |
| 1084 | /* 1060 */ 107, 138, 138, 137, 138, 138, 113, 114, 115, 116, |
| 1085 | /* 1070 */ 117, 138, 105, 138, 138, 138, 138, 137, 138, 138, |
| 1086 | /* 1080 */ 138, 105, 115, 116, 117, 138, 138, 138, 138, 122, |
| 1087 | /* 1090 */ 137, 115, 116, 117, 138, 105, 129, 138, 131, 105, |
| 1088 | /* 1100 */ 138, 138, 138, 138, 137, 115, 116, 117, 105, 115, |
| 1089 | /* 1110 */ 116, 117, 138, 137, 138, 138, 138, 138, 115, 116, |
| 1090 | /* 1120 */ 117, 138, 138, 138, 105, 138, 138, 137, 138, 138, |
| 1091 | /* 1130 */ 138, 137, 105, 138, 115, 116, 117, 105, 138, 138, |
| 1092 | /* 1140 */ 137, 138, 115, 116, 117, 138, 138, 115, 116, 117, |
| 1093 | /* 1150 */ 138, 105, 138, 138, 138, 138, 137, 138, 138, 138, |
| 1094 | /* 1160 */ 105, 115, 116, 117, 137, 138, 105, 138, 138, 137, |
| 1095 | /* 1170 */ 115, 116, 117, 138, 105, 138, 115, 116, 117, 138, |
| 1096 | /* 1180 */ 138, 138, 138, 137, 115, 116, 117, 138, 138, 105, |
| 1097 | /* 1190 */ 138, 138, 137, 138, 138, 138, 138, 105, 137, 115, |
| 1098 | /* 1200 */ 116, 117, 105, 138, 138, 105, 137, 115, 116, 117, |
| 1099 | /* 1210 */ 138, 138, 115, 116, 117, 115, 116, 117, 138, 138, |
| 1100 | /* 1220 */ 138, 137, 138, 138, 138, 138, 138, 138, 138, 137, |
| 1101 | /* 1230 */ 105, 138, 138, 105, 137, 138, 138, 137, 105, 138, |
| 1102 | /* 1240 */ 115, 116, 117, 115, 116, 117, 105, 138, 115, 116, |
| 1103 | /* 1250 */ 117, 138, 138, 138, 138, 138, 115, 116, 117, 138, |
| 1104 | /* 1260 */ 138, 138, 137, 138, 138, 137, 138, 138, 138, 105, |
| 1105 | /* 1270 */ 137, 138, 105, 138, 138, 138, 138, 138, 137, 115, |
| 1106 | /* 1280 */ 116, 117, 115, 116, 117, 138, 138, 138, 138, 138, |
| 1107 | /* 1290 */ 138, 138, 138, 138, 138, 138, 138, 138, 138, 138, |
| 1108 | /* 1300 */ 138, 137, 138, 138, 137, 101, 101, 101, 101, 101, |
| 1109 | /* 1310 */ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, |
| 1110 | /* 1320 */ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, |
| 1111 | /* 1330 */ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, |
| 1112 | /* 1340 */ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, |
| 1113 | /* 1350 */ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, |
| 1114 | /* 1360 */ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, |
| 1115 | /* 1370 */ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, |
| 1116 | /* 1380 */ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, |
| 1117 | /* 1390 */ 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, |
| 1118 | /* 1400 */ 101, 101, 101, 101, 101, 101, |
| 1119 | }; |
| 1120 | #define YY_SHIFT_COUNT (163) |
| 1121 | #define YY_SHIFT_MIN (0) |
| 1122 | #define YY_SHIFT_MAX (884) |
| 1123 | static const unsigned short int yy_shift_ofst[] = { |
| 1124 | /* 0 */ 471, 129, 163, 229, 229, 229, 229, 229, 229, 229, |
| 1125 | /* 10 */ 229, 229, 229, 229, 229, 229, 229, 229, 229, 229, |
| 1126 | /* 20 */ 229, 229, 229, 229, 229, 229, 229, 358, 526, 638, |
| 1127 | /* 30 */ 358, 471, 542, 542, 0, 65, 471, 670, 638, 670, |
| 1128 | /* 40 */ 504, 504, 504, 574, 600, 638, 638, 638, 638, 638, |
| 1129 | /* 50 */ 638, 696, 638, 638, 712, 638, 638, 638, 638, 638, |
| 1130 | /* 60 */ 638, 638, 638, 638, 638, 254, 162, 162, 162, 162, |
| 1131 | /* 70 */ 162, 435, 263, 332, 401, 464, 464, 205, 48, 1305, |
| 1132 | /* 80 */ 1305, 1305, 1305, 132, 132, 528, 578, 62, 131, 341, |
| 1133 | /* 90 */ 423, 293, 7, 469, 622, 698, 792, 777, 884, 506, |
| 1134 | /* 100 */ 506, 506, 63, 506, 506, 506, 831, 506, 327, 103, |
| 1135 | /* 110 */ 174, 187, 6, 66, 141, 236, 236, 244, 250, 140, |
| 1136 | /* 120 */ 211, 381, 147, 178, 203, 216, 212, 219, 206, 223, |
| 1137 | /* 130 */ 231, 238, 234, 274, 284, 278, 257, 324, 279, 281, |
| 1138 | /* 140 */ 290, 294, 373, 380, 383, 345, 386, 387, 389, 301, |
| 1139 | /* 150 */ 321, 377, 301, 399, 400, 403, 406, 396, 402, 407, |
| 1140 | /* 160 */ 404, 344, 436, 427, |
| 1141 | }; |
| 1142 | #define YY_REDUCE_COUNT (82) |
| 1143 | #define YY_REDUCE_MIN (-132) |
| 1144 | #define YY_REDUCE_MAX (1167) |
| 1145 | static const short yy_reduce_ofst[] = { |
| 1146 | /* 0 */ -76, -98, -33, 35, 176, 213, 280, 451, 633, 709, |
| 1147 | /* 10 */ 714, 728, 747, 761, 766, 780, 805, 820, 834, 847, |
| 1148 | /* 20 */ 861, 874, 888, 913, 926, 940, 953, 54, 340, 967, |
| 1149 | /* 30 */ 305, -11, 539, 620, 76, 76, 249, 639, 455, 572, |
| 1150 | /* 40 */ 251, 656, 695, 732, 976, 990, 994, 1003, 1019, 1027, |
| 1151 | /* 50 */ 1032, 1046, 1055, 1061, 1069, 1084, 1092, 1097, 1100, 1125, |
| 1152 | /* 60 */ 1128, 1133, 1141, 1164, 1167, 82, -114, -39, -20, 30, |
| 1153 | /* 70 */ 169, 83, -132, -132, -132, -99, -77, -62, -40, 25, |
| 1154 | /* 80 */ 40, 59, 80, |
| 1155 | }; |
| 1156 | static const YYACTIONTYPE yy_default[] = { |
| 1157 | /* 0 */ 449, 443, 443, 443, 443, 443, 443, 443, 443, 443, |
| 1158 | /* 10 */ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443, |
| 1159 | /* 20 */ 443, 443, 443, 443, 443, 443, 443, 443, 473, 576, |
| @@ -1164,10 +1214,11 @@ | |
| 1214 | 0, /* COLOR => nothing */ |
| 1215 | 0, /* THICKNESS => nothing */ |
| 1216 | 0, /* PRINT => nothing */ |
| 1217 | 0, /* STRING => nothing */ |
| 1218 | 0, /* COMMA => nothing */ |
| 1219 | 0, /* ISODATE => nothing */ |
| 1220 | 0, /* CLASSNAME => nothing */ |
| 1221 | 0, /* LB => nothing */ |
| 1222 | 0, /* RB => nothing */ |
| 1223 | 0, /* UP => nothing */ |
| 1224 | 0, /* DOWN => nothing */ |
| @@ -1347,120 +1398,122 @@ | |
| 1398 | /* 21 */ "COLOR", |
| 1399 | /* 22 */ "THICKNESS", |
| 1400 | /* 23 */ "PRINT", |
| 1401 | /* 24 */ "STRING", |
| 1402 | /* 25 */ "COMMA", |
| 1403 | /* 26 */ "ISODATE", |
| 1404 | /* 27 */ "CLASSNAME", |
| 1405 | /* 28 */ "LB", |
| 1406 | /* 29 */ "RB", |
| 1407 | /* 30 */ "UP", |
| 1408 | /* 31 */ "DOWN", |
| 1409 | /* 32 */ "LEFT", |
| 1410 | /* 33 */ "RIGHT", |
| 1411 | /* 34 */ "CLOSE", |
| 1412 | /* 35 */ "CHOP", |
| 1413 | /* 36 */ "FROM", |
| 1414 | /* 37 */ "TO", |
| 1415 | /* 38 */ "THEN", |
| 1416 | /* 39 */ "HEADING", |
| 1417 | /* 40 */ "GO", |
| 1418 | /* 41 */ "AT", |
| 1419 | /* 42 */ "WITH", |
| 1420 | /* 43 */ "SAME", |
| 1421 | /* 44 */ "AS", |
| 1422 | /* 45 */ "FIT", |
| 1423 | /* 46 */ "BEHIND", |
| 1424 | /* 47 */ "UNTIL", |
| 1425 | /* 48 */ "EVEN", |
| 1426 | /* 49 */ "DOT_E", |
| 1427 | /* 50 */ "HEIGHT", |
| 1428 | /* 51 */ "WIDTH", |
| 1429 | /* 52 */ "RADIUS", |
| 1430 | /* 53 */ "DIAMETER", |
| 1431 | /* 54 */ "DOTTED", |
| 1432 | /* 55 */ "DASHED", |
| 1433 | /* 56 */ "CW", |
| 1434 | /* 57 */ "CCW", |
| 1435 | /* 58 */ "LARROW", |
| 1436 | /* 59 */ "RARROW", |
| 1437 | /* 60 */ "LRARROW", |
| 1438 | /* 61 */ "INVIS", |
| 1439 | /* 62 */ "THICK", |
| 1440 | /* 63 */ "THIN", |
| 1441 | /* 64 */ "SOLID", |
| 1442 | /* 65 */ "CENTER", |
| 1443 | /* 66 */ "LJUST", |
| 1444 | /* 67 */ "RJUST", |
| 1445 | /* 68 */ "ABOVE", |
| 1446 | /* 69 */ "BELOW", |
| 1447 | /* 70 */ "ITALIC", |
| 1448 | /* 71 */ "BOLD", |
| 1449 | /* 72 */ "MONO", |
| 1450 | /* 73 */ "ALIGNED", |
| 1451 | /* 74 */ "BIG", |
| 1452 | /* 75 */ "SMALL", |
| 1453 | /* 76 */ "AND", |
| 1454 | /* 77 */ "LT", |
| 1455 | /* 78 */ "GT", |
| 1456 | /* 79 */ "ON", |
| 1457 | /* 80 */ "WAY", |
| 1458 | /* 81 */ "BETWEEN", |
| 1459 | /* 82 */ "THE", |
| 1460 | /* 83 */ "NTH", |
| 1461 | /* 84 */ "VERTEX", |
| 1462 | /* 85 */ "TOP", |
| 1463 | /* 86 */ "BOTTOM", |
| 1464 | /* 87 */ "START", |
| 1465 | /* 88 */ "END", |
| 1466 | /* 89 */ "IN", |
| 1467 | /* 90 */ "THIS", |
| 1468 | /* 91 */ "DOT_U", |
| 1469 | /* 92 */ "LAST", |
| 1470 | /* 93 */ "NUMBER", |
| 1471 | /* 94 */ "FUNC1", |
| 1472 | /* 95 */ "FUNC2", |
| 1473 | /* 96 */ "DIST", |
| 1474 | /* 97 */ "DOT_XY", |
| 1475 | /* 98 */ "X", |
| 1476 | /* 99 */ "Y", |
| 1477 | /* 100 */ "DOT_L", |
| 1478 | /* 101 */ "statement_list", |
| 1479 | /* 102 */ "statement", |
| 1480 | /* 103 */ "unnamed_statement", |
| 1481 | /* 104 */ "basetype", |
| 1482 | /* 105 */ "expr", |
| 1483 | /* 106 */ "numproperty", |
| 1484 | /* 107 */ "edge", |
| 1485 | /* 108 */ "isodate", |
| 1486 | /* 109 */ "direction", |
| 1487 | /* 110 */ "dashproperty", |
| 1488 | /* 111 */ "colorproperty", |
| 1489 | /* 112 */ "locproperty", |
| 1490 | /* 113 */ "position", |
| 1491 | /* 114 */ "place", |
| 1492 | /* 115 */ "object", |
| 1493 | /* 116 */ "objectname", |
| 1494 | /* 117 */ "nth", |
| 1495 | /* 118 */ "textposition", |
| 1496 | /* 119 */ "rvalue", |
| 1497 | /* 120 */ "lvalue", |
| 1498 | /* 121 */ "even", |
| 1499 | /* 122 */ "relexpr", |
| 1500 | /* 123 */ "optrelexpr", |
| 1501 | /* 124 */ "document", |
| 1502 | /* 125 */ "print", |
| 1503 | /* 126 */ "prlist", |
| 1504 | /* 127 */ "pritem", |
| 1505 | /* 128 */ "prsep", |
| 1506 | /* 129 */ "attribute_list", |
| 1507 | /* 130 */ "savelist", |
| 1508 | /* 131 */ "alist", |
| 1509 | /* 132 */ "attribute", |
| 1510 | /* 133 */ "go", |
| 1511 | /* 134 */ "boolproperty", |
| 1512 | /* 135 */ "withclause", |
| 1513 | /* 136 */ "between", |
| 1514 | /* 137 */ "place2", |
| 1515 | }; |
| 1516 | #endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */ |
| 1517 | |
| 1518 | #ifndef NDEBUG |
| 1519 | /* For tracing reduce actions, the names of all rules are required. |
| @@ -1743,24 +1796,24 @@ | |
| 1796 | ** Note: during a reduce, the only symbols destroyed are those |
| 1797 | ** which appear on the RHS of the rule, but which are *not* used |
| 1798 | ** inside the C code. |
| 1799 | */ |
| 1800 | /********* Begin destructor definitions ***************************************/ |
| 1801 | case 101: /* statement_list */ |
| 1802 | { |
| 1803 | #line 524 "pikchr.y" |
| 1804 | pik_elist_free(p,(yypminor->yy23)); |
| 1805 | #line 1805 "pikchr.c" |
| 1806 | } |
| 1807 | break; |
| 1808 | case 102: /* statement */ |
| 1809 | case 103: /* unnamed_statement */ |
| 1810 | case 104: /* basetype */ |
| 1811 | { |
| 1812 | #line 526 "pikchr.y" |
| 1813 | pik_elem_free(p,(yypminor->yy54)); |
| 1814 | #line 1814 "pikchr.c" |
| 1815 | } |
| 1816 | break; |
| 1817 | /********* End destructor definitions *****************************************/ |
| 1818 | default: break; /* If no destructor action specified: do nothing */ |
| 1819 | } |
| @@ -1991,14 +2044,14 @@ | |
| 2044 | #endif |
| 2045 | while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser); |
| 2046 | /* Here code is inserted which will execute if the parser |
| 2047 | ** stack every overflows */ |
| 2048 | /******** Begin %stack_overflow code ******************************************/ |
| 2049 | #line 559 "pikchr.y" |
| 2050 | |
| 2051 | pik_error(p, 0, "parser stack overflow"); |
| 2052 | #line 2052 "pikchr.c" |
| 2053 | /******** End %stack_overflow code ********************************************/ |
| 2054 | pik_parserARG_STORE /* Suppress warning about unused %extra_argument var */ |
| 2055 | pik_parserCTX_STORE |
| 2056 | } |
| 2057 | |
| @@ -2060,166 +2113,166 @@ | |
| 2113 | } |
| 2114 | |
| 2115 | /* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side |
| 2116 | ** of that rule */ |
| 2117 | static const YYCODETYPE yyRuleInfoLhs[] = { |
| 2118 | 124, /* (0) document ::= statement_list */ |
| 2119 | 101, /* (1) statement_list ::= statement */ |
| 2120 | 101, /* (2) statement_list ::= statement_list EOL statement */ |
| 2121 | 102, /* (3) statement ::= */ |
| 2122 | 102, /* (4) statement ::= direction */ |
| 2123 | 102, /* (5) statement ::= lvalue ASSIGN rvalue */ |
| 2124 | 102, /* (6) statement ::= PLACENAME COLON unnamed_statement */ |
| 2125 | 102, /* (7) statement ::= PLACENAME COLON position */ |
| 2126 | 102, /* (8) statement ::= unnamed_statement */ |
| 2127 | 102, /* (9) statement ::= print prlist */ |
| 2128 | 102, /* (10) statement ::= ASSERT LP expr EQ expr RP */ |
| 2129 | 102, /* (11) statement ::= ASSERT LP position EQ position RP */ |
| 2130 | 102, /* (12) statement ::= DEFINE ID CODEBLOCK */ |
| 2131 | 119, /* (13) rvalue ::= PLACENAME */ |
| 2132 | 127, /* (14) pritem ::= FILL */ |
| 2133 | 127, /* (15) pritem ::= COLOR */ |
| 2134 | 127, /* (16) pritem ::= THICKNESS */ |
| 2135 | 127, /* (17) pritem ::= rvalue */ |
| 2136 | 127, /* (18) pritem ::= STRING */ |
| 2137 | 128, /* (19) prsep ::= COMMA */ |
| 2138 | 103, /* (20) unnamed_statement ::= basetype attribute_list */ |
| 2139 | 104, /* (21) basetype ::= CLASSNAME */ |
| 2140 | 104, /* (22) basetype ::= STRING textposition */ |
| 2141 | 104, /* (23) basetype ::= LB savelist statement_list RB */ |
| 2142 | 130, /* (24) savelist ::= */ |
| 2143 | 122, /* (25) relexpr ::= expr */ |
| 2144 | 122, /* (26) relexpr ::= expr PERCENT */ |
| 2145 | 123, /* (27) optrelexpr ::= */ |
| 2146 | 129, /* (28) attribute_list ::= relexpr alist */ |
| 2147 | 132, /* (29) attribute ::= numproperty relexpr */ |
| 2148 | 132, /* (30) attribute ::= dashproperty expr */ |
| 2149 | 132, /* (31) attribute ::= dashproperty */ |
| 2150 | 132, /* (32) attribute ::= colorproperty rvalue */ |
| 2151 | 132, /* (33) attribute ::= go direction optrelexpr */ |
| 2152 | 132, /* (34) attribute ::= go direction even position */ |
| 2153 | 132, /* (35) attribute ::= CLOSE */ |
| 2154 | 132, /* (36) attribute ::= CHOP */ |
| 2155 | 132, /* (37) attribute ::= FROM position */ |
| 2156 | 132, /* (38) attribute ::= TO position */ |
| 2157 | 132, /* (39) attribute ::= THEN */ |
| 2158 | 132, /* (40) attribute ::= THEN optrelexpr HEADING expr */ |
| 2159 | 132, /* (41) attribute ::= THEN optrelexpr EDGEPT */ |
| 2160 | 132, /* (42) attribute ::= GO optrelexpr HEADING expr */ |
| 2161 | 132, /* (43) attribute ::= GO optrelexpr EDGEPT */ |
| 2162 | 132, /* (44) attribute ::= AT position */ |
| 2163 | 132, /* (45) attribute ::= SAME */ |
| 2164 | 132, /* (46) attribute ::= SAME AS object */ |
| 2165 | 132, /* (47) attribute ::= STRING textposition */ |
| 2166 | 132, /* (48) attribute ::= FIT */ |
| 2167 | 132, /* (49) attribute ::= BEHIND object */ |
| 2168 | 135, /* (50) withclause ::= DOT_E edge AT position */ |
| 2169 | 135, /* (51) withclause ::= edge AT position */ |
| 2170 | 106, /* (52) numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */ |
| 2171 | 134, /* (53) boolproperty ::= CW */ |
| 2172 | 134, /* (54) boolproperty ::= CCW */ |
| 2173 | 134, /* (55) boolproperty ::= LARROW */ |
| 2174 | 134, /* (56) boolproperty ::= RARROW */ |
| 2175 | 134, /* (57) boolproperty ::= LRARROW */ |
| 2176 | 134, /* (58) boolproperty ::= INVIS */ |
| 2177 | 134, /* (59) boolproperty ::= THICK */ |
| 2178 | 134, /* (60) boolproperty ::= THIN */ |
| 2179 | 134, /* (61) boolproperty ::= SOLID */ |
| 2180 | 118, /* (62) textposition ::= */ |
| 2181 | 118, /* (63) textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL */ |
| 2182 | 113, /* (64) position ::= expr COMMA expr */ |
| 2183 | 113, /* (65) position ::= place PLUS expr COMMA expr */ |
| 2184 | 113, /* (66) position ::= place MINUS expr COMMA expr */ |
| 2185 | 113, /* (67) position ::= place PLUS LP expr COMMA expr RP */ |
| 2186 | 113, /* (68) position ::= place MINUS LP expr COMMA expr RP */ |
| 2187 | 113, /* (69) position ::= LP position COMMA position RP */ |
| 2188 | 113, /* (70) position ::= LP position RP */ |
| 2189 | 113, /* (71) position ::= expr between position AND position */ |
| 2190 | 113, /* (72) position ::= expr LT position COMMA position GT */ |
| 2191 | 113, /* (73) position ::= expr ABOVE position */ |
| 2192 | 113, /* (74) position ::= expr BELOW position */ |
| 2193 | 113, /* (75) position ::= expr LEFT OF position */ |
| 2194 | 113, /* (76) position ::= expr RIGHT OF position */ |
| 2195 | 113, /* (77) position ::= expr ON HEADING EDGEPT OF position */ |
| 2196 | 113, /* (78) position ::= expr HEADING EDGEPT OF position */ |
| 2197 | 113, /* (79) position ::= expr EDGEPT OF position */ |
| 2198 | 113, /* (80) position ::= expr ON HEADING expr FROM position */ |
| 2199 | 113, /* (81) position ::= expr HEADING expr FROM position */ |
| 2200 | 114, /* (82) place ::= edge OF object */ |
| 2201 | 137, /* (83) place2 ::= object */ |
| 2202 | 137, /* (84) place2 ::= object DOT_E edge */ |
| 2203 | 137, /* (85) place2 ::= NTH VERTEX OF object */ |
| 2204 | 115, /* (86) object ::= nth */ |
| 2205 | 115, /* (87) object ::= nth OF|IN object */ |
| 2206 | 116, /* (88) objectname ::= THIS */ |
| 2207 | 116, /* (89) objectname ::= PLACENAME */ |
| 2208 | 116, /* (90) objectname ::= objectname DOT_U PLACENAME */ |
| 2209 | 117, /* (91) nth ::= NTH CLASSNAME */ |
| 2210 | 117, /* (92) nth ::= NTH LAST CLASSNAME */ |
| 2211 | 117, /* (93) nth ::= LAST CLASSNAME */ |
| 2212 | 117, /* (94) nth ::= LAST */ |
| 2213 | 117, /* (95) nth ::= NTH LB RB */ |
| 2214 | 117, /* (96) nth ::= NTH LAST LB RB */ |
| 2215 | 117, /* (97) nth ::= LAST LB RB */ |
| 2216 | 105, /* (98) expr ::= expr PLUS expr */ |
| 2217 | 105, /* (99) expr ::= expr MINUS expr */ |
| 2218 | 105, /* (100) expr ::= expr STAR expr */ |
| 2219 | 105, /* (101) expr ::= expr SLASH expr */ |
| 2220 | 105, /* (102) expr ::= MINUS expr */ |
| 2221 | 105, /* (103) expr ::= PLUS expr */ |
| 2222 | 105, /* (104) expr ::= LP expr RP */ |
| 2223 | 105, /* (105) expr ::= LP FILL|COLOR|THICKNESS RP */ |
| 2224 | 105, /* (106) expr ::= NUMBER */ |
| 2225 | 105, /* (107) expr ::= ID */ |
| 2226 | 105, /* (108) expr ::= FUNC1 LP expr RP */ |
| 2227 | 105, /* (109) expr ::= FUNC2 LP expr COMMA expr RP */ |
| 2228 | 105, /* (110) expr ::= DIST LP position COMMA position RP */ |
| 2229 | 105, /* (111) expr ::= place2 DOT_XY X */ |
| 2230 | 105, /* (112) expr ::= place2 DOT_XY Y */ |
| 2231 | 105, /* (113) expr ::= object DOT_L numproperty */ |
| 2232 | 105, /* (114) expr ::= object DOT_L dashproperty */ |
| 2233 | 105, /* (115) expr ::= object DOT_L colorproperty */ |
| 2234 | 120, /* (116) lvalue ::= ID */ |
| 2235 | 120, /* (117) lvalue ::= FILL */ |
| 2236 | 120, /* (118) lvalue ::= COLOR */ |
| 2237 | 120, /* (119) lvalue ::= THICKNESS */ |
| 2238 | 119, /* (120) rvalue ::= expr */ |
| 2239 | 125, /* (121) print ::= PRINT */ |
| 2240 | 126, /* (122) prlist ::= pritem */ |
| 2241 | 126, /* (123) prlist ::= prlist prsep pritem */ |
| 2242 | 109, /* (124) direction ::= UP */ |
| 2243 | 109, /* (125) direction ::= DOWN */ |
| 2244 | 109, /* (126) direction ::= LEFT */ |
| 2245 | 109, /* (127) direction ::= RIGHT */ |
| 2246 | 123, /* (128) optrelexpr ::= relexpr */ |
| 2247 | 129, /* (129) attribute_list ::= alist */ |
| 2248 | 131, /* (130) alist ::= */ |
| 2249 | 131, /* (131) alist ::= alist attribute */ |
| 2250 | 132, /* (132) attribute ::= boolproperty */ |
| 2251 | 132, /* (133) attribute ::= WITH withclause */ |
| 2252 | 133, /* (134) go ::= GO */ |
| 2253 | 133, /* (135) go ::= */ |
| 2254 | 121, /* (136) even ::= UNTIL EVEN WITH */ |
| 2255 | 121, /* (137) even ::= EVEN WITH */ |
| 2256 | 110, /* (138) dashproperty ::= DOTTED */ |
| 2257 | 110, /* (139) dashproperty ::= DASHED */ |
| 2258 | 111, /* (140) colorproperty ::= FILL */ |
| 2259 | 111, /* (141) colorproperty ::= COLOR */ |
| 2260 | 113, /* (142) position ::= place */ |
| 2261 | 136, /* (143) between ::= WAY BETWEEN */ |
| 2262 | 136, /* (144) between ::= BETWEEN */ |
| 2263 | 136, /* (145) between ::= OF THE WAY BETWEEN */ |
| 2264 | 114, /* (146) place ::= place2 */ |
| 2265 | 107, /* (147) edge ::= CENTER */ |
| 2266 | 107, /* (148) edge ::= EDGEPT */ |
| 2267 | 107, /* (149) edge ::= TOP */ |
| 2268 | 107, /* (150) edge ::= BOTTOM */ |
| 2269 | 107, /* (151) edge ::= START */ |
| 2270 | 107, /* (152) edge ::= END */ |
| 2271 | 107, /* (153) edge ::= RIGHT */ |
| 2272 | 107, /* (154) edge ::= LEFT */ |
| 2273 | 115, /* (155) object ::= objectname */ |
| 2274 | }; |
| 2275 | |
| 2276 | /* For rule J, yyRuleInfoNRhs[J] contains the negative of the number |
| 2277 | ** of symbols on the right-hand side of that rule. */ |
| 2278 | static const signed char yyRuleInfoNRhs[] = { |
| @@ -2419,620 +2472,620 @@ | |
| 2472 | ** break; |
| 2473 | */ |
| 2474 | /********** Begin reduce actions **********************************************/ |
| 2475 | YYMINORTYPE yylhsminor; |
| 2476 | case 0: /* document ::= statement_list */ |
| 2477 | #line 563 "pikchr.y" |
| 2478 | {pik_render(p,yymsp[0].minor.yy23);} |
| 2479 | #line 2479 "pikchr.c" |
| 2480 | break; |
| 2481 | case 1: /* statement_list ::= statement */ |
| 2482 | #line 566 "pikchr.y" |
| 2483 | { yylhsminor.yy23 = pik_elist_append(p,0,yymsp[0].minor.yy54); } |
| 2484 | #line 2484 "pikchr.c" |
| 2485 | yymsp[0].minor.yy23 = yylhsminor.yy23; |
| 2486 | break; |
| 2487 | case 2: /* statement_list ::= statement_list EOL statement */ |
| 2488 | #line 568 "pikchr.y" |
| 2489 | { yylhsminor.yy23 = pik_elist_append(p,yymsp[-2].minor.yy23,yymsp[0].minor.yy54); } |
| 2490 | #line 2490 "pikchr.c" |
| 2491 | yymsp[-2].minor.yy23 = yylhsminor.yy23; |
| 2492 | break; |
| 2493 | case 3: /* statement ::= */ |
| 2494 | #line 571 "pikchr.y" |
| 2495 | { yymsp[1].minor.yy54 = 0; } |
| 2496 | #line 2496 "pikchr.c" |
| 2497 | break; |
| 2498 | case 4: /* statement ::= direction */ |
| 2499 | #line 572 "pikchr.y" |
| 2500 | { pik_set_direction(p,yymsp[0].minor.yy0.eCode); yylhsminor.yy54=0; } |
| 2501 | #line 2501 "pikchr.c" |
| 2502 | yymsp[0].minor.yy54 = yylhsminor.yy54; |
| 2503 | break; |
| 2504 | case 5: /* statement ::= lvalue ASSIGN rvalue */ |
| 2505 | #line 573 "pikchr.y" |
| 2506 | {pik_set_var(p,&yymsp[-2].minor.yy0,yymsp[0].minor.yy129,&yymsp[-1].minor.yy0); yylhsminor.yy54=0;} |
| 2507 | #line 2507 "pikchr.c" |
| 2508 | yymsp[-2].minor.yy54 = yylhsminor.yy54; |
| 2509 | break; |
| 2510 | case 6: /* statement ::= PLACENAME COLON unnamed_statement */ |
| 2511 | #line 575 "pikchr.y" |
| 2512 | { yylhsminor.yy54 = yymsp[0].minor.yy54; pik_elem_setname(p,yymsp[0].minor.yy54,&yymsp[-2].minor.yy0); } |
| 2513 | #line 2513 "pikchr.c" |
| 2514 | yymsp[-2].minor.yy54 = yylhsminor.yy54; |
| 2515 | break; |
| 2516 | case 7: /* statement ::= PLACENAME COLON position */ |
| 2517 | #line 577 "pikchr.y" |
| 2518 | { yylhsminor.yy54 = pik_elem_new(p,0,0,0); |
| 2519 | if(yylhsminor.yy54){ yylhsminor.yy54->ptAt = yymsp[0].minor.yy187; pik_elem_setname(p,yylhsminor.yy54,&yymsp[-2].minor.yy0); }} |
| 2520 | #line 2520 "pikchr.c" |
| 2521 | yymsp[-2].minor.yy54 = yylhsminor.yy54; |
| 2522 | break; |
| 2523 | case 8: /* statement ::= unnamed_statement */ |
| 2524 | #line 579 "pikchr.y" |
| 2525 | {yylhsminor.yy54 = yymsp[0].minor.yy54;} |
| 2526 | #line 2526 "pikchr.c" |
| 2527 | yymsp[0].minor.yy54 = yylhsminor.yy54; |
| 2528 | break; |
| 2529 | case 9: /* statement ::= print prlist */ |
| 2530 | #line 580 "pikchr.y" |
| 2531 | {pik_append(p,"<br>\n",5); yymsp[-1].minor.yy54=0;} |
| 2532 | #line 2532 "pikchr.c" |
| 2533 | break; |
| 2534 | case 10: /* statement ::= ASSERT LP expr EQ expr RP */ |
| 2535 | #line 585 "pikchr.y" |
| 2536 | {yymsp[-5].minor.yy54=pik_assert(p,yymsp[-3].minor.yy129,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy129);} |
| 2537 | #line 2537 "pikchr.c" |
| 2538 | break; |
| 2539 | case 11: /* statement ::= ASSERT LP position EQ position RP */ |
| 2540 | #line 587 "pikchr.y" |
| 2541 | {yymsp[-5].minor.yy54=pik_position_assert(p,&yymsp[-3].minor.yy187,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy187);} |
| 2542 | #line 2542 "pikchr.c" |
| 2543 | break; |
| 2544 | case 12: /* statement ::= DEFINE ID CODEBLOCK */ |
| 2545 | #line 588 "pikchr.y" |
| 2546 | {yymsp[-2].minor.yy54=0; pik_add_macro(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);} |
| 2547 | #line 2547 "pikchr.c" |
| 2548 | break; |
| 2549 | case 13: /* rvalue ::= PLACENAME */ |
| 2550 | #line 599 "pikchr.y" |
| 2551 | {yylhsminor.yy129 = pik_lookup_color(p,&yymsp[0].minor.yy0);} |
| 2552 | #line 2552 "pikchr.c" |
| 2553 | yymsp[0].minor.yy129 = yylhsminor.yy129; |
| 2554 | break; |
| 2555 | case 14: /* pritem ::= FILL */ |
| 2556 | case 15: /* pritem ::= COLOR */ yytestcase(yyruleno==15); |
| 2557 | case 16: /* pritem ::= THICKNESS */ yytestcase(yyruleno==16); |
| 2558 | #line 604 "pikchr.y" |
| 2559 | {pik_append_num(p,"",pik_value(p,yymsp[0].minor.yy0.z,yymsp[0].minor.yy0.n,0));} |
| 2560 | #line 2560 "pikchr.c" |
| 2561 | break; |
| 2562 | case 17: /* pritem ::= rvalue */ |
| 2563 | #line 607 "pikchr.y" |
| 2564 | {pik_append_num(p,"",yymsp[0].minor.yy129);} |
| 2565 | #line 2565 "pikchr.c" |
| 2566 | break; |
| 2567 | case 18: /* pritem ::= STRING */ |
| 2568 | #line 608 "pikchr.y" |
| 2569 | {pik_append_text(p,yymsp[0].minor.yy0.z+1,yymsp[0].minor.yy0.n-2,0);} |
| 2570 | #line 2570 "pikchr.c" |
| 2571 | break; |
| 2572 | case 19: /* prsep ::= COMMA */ |
| 2573 | #line 609 "pikchr.y" |
| 2574 | {pik_append(p, " ", 1);} |
| 2575 | #line 2575 "pikchr.c" |
| 2576 | break; |
| 2577 | case 20: /* unnamed_statement ::= basetype attribute_list */ |
| 2578 | #line 614 "pikchr.y" |
| 2579 | {yylhsminor.yy54 = yymsp[-1].minor.yy54; pik_after_adding_attributes(p,yylhsminor.yy54);} |
| 2580 | #line 2580 "pikchr.c" |
| 2581 | yymsp[-1].minor.yy54 = yylhsminor.yy54; |
| 2582 | break; |
| 2583 | case 21: /* basetype ::= CLASSNAME */ |
| 2584 | #line 616 "pikchr.y" |
| 2585 | {yylhsminor.yy54 = pik_elem_new(p,&yymsp[0].minor.yy0,0,0); } |
| 2586 | #line 2586 "pikchr.c" |
| 2587 | yymsp[0].minor.yy54 = yylhsminor.yy54; |
| 2588 | break; |
| 2589 | case 22: /* basetype ::= STRING textposition */ |
| 2590 | #line 618 "pikchr.y" |
| 2591 | {yymsp[-1].minor.yy0.eCode = yymsp[0].minor.yy272; yylhsminor.yy54 = pik_elem_new(p,0,&yymsp[-1].minor.yy0,0); } |
| 2592 | #line 2592 "pikchr.c" |
| 2593 | yymsp[-1].minor.yy54 = yylhsminor.yy54; |
| 2594 | break; |
| 2595 | case 23: /* basetype ::= LB savelist statement_list RB */ |
| 2596 | #line 620 "pikchr.y" |
| 2597 | { p->list = yymsp[-2].minor.yy23; yymsp[-3].minor.yy54 = pik_elem_new(p,0,0,yymsp[-1].minor.yy23); if(yymsp[-3].minor.yy54) yymsp[-3].minor.yy54->errTok = yymsp[0].minor.yy0; } |
| 2598 | #line 2598 "pikchr.c" |
| 2599 | break; |
| 2600 | case 24: /* savelist ::= */ |
| 2601 | #line 625 "pikchr.y" |
| 2602 | {yymsp[1].minor.yy23 = p->list; p->list = 0;} |
| 2603 | #line 2603 "pikchr.c" |
| 2604 | break; |
| 2605 | case 25: /* relexpr ::= expr */ |
| 2606 | #line 632 "pikchr.y" |
| 2607 | {yylhsminor.yy28.rAbs = yymsp[0].minor.yy129; yylhsminor.yy28.rRel = 0;} |
| 2608 | #line 2608 "pikchr.c" |
| 2609 | yymsp[0].minor.yy28 = yylhsminor.yy28; |
| 2610 | break; |
| 2611 | case 26: /* relexpr ::= expr PERCENT */ |
| 2612 | #line 633 "pikchr.y" |
| 2613 | {yylhsminor.yy28.rAbs = 0; yylhsminor.yy28.rRel = yymsp[-1].minor.yy129/100;} |
| 2614 | #line 2614 "pikchr.c" |
| 2615 | yymsp[-1].minor.yy28 = yylhsminor.yy28; |
| 2616 | break; |
| 2617 | case 27: /* optrelexpr ::= */ |
| 2618 | #line 635 "pikchr.y" |
| 2619 | {yymsp[1].minor.yy28.rAbs = 0; yymsp[1].minor.yy28.rRel = 1.0;} |
| 2620 | #line 2620 "pikchr.c" |
| 2621 | break; |
| 2622 | case 28: /* attribute_list ::= relexpr alist */ |
| 2623 | #line 637 "pikchr.y" |
| 2624 | {pik_add_direction(p,0,&yymsp[-1].minor.yy28);} |
| 2625 | #line 2625 "pikchr.c" |
| 2626 | break; |
| 2627 | case 29: /* attribute ::= numproperty relexpr */ |
| 2628 | #line 641 "pikchr.y" |
| 2629 | { pik_set_numprop(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy28); } |
| 2630 | #line 2630 "pikchr.c" |
| 2631 | break; |
| 2632 | case 30: /* attribute ::= dashproperty expr */ |
| 2633 | #line 642 "pikchr.y" |
| 2634 | { pik_set_dashed(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy129); } |
| 2635 | #line 2635 "pikchr.c" |
| 2636 | break; |
| 2637 | case 31: /* attribute ::= dashproperty */ |
| 2638 | #line 643 "pikchr.y" |
| 2639 | { pik_set_dashed(p,&yymsp[0].minor.yy0,0); } |
| 2640 | #line 2640 "pikchr.c" |
| 2641 | break; |
| 2642 | case 32: /* attribute ::= colorproperty rvalue */ |
| 2643 | #line 644 "pikchr.y" |
| 2644 | { pik_set_clrprop(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy129); } |
| 2645 | #line 2645 "pikchr.c" |
| 2646 | break; |
| 2647 | case 33: /* attribute ::= go direction optrelexpr */ |
| 2648 | #line 645 "pikchr.y" |
| 2649 | { pik_add_direction(p,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy28);} |
| 2650 | #line 2650 "pikchr.c" |
| 2651 | break; |
| 2652 | case 34: /* attribute ::= go direction even position */ |
| 2653 | #line 646 "pikchr.y" |
| 2654 | {pik_evenwith(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy187);} |
| 2655 | #line 2655 "pikchr.c" |
| 2656 | break; |
| 2657 | case 35: /* attribute ::= CLOSE */ |
| 2658 | #line 647 "pikchr.y" |
| 2659 | { pik_close_path(p,&yymsp[0].minor.yy0); } |
| 2660 | #line 2660 "pikchr.c" |
| 2661 | break; |
| 2662 | case 36: /* attribute ::= CHOP */ |
| 2663 | #line 648 "pikchr.y" |
| 2664 | { p->cur->bChop = 1; } |
| 2665 | #line 2665 "pikchr.c" |
| 2666 | break; |
| 2667 | case 37: /* attribute ::= FROM position */ |
| 2668 | #line 649 "pikchr.y" |
| 2669 | { pik_set_from(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy187); } |
| 2670 | #line 2670 "pikchr.c" |
| 2671 | break; |
| 2672 | case 38: /* attribute ::= TO position */ |
| 2673 | #line 650 "pikchr.y" |
| 2674 | { pik_add_to(p,p->cur,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy187); } |
| 2675 | #line 2675 "pikchr.c" |
| 2676 | break; |
| 2677 | case 39: /* attribute ::= THEN */ |
| 2678 | #line 651 "pikchr.y" |
| 2679 | { pik_then(p, &yymsp[0].minor.yy0, p->cur); } |
| 2680 | #line 2680 "pikchr.c" |
| 2681 | break; |
| 2682 | case 40: /* attribute ::= THEN optrelexpr HEADING expr */ |
| 2683 | case 42: /* attribute ::= GO optrelexpr HEADING expr */ yytestcase(yyruleno==42); |
| 2684 | #line 653 "pikchr.y" |
| 2685 | {pik_move_hdg(p,&yymsp[-2].minor.yy28,&yymsp[-1].minor.yy0,yymsp[0].minor.yy129,0,&yymsp[-3].minor.yy0);} |
| 2686 | #line 2686 "pikchr.c" |
| 2687 | break; |
| 2688 | case 41: /* attribute ::= THEN optrelexpr EDGEPT */ |
| 2689 | case 43: /* attribute ::= GO optrelexpr EDGEPT */ yytestcase(yyruleno==43); |
| 2690 | #line 654 "pikchr.y" |
| 2691 | {pik_move_hdg(p,&yymsp[-1].minor.yy28,0,0,&yymsp[0].minor.yy0,&yymsp[-2].minor.yy0);} |
| 2692 | #line 2692 "pikchr.c" |
| 2693 | break; |
| 2694 | case 44: /* attribute ::= AT position */ |
| 2695 | #line 659 "pikchr.y" |
| 2696 | { pik_set_at(p,0,&yymsp[0].minor.yy187,&yymsp[-1].minor.yy0); } |
| 2697 | #line 2697 "pikchr.c" |
| 2698 | break; |
| 2699 | case 45: /* attribute ::= SAME */ |
| 2700 | #line 661 "pikchr.y" |
| 2701 | {pik_same(p,0,&yymsp[0].minor.yy0);} |
| 2702 | #line 2702 "pikchr.c" |
| 2703 | break; |
| 2704 | case 46: /* attribute ::= SAME AS object */ |
| 2705 | #line 662 "pikchr.y" |
| 2706 | {pik_same(p,yymsp[0].minor.yy54,&yymsp[-2].minor.yy0);} |
| 2707 | #line 2707 "pikchr.c" |
| 2708 | break; |
| 2709 | case 47: /* attribute ::= STRING textposition */ |
| 2710 | #line 663 "pikchr.y" |
| 2711 | {pik_add_txt(p,&yymsp[-1].minor.yy0,yymsp[0].minor.yy272);} |
| 2712 | #line 2712 "pikchr.c" |
| 2713 | break; |
| 2714 | case 48: /* attribute ::= FIT */ |
| 2715 | #line 664 "pikchr.y" |
| 2716 | {pik_size_to_fit(p,0,&yymsp[0].minor.yy0,3); } |
| 2717 | #line 2717 "pikchr.c" |
| 2718 | break; |
| 2719 | case 49: /* attribute ::= BEHIND object */ |
| 2720 | #line 665 "pikchr.y" |
| 2721 | {pik_behind(p,yymsp[0].minor.yy54);} |
| 2722 | #line 2722 "pikchr.c" |
| 2723 | break; |
| 2724 | case 50: /* withclause ::= DOT_E edge AT position */ |
| 2725 | case 51: /* withclause ::= edge AT position */ yytestcase(yyruleno==51); |
| 2726 | #line 673 "pikchr.y" |
| 2727 | { pik_set_at(p,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy187,&yymsp[-1].minor.yy0); } |
| 2728 | #line 2728 "pikchr.c" |
| 2729 | break; |
| 2730 | case 52: /* numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */ |
| 2731 | #line 677 "pikchr.y" |
| 2732 | {yylhsminor.yy0 = yymsp[0].minor.yy0;} |
| 2733 | #line 2733 "pikchr.c" |
| 2734 | yymsp[0].minor.yy0 = yylhsminor.yy0; |
| 2735 | break; |
| 2736 | case 53: /* boolproperty ::= CW */ |
| 2737 | #line 688 "pikchr.y" |
| 2738 | {p->cur->cw = 1;} |
| 2739 | #line 2739 "pikchr.c" |
| 2740 | break; |
| 2741 | case 54: /* boolproperty ::= CCW */ |
| 2742 | #line 689 "pikchr.y" |
| 2743 | {p->cur->cw = 0;} |
| 2744 | #line 2744 "pikchr.c" |
| 2745 | break; |
| 2746 | case 55: /* boolproperty ::= LARROW */ |
| 2747 | #line 690 "pikchr.y" |
| 2748 | {p->cur->larrow=1; p->cur->rarrow=0; } |
| 2749 | #line 2749 "pikchr.c" |
| 2750 | break; |
| 2751 | case 56: /* boolproperty ::= RARROW */ |
| 2752 | #line 691 "pikchr.y" |
| 2753 | {p->cur->larrow=0; p->cur->rarrow=1; } |
| 2754 | #line 2754 "pikchr.c" |
| 2755 | break; |
| 2756 | case 57: /* boolproperty ::= LRARROW */ |
| 2757 | #line 692 "pikchr.y" |
| 2758 | {p->cur->larrow=1; p->cur->rarrow=1; } |
| 2759 | #line 2759 "pikchr.c" |
| 2760 | break; |
| 2761 | case 58: /* boolproperty ::= INVIS */ |
| 2762 | #line 693 "pikchr.y" |
| 2763 | {p->cur->sw = -0.00001;} |
| 2764 | #line 2764 "pikchr.c" |
| 2765 | break; |
| 2766 | case 59: /* boolproperty ::= THICK */ |
| 2767 | #line 694 "pikchr.y" |
| 2768 | {p->cur->sw *= 1.5;} |
| 2769 | #line 2769 "pikchr.c" |
| 2770 | break; |
| 2771 | case 60: /* boolproperty ::= THIN */ |
| 2772 | #line 695 "pikchr.y" |
| 2773 | {p->cur->sw *= 0.67;} |
| 2774 | #line 2774 "pikchr.c" |
| 2775 | break; |
| 2776 | case 61: /* boolproperty ::= SOLID */ |
| 2777 | #line 696 "pikchr.y" |
| 2778 | {p->cur->sw = pik_value(p,"thickness",9,0); |
| 2779 | p->cur->dotted = p->cur->dashed = 0.0;} |
| 2780 | #line 2780 "pikchr.c" |
| 2781 | break; |
| 2782 | case 62: /* textposition ::= */ |
| 2783 | #line 699 "pikchr.y" |
| 2784 | {yymsp[1].minor.yy272 = 0;} |
| 2785 | #line 2785 "pikchr.c" |
| 2786 | break; |
| 2787 | case 63: /* textposition ::= textposition CENTER|LJUST|RJUST|ABOVE|BELOW|ITALIC|BOLD|MONO|ALIGNED|BIG|SMALL */ |
| 2788 | #line 702 "pikchr.y" |
| 2789 | {yylhsminor.yy272 = (short int)pik_text_position(yymsp[-1].minor.yy272,&yymsp[0].minor.yy0);} |
| 2790 | #line 2790 "pikchr.c" |
| 2791 | yymsp[-1].minor.yy272 = yylhsminor.yy272; |
| 2792 | break; |
| 2793 | case 64: /* position ::= expr COMMA expr */ |
| 2794 | #line 705 "pikchr.y" |
| 2795 | {yylhsminor.yy187.x=yymsp[-2].minor.yy129; yylhsminor.yy187.y=yymsp[0].minor.yy129;} |
| 2796 | #line 2796 "pikchr.c" |
| 2797 | yymsp[-2].minor.yy187 = yylhsminor.yy187; |
| 2798 | break; |
| 2799 | case 65: /* position ::= place PLUS expr COMMA expr */ |
| 2800 | #line 707 "pikchr.y" |
| 2801 | {yylhsminor.yy187.x=yymsp[-4].minor.yy187.x+yymsp[-2].minor.yy129; yylhsminor.yy187.y=yymsp[-4].minor.yy187.y+yymsp[0].minor.yy129;} |
| 2802 | #line 2802 "pikchr.c" |
| 2803 | yymsp[-4].minor.yy187 = yylhsminor.yy187; |
| 2804 | break; |
| 2805 | case 66: /* position ::= place MINUS expr COMMA expr */ |
| 2806 | #line 708 "pikchr.y" |
| 2807 | {yylhsminor.yy187.x=yymsp[-4].minor.yy187.x-yymsp[-2].minor.yy129; yylhsminor.yy187.y=yymsp[-4].minor.yy187.y-yymsp[0].minor.yy129;} |
| 2808 | #line 2808 "pikchr.c" |
| 2809 | yymsp[-4].minor.yy187 = yylhsminor.yy187; |
| 2810 | break; |
| 2811 | case 67: /* position ::= place PLUS LP expr COMMA expr RP */ |
| 2812 | #line 710 "pikchr.y" |
| 2813 | {yylhsminor.yy187.x=yymsp[-6].minor.yy187.x+yymsp[-3].minor.yy129; yylhsminor.yy187.y=yymsp[-6].minor.yy187.y+yymsp[-1].minor.yy129;} |
| 2814 | #line 2814 "pikchr.c" |
| 2815 | yymsp[-6].minor.yy187 = yylhsminor.yy187; |
| 2816 | break; |
| 2817 | case 68: /* position ::= place MINUS LP expr COMMA expr RP */ |
| 2818 | #line 712 "pikchr.y" |
| 2819 | {yylhsminor.yy187.x=yymsp[-6].minor.yy187.x-yymsp[-3].minor.yy129; yylhsminor.yy187.y=yymsp[-6].minor.yy187.y-yymsp[-1].minor.yy129;} |
| 2820 | #line 2820 "pikchr.c" |
| 2821 | yymsp[-6].minor.yy187 = yylhsminor.yy187; |
| 2822 | break; |
| 2823 | case 69: /* position ::= LP position COMMA position RP */ |
| 2824 | #line 713 "pikchr.y" |
| 2825 | {yymsp[-4].minor.yy187.x=yymsp[-3].minor.yy187.x; yymsp[-4].minor.yy187.y=yymsp[-1].minor.yy187.y;} |
| 2826 | #line 2826 "pikchr.c" |
| 2827 | break; |
| 2828 | case 70: /* position ::= LP position RP */ |
| 2829 | #line 714 "pikchr.y" |
| 2830 | {yymsp[-2].minor.yy187=yymsp[-1].minor.yy187;} |
| 2831 | #line 2831 "pikchr.c" |
| 2832 | break; |
| 2833 | case 71: /* position ::= expr between position AND position */ |
| 2834 | #line 716 "pikchr.y" |
| 2835 | {yylhsminor.yy187 = pik_position_between(yymsp[-4].minor.yy129,yymsp[-2].minor.yy187,yymsp[0].minor.yy187);} |
| 2836 | #line 2836 "pikchr.c" |
| 2837 | yymsp[-4].minor.yy187 = yylhsminor.yy187; |
| 2838 | break; |
| 2839 | case 72: /* position ::= expr LT position COMMA position GT */ |
| 2840 | #line 718 "pikchr.y" |
| 2841 | {yylhsminor.yy187 = pik_position_between(yymsp[-5].minor.yy129,yymsp[-3].minor.yy187,yymsp[-1].minor.yy187);} |
| 2842 | #line 2842 "pikchr.c" |
| 2843 | yymsp[-5].minor.yy187 = yylhsminor.yy187; |
| 2844 | break; |
| 2845 | case 73: /* position ::= expr ABOVE position */ |
| 2846 | #line 719 "pikchr.y" |
| 2847 | {yylhsminor.yy187=yymsp[0].minor.yy187; yylhsminor.yy187.y += yymsp[-2].minor.yy129;} |
| 2848 | #line 2848 "pikchr.c" |
| 2849 | yymsp[-2].minor.yy187 = yylhsminor.yy187; |
| 2850 | break; |
| 2851 | case 74: /* position ::= expr BELOW position */ |
| 2852 | #line 720 "pikchr.y" |
| 2853 | {yylhsminor.yy187=yymsp[0].minor.yy187; yylhsminor.yy187.y -= yymsp[-2].minor.yy129;} |
| 2854 | #line 2854 "pikchr.c" |
| 2855 | yymsp[-2].minor.yy187 = yylhsminor.yy187; |
| 2856 | break; |
| 2857 | case 75: /* position ::= expr LEFT OF position */ |
| 2858 | #line 721 "pikchr.y" |
| 2859 | {yylhsminor.yy187=yymsp[0].minor.yy187; yylhsminor.yy187.x -= yymsp[-3].minor.yy129;} |
| 2860 | #line 2860 "pikchr.c" |
| 2861 | yymsp[-3].minor.yy187 = yylhsminor.yy187; |
| 2862 | break; |
| 2863 | case 76: /* position ::= expr RIGHT OF position */ |
| 2864 | #line 722 "pikchr.y" |
| 2865 | {yylhsminor.yy187=yymsp[0].minor.yy187; yylhsminor.yy187.x += yymsp[-3].minor.yy129;} |
| 2866 | #line 2866 "pikchr.c" |
| 2867 | yymsp[-3].minor.yy187 = yylhsminor.yy187; |
| 2868 | break; |
| 2869 | case 77: /* position ::= expr ON HEADING EDGEPT OF position */ |
| 2870 | #line 724 "pikchr.y" |
| 2871 | {yylhsminor.yy187 = pik_position_at_hdg(yymsp[-5].minor.yy129,&yymsp[-2].minor.yy0,yymsp[0].minor.yy187);} |
| 2872 | #line 2872 "pikchr.c" |
| 2873 | yymsp[-5].minor.yy187 = yylhsminor.yy187; |
| 2874 | break; |
| 2875 | case 78: /* position ::= expr HEADING EDGEPT OF position */ |
| 2876 | #line 726 "pikchr.y" |
| 2877 | {yylhsminor.yy187 = pik_position_at_hdg(yymsp[-4].minor.yy129,&yymsp[-2].minor.yy0,yymsp[0].minor.yy187);} |
| 2878 | #line 2878 "pikchr.c" |
| 2879 | yymsp[-4].minor.yy187 = yylhsminor.yy187; |
| 2880 | break; |
| 2881 | case 79: /* position ::= expr EDGEPT OF position */ |
| 2882 | #line 728 "pikchr.y" |
| 2883 | {yylhsminor.yy187 = pik_position_at_hdg(yymsp[-3].minor.yy129,&yymsp[-2].minor.yy0,yymsp[0].minor.yy187);} |
| 2884 | #line 2884 "pikchr.c" |
| 2885 | yymsp[-3].minor.yy187 = yylhsminor.yy187; |
| 2886 | break; |
| 2887 | case 80: /* position ::= expr ON HEADING expr FROM position */ |
| 2888 | #line 730 "pikchr.y" |
| 2889 | {yylhsminor.yy187 = pik_position_at_angle(yymsp[-5].minor.yy129,yymsp[-2].minor.yy129,yymsp[0].minor.yy187);} |
| 2890 | #line 2890 "pikchr.c" |
| 2891 | yymsp[-5].minor.yy187 = yylhsminor.yy187; |
| 2892 | break; |
| 2893 | case 81: /* position ::= expr HEADING expr FROM position */ |
| 2894 | #line 732 "pikchr.y" |
| 2895 | {yylhsminor.yy187 = pik_position_at_angle(yymsp[-4].minor.yy129,yymsp[-2].minor.yy129,yymsp[0].minor.yy187);} |
| 2896 | #line 2896 "pikchr.c" |
| 2897 | yymsp[-4].minor.yy187 = yylhsminor.yy187; |
| 2898 | break; |
| 2899 | case 82: /* place ::= edge OF object */ |
| 2900 | #line 744 "pikchr.y" |
| 2901 | {yylhsminor.yy187 = pik_place_of_elem(p,yymsp[0].minor.yy54,&yymsp[-2].minor.yy0);} |
| 2902 | #line 2902 "pikchr.c" |
| 2903 | yymsp[-2].minor.yy187 = yylhsminor.yy187; |
| 2904 | break; |
| 2905 | case 83: /* place2 ::= object */ |
| 2906 | #line 745 "pikchr.y" |
| 2907 | {yylhsminor.yy187 = pik_place_of_elem(p,yymsp[0].minor.yy54,0);} |
| 2908 | #line 2908 "pikchr.c" |
| 2909 | yymsp[0].minor.yy187 = yylhsminor.yy187; |
| 2910 | break; |
| 2911 | case 84: /* place2 ::= object DOT_E edge */ |
| 2912 | #line 746 "pikchr.y" |
| 2913 | {yylhsminor.yy187 = pik_place_of_elem(p,yymsp[-2].minor.yy54,&yymsp[0].minor.yy0);} |
| 2914 | #line 2914 "pikchr.c" |
| 2915 | yymsp[-2].minor.yy187 = yylhsminor.yy187; |
| 2916 | break; |
| 2917 | case 85: /* place2 ::= NTH VERTEX OF object */ |
| 2918 | #line 747 "pikchr.y" |
| 2919 | {yylhsminor.yy187 = pik_nth_vertex(p,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,yymsp[0].minor.yy54);} |
| 2920 | #line 2920 "pikchr.c" |
| 2921 | yymsp[-3].minor.yy187 = yylhsminor.yy187; |
| 2922 | break; |
| 2923 | case 86: /* object ::= nth */ |
| 2924 | #line 759 "pikchr.y" |
| 2925 | {yylhsminor.yy54 = pik_find_nth(p,0,&yymsp[0].minor.yy0);} |
| 2926 | #line 2926 "pikchr.c" |
| 2927 | yymsp[0].minor.yy54 = yylhsminor.yy54; |
| 2928 | break; |
| 2929 | case 87: /* object ::= nth OF|IN object */ |
| 2930 | #line 760 "pikchr.y" |
| 2931 | {yylhsminor.yy54 = pik_find_nth(p,yymsp[0].minor.yy54,&yymsp[-2].minor.yy0);} |
| 2932 | #line 2932 "pikchr.c" |
| 2933 | yymsp[-2].minor.yy54 = yylhsminor.yy54; |
| 2934 | break; |
| 2935 | case 88: /* objectname ::= THIS */ |
| 2936 | #line 762 "pikchr.y" |
| 2937 | {yymsp[0].minor.yy54 = p->cur;} |
| 2938 | #line 2938 "pikchr.c" |
| 2939 | break; |
| 2940 | case 89: /* objectname ::= PLACENAME */ |
| 2941 | #line 763 "pikchr.y" |
| 2942 | {yylhsminor.yy54 = pik_find_byname(p,0,&yymsp[0].minor.yy0);} |
| 2943 | #line 2943 "pikchr.c" |
| 2944 | yymsp[0].minor.yy54 = yylhsminor.yy54; |
| 2945 | break; |
| 2946 | case 90: /* objectname ::= objectname DOT_U PLACENAME */ |
| 2947 | #line 765 "pikchr.y" |
| 2948 | {yylhsminor.yy54 = pik_find_byname(p,yymsp[-2].minor.yy54,&yymsp[0].minor.yy0);} |
| 2949 | #line 2949 "pikchr.c" |
| 2950 | yymsp[-2].minor.yy54 = yylhsminor.yy54; |
| 2951 | break; |
| 2952 | case 91: /* nth ::= NTH CLASSNAME */ |
| 2953 | #line 767 "pikchr.y" |
| 2954 | {yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = pik_nth_value(p,&yymsp[-1].minor.yy0); } |
| 2955 | #line 2955 "pikchr.c" |
| 2956 | yymsp[-1].minor.yy0 = yylhsminor.yy0; |
| 2957 | break; |
| 2958 | case 92: /* nth ::= NTH LAST CLASSNAME */ |
| 2959 | #line 768 "pikchr.y" |
| 2960 | {yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -pik_nth_value(p,&yymsp[-2].minor.yy0); } |
| 2961 | #line 2961 "pikchr.c" |
| 2962 | yymsp[-2].minor.yy0 = yylhsminor.yy0; |
| 2963 | break; |
| 2964 | case 93: /* nth ::= LAST CLASSNAME */ |
| 2965 | #line 769 "pikchr.y" |
| 2966 | {yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.eCode = -1;} |
| 2967 | #line 2967 "pikchr.c" |
| 2968 | break; |
| 2969 | case 94: /* nth ::= LAST */ |
| 2970 | #line 770 "pikchr.y" |
| 2971 | {yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -1;} |
| 2972 | #line 2972 "pikchr.c" |
| 2973 | yymsp[0].minor.yy0 = yylhsminor.yy0; |
| 2974 | break; |
| 2975 | case 95: /* nth ::= NTH LB RB */ |
| 2976 | #line 771 "pikchr.y" |
| 2977 | {yylhsminor.yy0=yymsp[-1].minor.yy0; yylhsminor.yy0.eCode = pik_nth_value(p,&yymsp[-2].minor.yy0);} |
| 2978 | #line 2978 "pikchr.c" |
| 2979 | yymsp[-2].minor.yy0 = yylhsminor.yy0; |
| 2980 | break; |
| 2981 | case 96: /* nth ::= NTH LAST LB RB */ |
| 2982 | #line 772 "pikchr.y" |
| 2983 | {yylhsminor.yy0=yymsp[-1].minor.yy0; yylhsminor.yy0.eCode = -pik_nth_value(p,&yymsp[-3].minor.yy0);} |
| 2984 | #line 2984 "pikchr.c" |
| 2985 | yymsp[-3].minor.yy0 = yylhsminor.yy0; |
| 2986 | break; |
| 2987 | case 97: /* nth ::= LAST LB RB */ |
| 2988 | #line 773 "pikchr.y" |
| 2989 | {yymsp[-2].minor.yy0=yymsp[-1].minor.yy0; yymsp[-2].minor.yy0.eCode = -1; } |
| 2990 | #line 2990 "pikchr.c" |
| 2991 | break; |
| 2992 | case 98: /* expr ::= expr PLUS expr */ |
| 2993 | #line 775 "pikchr.y" |
| 2994 | {yylhsminor.yy129=yymsp[-2].minor.yy129+yymsp[0].minor.yy129;} |
| 2995 | #line 2995 "pikchr.c" |
| 2996 | yymsp[-2].minor.yy129 = yylhsminor.yy129; |
| 2997 | break; |
| 2998 | case 99: /* expr ::= expr MINUS expr */ |
| 2999 | #line 776 "pikchr.y" |
| 3000 | {yylhsminor.yy129=yymsp[-2].minor.yy129-yymsp[0].minor.yy129;} |
| 3001 | #line 3001 "pikchr.c" |
| 3002 | yymsp[-2].minor.yy129 = yylhsminor.yy129; |
| 3003 | break; |
| 3004 | case 100: /* expr ::= expr STAR expr */ |
| 3005 | #line 777 "pikchr.y" |
| 3006 | {yylhsminor.yy129=yymsp[-2].minor.yy129*yymsp[0].minor.yy129;} |
| 3007 | #line 3007 "pikchr.c" |
| 3008 | yymsp[-2].minor.yy129 = yylhsminor.yy129; |
| 3009 | break; |
| 3010 | case 101: /* expr ::= expr SLASH expr */ |
| 3011 | #line 778 "pikchr.y" |
| 3012 | { |
| 3013 | if( yymsp[0].minor.yy129==0.0 ){ pik_error(p, &yymsp[-1].minor.yy0, "division by zero"); yylhsminor.yy129 = 0.0; } |
| 3014 | else{ yylhsminor.yy129 = yymsp[-2].minor.yy129/yymsp[0].minor.yy129; } |
| 3015 | } |
| 3016 | #line 3016 "pikchr.c" |
| 3017 | yymsp[-2].minor.yy129 = yylhsminor.yy129; |
| 3018 | break; |
| 3019 | case 102: /* expr ::= MINUS expr */ |
| 3020 | #line 782 "pikchr.y" |
| 3021 | {yymsp[-1].minor.yy129=-yymsp[0].minor.yy129;} |
| 3022 | #line 3022 "pikchr.c" |
| 3023 | break; |
| 3024 | case 103: /* expr ::= PLUS expr */ |
| 3025 | #line 783 "pikchr.y" |
| 3026 | {yymsp[-1].minor.yy129=yymsp[0].minor.yy129;} |
| 3027 | #line 3027 "pikchr.c" |
| 3028 | break; |
| 3029 | case 104: /* expr ::= LP expr RP */ |
| 3030 | #line 784 "pikchr.y" |
| 3031 | {yymsp[-2].minor.yy129=yymsp[-1].minor.yy129;} |
| 3032 | #line 3032 "pikchr.c" |
| 3033 | break; |
| 3034 | case 105: /* expr ::= LP FILL|COLOR|THICKNESS RP */ |
| 3035 | #line 785 "pikchr.y" |
| 3036 | {yymsp[-2].minor.yy129=pik_get_var(p,&yymsp[-1].minor.yy0);} |
| 3037 | #line 3037 "pikchr.c" |
| 3038 | break; |
| 3039 | case 106: /* expr ::= NUMBER */ |
| 3040 | #line 786 "pikchr.y" |
| 3041 | {yylhsminor.yy129=pik_atof(&yymsp[0].minor.yy0);} |
| 3042 | #line 3042 "pikchr.c" |
| 3043 | yymsp[0].minor.yy129 = yylhsminor.yy129; |
| 3044 | break; |
| 3045 | case 107: /* expr ::= ID */ |
| 3046 | #line 787 "pikchr.y" |
| 3047 | {yylhsminor.yy129=pik_get_var(p,&yymsp[0].minor.yy0);} |
| 3048 | #line 3048 "pikchr.c" |
| 3049 | yymsp[0].minor.yy129 = yylhsminor.yy129; |
| 3050 | break; |
| 3051 | case 108: /* expr ::= FUNC1 LP expr RP */ |
| 3052 | #line 788 "pikchr.y" |
| 3053 | {yylhsminor.yy129 = pik_func(p,&yymsp[-3].minor.yy0,yymsp[-1].minor.yy129,0.0);} |
| 3054 | #line 3054 "pikchr.c" |
| 3055 | yymsp[-3].minor.yy129 = yylhsminor.yy129; |
| 3056 | break; |
| 3057 | case 109: /* expr ::= FUNC2 LP expr COMMA expr RP */ |
| 3058 | #line 789 "pikchr.y" |
| 3059 | {yylhsminor.yy129 = pik_func(p,&yymsp[-5].minor.yy0,yymsp[-3].minor.yy129,yymsp[-1].minor.yy129);} |
| 3060 | #line 3060 "pikchr.c" |
| 3061 | yymsp[-5].minor.yy129 = yylhsminor.yy129; |
| 3062 | break; |
| 3063 | case 110: /* expr ::= DIST LP position COMMA position RP */ |
| 3064 | #line 790 "pikchr.y" |
| 3065 | {yymsp[-5].minor.yy129 = pik_dist(&yymsp[-3].minor.yy187,&yymsp[-1].minor.yy187);} |
| 3066 | #line 3066 "pikchr.c" |
| 3067 | break; |
| 3068 | case 111: /* expr ::= place2 DOT_XY X */ |
| 3069 | #line 791 "pikchr.y" |
| 3070 | {yylhsminor.yy129 = yymsp[-2].minor.yy187.x;} |
| 3071 | #line 3071 "pikchr.c" |
| 3072 | yymsp[-2].minor.yy129 = yylhsminor.yy129; |
| 3073 | break; |
| 3074 | case 112: /* expr ::= place2 DOT_XY Y */ |
| 3075 | #line 792 "pikchr.y" |
| 3076 | {yylhsminor.yy129 = yymsp[-2].minor.yy187.y;} |
| 3077 | #line 3077 "pikchr.c" |
| 3078 | yymsp[-2].minor.yy129 = yylhsminor.yy129; |
| 3079 | break; |
| 3080 | case 113: /* expr ::= object DOT_L numproperty */ |
| 3081 | case 114: /* expr ::= object DOT_L dashproperty */ yytestcase(yyruleno==114); |
| 3082 | case 115: /* expr ::= object DOT_L colorproperty */ yytestcase(yyruleno==115); |
| 3083 | #line 793 "pikchr.y" |
| 3084 | {yylhsminor.yy129=pik_property_of(yymsp[-2].minor.yy54,&yymsp[0].minor.yy0);} |
| 3085 | #line 3085 "pikchr.c" |
| 3086 | yymsp[-2].minor.yy129 = yylhsminor.yy129; |
| 3087 | break; |
| 3088 | default: |
| 3089 | /* (116) lvalue ::= ID */ yytestcase(yyruleno==116); |
| 3090 | /* (117) lvalue ::= FILL */ yytestcase(yyruleno==117); |
| 3091 | /* (118) lvalue ::= COLOR */ yytestcase(yyruleno==118); |
| @@ -3130,19 +3183,19 @@ | |
| 3183 | ){ |
| 3184 | pik_parserARG_FETCH |
| 3185 | pik_parserCTX_FETCH |
| 3186 | #define TOKEN yyminor |
| 3187 | /************ Begin %syntax_error code ****************************************/ |
| 3188 | #line 551 "pikchr.y" |
| 3189 | |
| 3190 | if( TOKEN.z && TOKEN.z[0] ){ |
| 3191 | pik_error(p, &TOKEN, "syntax error"); |
| 3192 | }else{ |
| 3193 | pik_error(p, 0, "syntax error"); |
| 3194 | } |
| 3195 | UNUSED_PARAMETER(yymajor); |
| 3196 | #line 3196 "pikchr.c" |
| 3197 | /************ End %syntax_error code ******************************************/ |
| 3198 | pik_parserARG_STORE /* Suppress warning about unused %extra_argument variable */ |
| 3199 | pik_parserCTX_STORE |
| 3200 | } |
| 3201 | |
| @@ -3407,11 +3460,11 @@ | |
| 3460 | #else |
| 3461 | (void)iToken; |
| 3462 | return 0; |
| 3463 | #endif |
| 3464 | } |
| 3465 | #line 798 "pikchr.y" |
| 3466 | |
| 3467 | |
| 3468 | |
| 3469 | /* Chart of the 148 official CSS color names with their |
| 3470 | ** corresponding RGB values thru Color Module Level 4: |
| @@ -3634,42 +3687,57 @@ | |
| 3687 | ** than true arcs. Multiple reasons: (1) the legacy-PIC parameters |
| 3688 | ** that control arcs are obscure and I could not figure out what they |
| 3689 | ** mean based on available documentation. (2) Arcs are rarely used, |
| 3690 | ** and so do not seem that important. |
| 3691 | */ |
| 3692 | static PPoint arcControlPoint(int cw, PPoint f, PPoint t){ |
| 3693 | PPoint m; |
| 3694 | PNum dx, dy; |
| 3695 | m.x = 0.5*(f.x+t.x); |
| 3696 | m.y = 0.5*(f.y+t.y); |
| 3697 | dx = t.x - f.x; |
| 3698 | dy = t.y - f.y; |
| 3699 | if( cw ){ |
| 3700 | m.x -= 0.5*dy; |
| 3701 | m.y += 0.5*dx; |
| 3702 | }else{ |
| 3703 | m.x += 0.5*dy; |
| 3704 | m.y -= 0.5*dx; |
| 3705 | } |
| 3706 | return m; |
| 3707 | } |
| 3708 | static void arcCheck(Pik *p, PObj *pObj){ |
| 3709 | PPoint f, m, t; |
| 3710 | PNum sw; |
| 3711 | int i; |
| 3712 | if( p->nTPath>2 ){ |
| 3713 | pik_error(p, &pObj->errTok, "arc geometry error"); |
| 3714 | return; |
| 3715 | } |
| 3716 | f = p->aTPath[0]; |
| 3717 | t = p->aTPath[1]; |
| 3718 | m = arcControlPoint(pObj->cw, f, t); |
| 3719 | sw = pObj->sw; |
| 3720 | for(i=1; i<16; i++){ |
| 3721 | PNum t1, t2, a, b, c, x, y; |
| 3722 | t1 = 0.0625*i; |
| 3723 | t2 = 1.0 - t1; |
| 3724 | a = t2*t2; |
| 3725 | b = 2*t1*t2; |
| 3726 | c = t1*t1; |
| 3727 | x = a*f.x + b*m.x + c*t.x; |
| 3728 | y = a*f.y + b*m.y + c*t.y; |
| 3729 | pik_bbox_addellipse(&pObj->bbox, x, y, sw, sw); |
| 3730 | } |
| 3731 | } |
| 3732 | static void arcRender(Pik *p, PObj *pObj){ |
| 3733 | PPoint f, m, t; |
| 3734 | if( pObj->nPath<2 ) return; |
| 3735 | if( pObj->sw<0.0 ) return; |
| 3736 | f = pObj->aPath[0]; |
| 3737 | t = pObj->aPath[1]; |
| 3738 | m = arcControlPoint(pObj->cw,f,t); |
| 3739 | if( pObj->larrow ){ |
| 3740 | pik_draw_arrowhead(p,&m,&f,pObj); |
| 3741 | } |
| 3742 | if( pObj->rarrow ){ |
| 3743 | pik_draw_arrowhead(p,&m,&t,pObj); |
| @@ -4338,11 +4406,11 @@ | |
| 4406 | static PPoint textOffset(Pik *p, PObj *pObj, int cp){ |
| 4407 | /* Automatically slim-down the width and height of text |
| 4408 | ** statements so that the bounding box tightly encloses the text, |
| 4409 | ** then get boxOffset() to do the offset computation. |
| 4410 | */ |
| 4411 | pik_size_to_fit(p, pObj, &pObj->errTok,3); |
| 4412 | return boxOffset(p, pObj, cp); |
| 4413 | } |
| 4414 | static void textRender(Pik *p, PObj *pObj){ |
| 4415 | pik_append_txt(p, pObj, 0); |
| 4416 | } |
| @@ -6347,16 +6415,15 @@ | |
| 6415 | ** |
| 6416 | ** 1: Fit horizontally only |
| 6417 | ** 2: Fit vertically only |
| 6418 | ** 3: Fit both ways |
| 6419 | */ |
| 6420 | static void pik_size_to_fit(Pik *p, PObj *pObj, PToken *pFit, int eWhich){ |
| 6421 | PNum w, h; |
| 6422 | PBox bbox; |
| 6423 | if( p->nErr ) return; |
| 6424 | if( pObj==0 ) pObj = p->cur; |
| 6425 | |
| 6426 | if( pObj->nTxt==0 ){ |
| 6427 | pik_error(0, pFit, "no text to fit to"); |
| 6428 | return; |
| 6429 | } |
| @@ -6495,13 +6562,13 @@ | |
| 6562 | unsigned int i; |
| 6563 | mid = (first+last)/2; |
| 6564 | zClr = aColor[mid].zName; |
| 6565 | for(i=0; i<pId->n; i++){ |
| 6566 | c1 = zClr[i]&0x7f; |
| 6567 | if( IsUpper(c1) ) c1 = ToLower(c1); |
| 6568 | c2 = pId->z[i]&0x7f; |
| 6569 | if( IsUpper(c2) ) c2 = ToLower(c2); |
| 6570 | c = c2 - c1; |
| 6571 | if( c ) break; |
| 6572 | } |
| 6573 | if( c==0 && aColor[mid].zName[pId->n] ) c = -1; |
| 6574 | if( c==0 ) return (double)aColor[mid].val; |
| @@ -6896,20 +6963,20 @@ | |
| 6963 | */ |
| 6964 | if( pObj->h<=0.0 ){ |
| 6965 | if( pObj->nTxt==0 ){ |
| 6966 | pObj->h = 0.0; |
| 6967 | }else if( pObj->w<=0.0 ){ |
| 6968 | pik_size_to_fit(p, pObj, &pObj->errTok, 3); |
| 6969 | }else{ |
| 6970 | pik_size_to_fit(p, pObj, &pObj->errTok, 2); |
| 6971 | } |
| 6972 | } |
| 6973 | if( pObj->w<=0.0 ){ |
| 6974 | if( pObj->nTxt==0 ){ |
| 6975 | pObj->w = 0.0; |
| 6976 | }else{ |
| 6977 | pik_size_to_fit(p, pObj, &pObj->errTok, 1); |
| 6978 | } |
| 6979 | } |
| 6980 | ofst = pik_elem_offset(p, pObj, pObj->eWith); |
| 6981 | dx = (pObj->with.x - ofst.x) - pObj->ptAt.x; |
| 6982 | dy = (pObj->with.y - ofst.y) - pObj->ptAt.y; |
| @@ -7235,11 +7302,12 @@ | |
| 7302 | pik_append_num(p, " width=\"", p->wSVG); |
| 7303 | pik_append_num(p, "\" height=\"", p->hSVG); |
| 7304 | pik_append(p, "\"", 1); |
| 7305 | } |
| 7306 | pik_append_dis(p, " viewBox=\"0 0 ",w,""); |
| 7307 | pik_append_dis(p, " ",h,"\""); |
| 7308 | pik_append(p, " data-pikchr-date=\"" MANIFEST_ISODATE "\">\n", -1); |
| 7309 | pik_elist_render(p, pList); |
| 7310 | pik_append(p,"</svg>\n", -1); |
| 7311 | }else{ |
| 7312 | p->wSVG = -1; |
| 7313 | p->hSVG = -1; |
| @@ -7319,10 +7387,11 @@ | |
| 7387 | { "n", 1, T_EDGEPT, 0, CP_N }, |
| 7388 | { "ne", 2, T_EDGEPT, 0, CP_NE }, |
| 7389 | { "north", 5, T_EDGEPT, 0, CP_N }, |
| 7390 | { "nw", 2, T_EDGEPT, 0, CP_NW }, |
| 7391 | { "of", 2, T_OF, 0, 0 }, |
| 7392 | { "pikchr_date",11, T_ISODATE, 0, 0, }, |
| 7393 | { "previous", 8, T_LAST, 0, 0, }, |
| 7394 | { "print", 5, T_PRINT, 0, 0 }, |
| 7395 | { "rad", 3, T_RADIUS, 0, 0 }, |
| 7396 | { "radius", 6, T_RADIUS, 0, 0 }, |
| 7397 | { "right", 5, T_RIGHT, DIR_RIGHT, CP_E }, |
| @@ -7605,11 +7674,11 @@ | |
| 7674 | } |
| 7675 | default: { |
| 7676 | c = z[0]; |
| 7677 | if( c=='.' ){ |
| 7678 | unsigned char c1 = z[1]; |
| 7679 | if( IsLower(c1) ){ |
| 7680 | const PikWord *pFound; |
| 7681 | for(i=2; (c = z[i])>='a' && c<='z'; i++){} |
| 7682 | pFound = pik_find_word((const char*)z+1, i-1, |
| 7683 | pik_keywords, count(pik_keywords)); |
| 7684 | if( pFound && (pFound->eEdge>0 || |
| @@ -7625,15 +7694,15 @@ | |
| 7694 | }else{ |
| 7695 | /* Any other "dot" */ |
| 7696 | pToken->eType = T_DOT_L; |
| 7697 | } |
| 7698 | return 1; |
| 7699 | }else if( IsDigit(c1) ){ |
| 7700 | i = 0; |
| 7701 | /* no-op. Fall through to number handling */ |
| 7702 | }else if( IsUpper(c1) ){ |
| 7703 | for(i=2; (c = z[i])!=0 && (IsAlnum(c) || c=='_'); i++){} |
| 7704 | pToken->eType = T_DOT_U; |
| 7705 | return 1; |
| 7706 | }else{ |
| 7707 | pToken->eType = T_ERROR; |
| 7708 | return 1; |
| @@ -7644,11 +7713,11 @@ | |
| 7713 | int isInt = 1; |
| 7714 | if( c!='.' ){ |
| 7715 | nDigit = 1; |
| 7716 | for(i=1; (c = z[i])>='0' && c<='9'; i++){ nDigit++; } |
| 7717 | if( i==1 && (c=='x' || c=='X') ){ |
| 7718 | for(i=2; (c = z[i])!=0 && IsXDigit(c); i++){} |
| 7719 | pToken->eType = T_NUMBER; |
| 7720 | return i; |
| 7721 | } |
| 7722 | }else{ |
| 7723 | isInt = 0; |
| @@ -7700,13 +7769,13 @@ | |
| 7769 | ){ |
| 7770 | i += 2; |
| 7771 | } |
| 7772 | pToken->eType = T_NUMBER; |
| 7773 | return i; |
| 7774 | }else if( IsLower(c) ){ |
| 7775 | const PikWord *pFound; |
| 7776 | for(i=1; (c = z[i])!=0 && (IsAlnum(c) || c=='_'); i++){} |
| 7777 | pFound = pik_find_word((const char*)z, i, |
| 7778 | pik_keywords, count(pik_keywords)); |
| 7779 | if( pFound ){ |
| 7780 | pToken->eType = pFound->eType; |
| 7781 | pToken->eCode = pFound->eCode; |
| @@ -7719,19 +7788,19 @@ | |
| 7788 | }else{ |
| 7789 | pToken->eType = T_ID; |
| 7790 | } |
| 7791 | return i; |
| 7792 | }else if( c>='A' && c<='Z' ){ |
| 7793 | for(i=1; (c = z[i])!=0 && (IsAlnum(c) || c=='_'); i++){} |
| 7794 | pToken->eType = T_PLACENAME; |
| 7795 | return i; |
| 7796 | }else if( c=='$' && z[1]>='1' && z[1]<='9' && !IsDigit(z[2]) ){ |
| 7797 | pToken->eType = T_PARAMETER; |
| 7798 | pToken->eCode = z[1] - '1'; |
| 7799 | return 2; |
| 7800 | }else if( c=='_' || c=='$' || c=='@' ){ |
| 7801 | for(i=1; (c = z[i])!=0 && (IsAlnum(c) || c=='_'); i++){} |
| 7802 | pToken->eType = T_ID; |
| 7803 | return i; |
| 7804 | }else{ |
| 7805 | pToken->eType = T_ERROR; |
| 7806 | return 1; |
| @@ -7814,12 +7883,12 @@ | |
| 7883 | /* Remove leading and trailing whitespace from each argument. |
| 7884 | ** If what remains is one of $1, $2, ... $9 then transfer the |
| 7885 | ** corresponding argument from the outer context */ |
| 7886 | for(j=0; j<=nArg; j++){ |
| 7887 | PToken *t = &args[j]; |
| 7888 | while( t->n>0 && IsSpace(t->z[0]) ){ t->n--; t->z++; } |
| 7889 | while( t->n>0 && IsSpace(t->z[t->n-1]) ){ t->n--; } |
| 7890 | if( t->n==2 && t->z[0]=='$' && t->z[1]>='1' && t->z[1]<='9' ){ |
| 7891 | if( pOuter ) *t = pOuter[t->z[1]-'1']; |
| 7892 | else t->n = 0; |
| 7893 | } |
| 7894 | } |
| @@ -7896,21 +7965,36 @@ | |
| 7965 | pMac->inUse = 0; |
| 7966 | }else{ |
| 7967 | #if 0 |
| 7968 | printf("******** Token %s (%d): \"%.*s\" **************\n", |
| 7969 | yyTokenName[token.eType], token.eType, |
| 7970 | (int)(IsSpace(token.z[0]) ? 0 : sz), token.z); |
| 7971 | #endif |
| 7972 | token.n = (unsigned short)(sz & 0xffff); |
| 7973 | if( p->nToken++ > PIKCHR_TOKEN_LIMIT ){ |
| 7974 | pik_error(p, &token, "script is too complex"); |
| 7975 | break; |
| 7976 | } |
| 7977 | if( token.eType==T_ISODATE ){ |
| 7978 | token.z = "\"" MANIFEST_ISODATE "\""; |
| 7979 | token.n = sizeof(MANIFEST_ISODATE)+1; |
| 7980 | token.eType = T_STRING; |
| 7981 | } |
| 7982 | pik_parser(pParser, token.eType, token); |
| 7983 | } |
| 7984 | } |
| 7985 | } |
| 7986 | |
| 7987 | /* |
| 7988 | ** Return the version name. |
| 7989 | */ |
| 7990 | const char *pikchr_version(void) |
| 7991 | /* Emscripten workaround, else it chokes on the inlined version */; |
| 7992 | |
| 7993 | const char *pikchr_version(void){ |
| 7994 | return RELEASE_VERSION " " MANIFEST_ISODATE; |
| 7995 | } |
| 7996 | |
| 7997 | /* |
| 7998 | ** Parse the PIKCHR script contained in zText[]. Return a rendering. Or |
| 7999 | ** if an error is encountered, return the error text. The error message |
| 8000 | ** is HTML formatted. So regardless of what happens, the return text |
| @@ -8130,10 +8214,14 @@ | |
| 8214 | exit(1); |
| 8215 | } |
| 8216 | bSvgOnly = 1; |
| 8217 | mFlags |= PIKCHR_PLAINTEXT_ERRORS; |
| 8218 | }else |
| 8219 | if( strcmp(z,"version")==0 || strcmp(z,"v")==0 ){ |
| 8220 | printf("pikchr %s\n", pikchr_version()); |
| 8221 | return 0; |
| 8222 | }else |
| 8223 | { |
| 8224 | fprintf(stderr,"unknown option: \"%s\"\n", argv[i]); |
| 8225 | usage(argv[0]); |
| 8226 | } |
| 8227 | continue; |
| @@ -8242,6 +8330,6 @@ | |
| 8330 | |
| 8331 | |
| 8332 | #endif /* PIKCHR_TCL */ |
| 8333 | |
| 8334 | |
| 8335 | #line 8335 "pikchr.c" |
| 8336 |
+770
-596
| --- extsrc/pikchr.js | ||
| +++ extsrc/pikchr.js | ||
| @@ -1,713 +1,887 @@ | ||
| 1 | 1 | |
| 2 | 2 | var initPikchrModule = (() => { |
| 3 | - var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined; | |
| 3 | + var _scriptName = typeof document != 'undefined' ? document.currentScript?.src : undefined; | |
| 4 | 4 | |
| 5 | 5 | return ( |
| 6 | -function(config) { | |
| 7 | - var initPikchrModule = config || {}; | |
| 6 | +function(moduleArg = {}) { | |
| 7 | + var moduleRtn; | |
| 8 | 8 | |
| 9 | -var Module = typeof initPikchrModule != "undefined" ? initPikchrModule : {}; | |
| 9 | +// include: shell.js | |
| 10 | +// The Module object: Our interface to the outside world. We import | |
| 11 | +// and export values on it. There are various ways Module can be used: | |
| 12 | +// 1. Not defined. We create it here | |
| 13 | +// 2. A function parameter, function(moduleArg) => Promise<Module> | |
| 14 | +// 3. pre-run appended it, var Module = {}; ..generated code.. | |
| 15 | +// 4. External script tag defines var Module. | |
| 16 | +// We need to check if Module already exists (e.g. case 3 above). | |
| 17 | +// Substitution will be replaced with actual code on later stage of the build, | |
| 18 | +// this way Closure Compiler will not mangle it (e.g. case 4. above). | |
| 19 | +// Note that if you want to run closure, and also to use Module | |
| 20 | +// after the generated code, you will need to define var Module = {}; | |
| 21 | +// before the code. Then that object will be used in the code, and you | |
| 22 | +// can continue to use Module afterwards as well. | |
| 23 | +var Module = moduleArg; | |
| 10 | 24 | |
| 25 | +// Set up the promise that indicates the Module is initialized | |
| 11 | 26 | var readyPromiseResolve, readyPromiseReject; |
| 12 | 27 | |
| 13 | -Module["ready"] = new Promise(function(resolve, reject) { | |
| 14 | - readyPromiseResolve = resolve; | |
| 15 | - readyPromiseReject = reject; | |
| 28 | +var readyPromise = new Promise((resolve, reject) => { | |
| 29 | + readyPromiseResolve = resolve; | |
| 30 | + readyPromiseReject = reject; | |
| 16 | 31 | }); |
| 17 | 32 | |
| 33 | +// Determine the runtime environment we are in. You can customize this by | |
| 34 | +// setting the ENVIRONMENT setting at compile time (see settings.js). | |
| 35 | +var ENVIRONMENT_IS_WEB = true; | |
| 36 | + | |
| 37 | +var ENVIRONMENT_IS_WORKER = false; | |
| 38 | + | |
| 39 | +// --pre-jses are emitted after the Module integration code, so that they can | |
| 40 | +// refer to Module (if they choose; they can also define Module) | |
| 41 | +// Sometimes an existing Module object exists with properties | |
| 42 | +// meant to overwrite the default module functionality. Here | |
| 43 | +// we collect those properties and reapply _after_ we configure | |
| 44 | +// the current environment's defaults to avoid having to be so | |
| 45 | +// defensive during initialization. | |
| 18 | 46 | var moduleOverrides = Object.assign({}, Module); |
| 19 | 47 | |
| 20 | 48 | var arguments_ = []; |
| 21 | 49 | |
| 22 | 50 | var thisProgram = "./this.program"; |
| 23 | 51 | |
| 24 | 52 | var quit_ = (status, toThrow) => { |
| 25 | - throw toThrow; | |
| 53 | + throw toThrow; | |
| 26 | 54 | }; |
| 27 | 55 | |
| 28 | -var ENVIRONMENT_IS_WEB = true; | |
| 29 | - | |
| 30 | -var ENVIRONMENT_IS_WORKER = false; | |
| 31 | - | |
| 56 | +// `/` should be present at the end if `scriptDirectory` is not empty | |
| 32 | 57 | var scriptDirectory = ""; |
| 33 | 58 | |
| 34 | 59 | function locateFile(path) { |
| 35 | - if (Module["locateFile"]) { | |
| 36 | - return Module["locateFile"](path, scriptDirectory); | |
| 37 | - } | |
| 38 | - return scriptDirectory + path; | |
| 39 | -} | |
| 40 | - | |
| 41 | -var read_, readAsync, readBinary, setWindowTitle; | |
| 42 | - | |
| 43 | -if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { | |
| 44 | - if (ENVIRONMENT_IS_WORKER) { | |
| 45 | - scriptDirectory = self.location.href; | |
| 46 | - } else if (typeof document != "undefined" && document.currentScript) { | |
| 47 | - scriptDirectory = document.currentScript.src; | |
| 48 | - } | |
| 49 | - if (_scriptDir) { | |
| 50 | - scriptDirectory = _scriptDir; | |
| 51 | - } | |
| 52 | - if (scriptDirectory.indexOf("blob:") !== 0) { | |
| 53 | - scriptDirectory = scriptDirectory.substr(0, scriptDirectory.replace(/[?#].*/, "").lastIndexOf("/") + 1); | |
| 54 | - } else { | |
| 55 | - scriptDirectory = ""; | |
| 56 | - } | |
| 57 | - { | |
| 58 | - read_ = url => { | |
| 59 | - var xhr = new XMLHttpRequest(); | |
| 60 | - xhr.open("GET", url, false); | |
| 61 | - xhr.send(null); | |
| 62 | - return xhr.responseText; | |
| 63 | - }; | |
| 64 | - if (ENVIRONMENT_IS_WORKER) { | |
| 65 | - readBinary = url => { | |
| 66 | - var xhr = new XMLHttpRequest(); | |
| 67 | - xhr.open("GET", url, false); | |
| 68 | - xhr.responseType = "arraybuffer"; | |
| 69 | - xhr.send(null); | |
| 70 | - return new Uint8Array(xhr.response); | |
| 71 | - }; | |
| 72 | - } | |
| 73 | - readAsync = (url, onload, onerror) => { | |
| 74 | - var xhr = new XMLHttpRequest(); | |
| 75 | - xhr.open("GET", url, true); | |
| 76 | - xhr.responseType = "arraybuffer"; | |
| 77 | - xhr.onload = () => { | |
| 78 | - if (xhr.status == 200 || xhr.status == 0 && xhr.response) { | |
| 79 | - onload(xhr.response); | |
| 80 | - return; | |
| 81 | - } | |
| 82 | - onerror(); | |
| 83 | - }; | |
| 84 | - xhr.onerror = onerror; | |
| 85 | - xhr.send(null); | |
| 86 | - }; | |
| 87 | - } | |
| 88 | - setWindowTitle = title => document.title = title; | |
| 89 | -} else {} | |
| 60 | + if (Module["locateFile"]) { | |
| 61 | + return Module["locateFile"](path, scriptDirectory); | |
| 62 | + } | |
| 63 | + return scriptDirectory + path; | |
| 64 | +} | |
| 65 | + | |
| 66 | +// Hooks that are implemented differently in different runtime environments. | |
| 67 | +var readAsync, readBinary; | |
| 68 | + | |
| 69 | +// Note that this includes Node.js workers when relevant (pthreads is enabled). | |
| 70 | +// Node.js workers are detected as a combination of ENVIRONMENT_IS_WORKER and | |
| 71 | +// ENVIRONMENT_IS_NODE. | |
| 72 | +if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { | |
| 73 | + if (ENVIRONMENT_IS_WORKER) { | |
| 74 | + // Check worker, not web, since window could be polyfilled | |
| 75 | + scriptDirectory = self.location.href; | |
| 76 | + } else if (typeof document != "undefined" && document.currentScript) { | |
| 77 | + // web | |
| 78 | + scriptDirectory = document.currentScript.src; | |
| 79 | + } | |
| 80 | + // When MODULARIZE, this JS may be executed later, after document.currentScript | |
| 81 | + // is gone, so we saved it, and we use it here instead of any other info. | |
| 82 | + if (_scriptName) { | |
| 83 | + scriptDirectory = _scriptName; | |
| 84 | + } | |
| 85 | + // blob urls look like blob:http://site.com/etc/etc and we cannot infer anything from them. | |
| 86 | + // otherwise, slice off the final part of the url to find the script directory. | |
| 87 | + // if scriptDirectory does not contain a slash, lastIndexOf will return -1, | |
| 88 | + // and scriptDirectory will correctly be replaced with an empty string. | |
| 89 | + // If scriptDirectory contains a query (starting with ?) or a fragment (starting with #), | |
| 90 | + // they are removed because they could contain a slash. | |
| 91 | + if (scriptDirectory.startsWith("blob:")) { | |
| 92 | + scriptDirectory = ""; | |
| 93 | + } else { | |
| 94 | + scriptDirectory = scriptDirectory.substr(0, scriptDirectory.replace(/[?#].*/, "").lastIndexOf("/") + 1); | |
| 95 | + } | |
| 96 | + { | |
| 97 | + // include: web_or_worker_shell_read.js | |
| 98 | + readAsync = url => fetch(url, { | |
| 99 | + credentials: "same-origin" | |
| 100 | + }).then(response => { | |
| 101 | + if (response.ok) { | |
| 102 | + return response.arrayBuffer(); | |
| 103 | + } | |
| 104 | + return Promise.reject(new Error(response.status + " : " + response.url)); | |
| 105 | + }); | |
| 106 | + } | |
| 107 | +} else // end include: web_or_worker_shell_read.js | |
| 108 | +{} | |
| 90 | 109 | |
| 91 | 110 | var out = Module["print"] || console.log.bind(console); |
| 92 | 111 | |
| 93 | -var err = Module["printErr"] || console.warn.bind(console); | |
| 112 | +var err = Module["printErr"] || console.error.bind(console); | |
| 94 | 113 | |
| 114 | +// Merge back in the overrides | |
| 95 | 115 | Object.assign(Module, moduleOverrides); |
| 96 | 116 | |
| 117 | +// Free the object hierarchy contained in the overrides, this lets the GC | |
| 118 | +// reclaim data used. | |
| 97 | 119 | moduleOverrides = null; |
| 98 | 120 | |
| 121 | +// Emit code to handle expected values on the Module object. This applies Module.x | |
| 122 | +// to the proper local x. This has two benefits: first, we only emit it if it is | |
| 123 | +// expected to arrive, and second, by using a local everywhere else that can be | |
| 124 | +// minified. | |
| 99 | 125 | if (Module["arguments"]) arguments_ = Module["arguments"]; |
| 100 | 126 | |
| 101 | 127 | if (Module["thisProgram"]) thisProgram = Module["thisProgram"]; |
| 102 | 128 | |
| 103 | -if (Module["quit"]) quit_ = Module["quit"]; | |
| 104 | - | |
| 105 | -var wasmBinary; | |
| 106 | - | |
| 107 | -if (Module["wasmBinary"]) wasmBinary = Module["wasmBinary"]; | |
| 108 | - | |
| 109 | -var noExitRuntime = Module["noExitRuntime"] || true; | |
| 110 | - | |
| 111 | -if (typeof WebAssembly != "object") { | |
| 112 | - abort("no native wasm support detected"); | |
| 113 | -} | |
| 114 | - | |
| 129 | +// perform assertions in shell.js after we set up out() and err(), as otherwise if an assertion fails it cannot print the message | |
| 130 | +// end include: shell.js | |
| 131 | +// include: preamble.js | |
| 132 | +// === Preamble library stuff === | |
| 133 | +// Documentation for the public APIs defined in this file must be updated in: | |
| 134 | +// site/source/docs/api_reference/preamble.js.rst | |
| 135 | +// A prebuilt local version of the documentation is available at: | |
| 136 | +// site/build/text/docs/api_reference/preamble.js.txt | |
| 137 | +// You can also build docs locally as HTML or other formats in site/ | |
| 138 | +// An online HTML version (which may be of a different version of Emscripten) | |
| 139 | +// is up at http://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html | |
| 140 | +var wasmBinary = Module["wasmBinary"]; | |
| 141 | + | |
| 142 | +// Wasm globals | |
| 115 | 143 | var wasmMemory; |
| 116 | 144 | |
| 145 | +//======================================== | |
| 146 | +// Runtime essentials | |
| 147 | +//======================================== | |
| 148 | +// whether we are quitting the application. no code should run after this. | |
| 149 | +// set in exit() and abort() | |
| 117 | 150 | var ABORT = false; |
| 118 | 151 | |
| 152 | +// set by exit() and abort(). Passed to 'onExit' handler. | |
| 153 | +// NOTE: This is also used as the process return code code in shell environments | |
| 154 | +// but only when noExitRuntime is false. | |
| 119 | 155 | var EXITSTATUS; |
| 120 | 156 | |
| 121 | -var UTF8Decoder = typeof TextDecoder != "undefined" ? new TextDecoder("utf8") : undefined; | |
| 122 | - | |
| 123 | -function UTF8ArrayToString(heapOrArray, idx, maxBytesToRead) { | |
| 124 | - var endIdx = idx + maxBytesToRead; | |
| 125 | - var endPtr = idx; | |
| 126 | - while (heapOrArray[endPtr] && !(endPtr >= endIdx)) ++endPtr; | |
| 127 | - if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) { | |
| 128 | - return UTF8Decoder.decode(heapOrArray.subarray(idx, endPtr)); | |
| 129 | - } | |
| 130 | - var str = ""; | |
| 131 | - while (idx < endPtr) { | |
| 132 | - var u0 = heapOrArray[idx++]; | |
| 133 | - if (!(u0 & 128)) { | |
| 134 | - str += String.fromCharCode(u0); | |
| 135 | - continue; | |
| 136 | - } | |
| 137 | - var u1 = heapOrArray[idx++] & 63; | |
| 138 | - if ((u0 & 224) == 192) { | |
| 139 | - str += String.fromCharCode((u0 & 31) << 6 | u1); | |
| 140 | - continue; | |
| 141 | - } | |
| 142 | - var u2 = heapOrArray[idx++] & 63; | |
| 143 | - if ((u0 & 240) == 224) { | |
| 144 | - u0 = (u0 & 15) << 12 | u1 << 6 | u2; | |
| 145 | - } else { | |
| 146 | - u0 = (u0 & 7) << 18 | u1 << 12 | u2 << 6 | heapOrArray[idx++] & 63; | |
| 147 | - } | |
| 148 | - if (u0 < 65536) { | |
| 149 | - str += String.fromCharCode(u0); | |
| 150 | - } else { | |
| 151 | - var ch = u0 - 65536; | |
| 152 | - str += String.fromCharCode(55296 | ch >> 10, 56320 | ch & 1023); | |
| 153 | - } | |
| 154 | - } | |
| 155 | - return str; | |
| 156 | -} | |
| 157 | - | |
| 158 | -function UTF8ToString(ptr, maxBytesToRead) { | |
| 159 | - return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : ""; | |
| 160 | -} | |
| 161 | - | |
| 162 | -function stringToUTF8Array(str, heap, outIdx, maxBytesToWrite) { | |
| 163 | - if (!(maxBytesToWrite > 0)) return 0; | |
| 164 | - var startIdx = outIdx; | |
| 165 | - var endIdx = outIdx + maxBytesToWrite - 1; | |
| 166 | - for (var i = 0; i < str.length; ++i) { | |
| 167 | - var u = str.charCodeAt(i); | |
| 168 | - if (u >= 55296 && u <= 57343) { | |
| 169 | - var u1 = str.charCodeAt(++i); | |
| 170 | - u = 65536 + ((u & 1023) << 10) | u1 & 1023; | |
| 171 | - } | |
| 172 | - if (u <= 127) { | |
| 173 | - if (outIdx >= endIdx) break; | |
| 174 | - heap[outIdx++] = u; | |
| 175 | - } else if (u <= 2047) { | |
| 176 | - if (outIdx + 1 >= endIdx) break; | |
| 177 | - heap[outIdx++] = 192 | u >> 6; | |
| 178 | - heap[outIdx++] = 128 | u & 63; | |
| 179 | - } else if (u <= 65535) { | |
| 180 | - if (outIdx + 2 >= endIdx) break; | |
| 181 | - heap[outIdx++] = 224 | u >> 12; | |
| 182 | - heap[outIdx++] = 128 | u >> 6 & 63; | |
| 183 | - heap[outIdx++] = 128 | u & 63; | |
| 184 | - } else { | |
| 185 | - if (outIdx + 3 >= endIdx) break; | |
| 186 | - heap[outIdx++] = 240 | u >> 18; | |
| 187 | - heap[outIdx++] = 128 | u >> 12 & 63; | |
| 188 | - heap[outIdx++] = 128 | u >> 6 & 63; | |
| 189 | - heap[outIdx++] = 128 | u & 63; | |
| 190 | - } | |
| 191 | - } | |
| 192 | - heap[outIdx] = 0; | |
| 193 | - return outIdx - startIdx; | |
| 194 | -} | |
| 195 | - | |
| 196 | -function stringToUTF8(str, outPtr, maxBytesToWrite) { | |
| 197 | - return stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite); | |
| 198 | -} | |
| 199 | - | |
| 200 | -var HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32, HEAPF32, HEAPF64; | |
| 201 | - | |
| 157 | +// Memory management | |
| 158 | +var /** @type {!Int8Array} */ HEAP8, /** @type {!Uint8Array} */ HEAPU8, /** @type {!Int16Array} */ HEAP16, /** @type {!Uint16Array} */ HEAPU16, /** @type {!Int32Array} */ HEAP32, /** @type {!Uint32Array} */ HEAPU32, /** @type {!Float32Array} */ HEAPF32, /** @type {!Float64Array} */ HEAPF64; | |
| 159 | + | |
| 160 | +// include: runtime_shared.js | |
| 202 | 161 | function updateMemoryViews() { |
| 203 | - var b = wasmMemory.buffer; | |
| 204 | - Module["HEAP8"] = HEAP8 = new Int8Array(b); | |
| 205 | - Module["HEAP16"] = HEAP16 = new Int16Array(b); | |
| 206 | - Module["HEAP32"] = HEAP32 = new Int32Array(b); | |
| 207 | - Module["HEAPU8"] = HEAPU8 = new Uint8Array(b); | |
| 208 | - Module["HEAPU16"] = HEAPU16 = new Uint16Array(b); | |
| 209 | - Module["HEAPU32"] = HEAPU32 = new Uint32Array(b); | |
| 210 | - Module["HEAPF32"] = HEAPF32 = new Float32Array(b); | |
| 211 | - Module["HEAPF64"] = HEAPF64 = new Float64Array(b); | |
| 212 | -} | |
| 213 | - | |
| 214 | -var INITIAL_MEMORY = Module["INITIAL_MEMORY"] || 16777216; | |
| 215 | - | |
| 216 | -var wasmTable; | |
| 217 | - | |
| 162 | + var b = wasmMemory.buffer; | |
| 163 | + Module["HEAP8"] = HEAP8 = new Int8Array(b); | |
| 164 | + Module["HEAP16"] = HEAP16 = new Int16Array(b); | |
| 165 | + Module["HEAPU8"] = HEAPU8 = new Uint8Array(b); | |
| 166 | + Module["HEAPU16"] = HEAPU16 = new Uint16Array(b); | |
| 167 | + Module["HEAP32"] = HEAP32 = new Int32Array(b); | |
| 168 | + Module["HEAPU32"] = HEAPU32 = new Uint32Array(b); | |
| 169 | + Module["HEAPF32"] = HEAPF32 = new Float32Array(b); | |
| 170 | + Module["HEAPF64"] = HEAPF64 = new Float64Array(b); | |
| 171 | +} | |
| 172 | + | |
| 173 | +// end include: runtime_shared.js | |
| 174 | +// include: runtime_stack_check.js | |
| 175 | +// end include: runtime_stack_check.js | |
| 218 | 176 | var __ATPRERUN__ = []; |
| 219 | 177 | |
| 178 | +// functions called before the runtime is initialized | |
| 220 | 179 | var __ATINIT__ = []; |
| 221 | 180 | |
| 181 | +// functions called during shutdown | |
| 222 | 182 | var __ATPOSTRUN__ = []; |
| 223 | 183 | |
| 184 | +// functions called after the main() is called | |
| 224 | 185 | var runtimeInitialized = false; |
| 225 | 186 | |
| 226 | -function keepRuntimeAlive() { | |
| 227 | - return noExitRuntime; | |
| 228 | -} | |
| 229 | - | |
| 230 | 187 | function preRun() { |
| 231 | - if (Module["preRun"]) { | |
| 232 | - if (typeof Module["preRun"] == "function") Module["preRun"] = [ Module["preRun"] ]; | |
| 233 | - while (Module["preRun"].length) { | |
| 234 | - addOnPreRun(Module["preRun"].shift()); | |
| 188 | + var preRuns = Module["preRun"]; | |
| 189 | + if (preRuns) { | |
| 190 | + if (typeof preRuns == "function") preRuns = [ preRuns ]; | |
| 191 | + preRuns.forEach(addOnPreRun); | |
| 235 | 192 | } |
| 236 | - } | |
| 237 | - callRuntimeCallbacks(__ATPRERUN__); | |
| 193 | + callRuntimeCallbacks(__ATPRERUN__); | |
| 238 | 194 | } |
| 239 | 195 | |
| 240 | 196 | function initRuntime() { |
| 241 | - runtimeInitialized = true; | |
| 242 | - callRuntimeCallbacks(__ATINIT__); | |
| 197 | + runtimeInitialized = true; | |
| 198 | + callRuntimeCallbacks(__ATINIT__); | |
| 243 | 199 | } |
| 244 | 200 | |
| 245 | 201 | function postRun() { |
| 246 | - if (Module["postRun"]) { | |
| 247 | - if (typeof Module["postRun"] == "function") Module["postRun"] = [ Module["postRun"] ]; | |
| 248 | - while (Module["postRun"].length) { | |
| 249 | - addOnPostRun(Module["postRun"].shift()); | |
| 202 | + var postRuns = Module["postRun"]; | |
| 203 | + if (postRuns) { | |
| 204 | + if (typeof postRuns == "function") postRuns = [ postRuns ]; | |
| 205 | + postRuns.forEach(addOnPostRun); | |
| 250 | 206 | } |
| 251 | - } | |
| 252 | - callRuntimeCallbacks(__ATPOSTRUN__); | |
| 207 | + callRuntimeCallbacks(__ATPOSTRUN__); | |
| 253 | 208 | } |
| 254 | 209 | |
| 255 | 210 | function addOnPreRun(cb) { |
| 256 | - __ATPRERUN__.unshift(cb); | |
| 211 | + __ATPRERUN__.unshift(cb); | |
| 257 | 212 | } |
| 258 | 213 | |
| 259 | 214 | function addOnInit(cb) { |
| 260 | - __ATINIT__.unshift(cb); | |
| 215 | + __ATINIT__.unshift(cb); | |
| 261 | 216 | } |
| 262 | 217 | |
| 263 | 218 | function addOnPostRun(cb) { |
| 264 | - __ATPOSTRUN__.unshift(cb); | |
| 219 | + __ATPOSTRUN__.unshift(cb); | |
| 265 | 220 | } |
| 266 | 221 | |
| 222 | +// include: runtime_math.js | |
| 223 | +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul | |
| 224 | +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/fround | |
| 225 | +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/clz32 | |
| 226 | +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc | |
| 227 | +// end include: runtime_math.js | |
| 228 | +// A counter of dependencies for calling run(). If we need to | |
| 229 | +// do asynchronous work before running, increment this and | |
| 230 | +// decrement it. Incrementing must happen in a place like | |
| 231 | +// Module.preRun (used by emcc to add file preloading). | |
| 232 | +// Note that you can add dependencies in preRun, even though | |
| 233 | +// it happens right before run - run will be postponed until | |
| 234 | +// the dependencies are met. | |
| 267 | 235 | var runDependencies = 0; |
| 268 | 236 | |
| 269 | 237 | var runDependencyWatcher = null; |
| 270 | 238 | |
| 271 | 239 | var dependenciesFulfilled = null; |
| 272 | 240 | |
| 273 | 241 | function addRunDependency(id) { |
| 274 | - runDependencies++; | |
| 275 | - if (Module["monitorRunDependencies"]) { | |
| 276 | - Module["monitorRunDependencies"](runDependencies); | |
| 277 | - } | |
| 242 | + runDependencies++; | |
| 243 | + Module["monitorRunDependencies"]?.(runDependencies); | |
| 278 | 244 | } |
| 279 | 245 | |
| 280 | 246 | function removeRunDependency(id) { |
| 281 | - runDependencies--; | |
| 282 | - if (Module["monitorRunDependencies"]) { | |
| 283 | - Module["monitorRunDependencies"](runDependencies); | |
| 284 | - } | |
| 285 | - if (runDependencies == 0) { | |
| 286 | - if (runDependencyWatcher !== null) { | |
| 287 | - clearInterval(runDependencyWatcher); | |
| 288 | - runDependencyWatcher = null; | |
| 289 | - } | |
| 290 | - if (dependenciesFulfilled) { | |
| 291 | - var callback = dependenciesFulfilled; | |
| 292 | - dependenciesFulfilled = null; | |
| 293 | - callback(); | |
| 294 | - } | |
| 295 | - } | |
| 296 | -} | |
| 297 | - | |
| 298 | -function abort(what) { | |
| 299 | - if (Module["onAbort"]) { | |
| 300 | - Module["onAbort"](what); | |
| 301 | - } | |
| 302 | - what = "Aborted(" + what + ")"; | |
| 303 | - err(what); | |
| 304 | - ABORT = true; | |
| 305 | - EXITSTATUS = 1; | |
| 306 | - what += ". Build with -sASSERTIONS for more info."; | |
| 307 | - var e = new WebAssembly.RuntimeError(what); | |
| 308 | - readyPromiseReject(e); | |
| 309 | - throw e; | |
| 310 | -} | |
| 311 | - | |
| 247 | + runDependencies--; | |
| 248 | + Module["monitorRunDependencies"]?.(runDependencies); | |
| 249 | + if (runDependencies == 0) { | |
| 250 | + if (runDependencyWatcher !== null) { | |
| 251 | + clearInterval(runDependencyWatcher); | |
| 252 | + runDependencyWatcher = null; | |
| 253 | + } | |
| 254 | + if (dependenciesFulfilled) { | |
| 255 | + var callback = dependenciesFulfilled; | |
| 256 | + dependenciesFulfilled = null; | |
| 257 | + callback(); | |
| 258 | + } | |
| 259 | + } | |
| 260 | +} | |
| 261 | + | |
| 262 | +/** @param {string|number=} what */ function abort(what) { | |
| 263 | + Module["onAbort"]?.(what); | |
| 264 | + what = "Aborted(" + what + ")"; | |
| 265 | + // TODO(sbc): Should we remove printing and leave it up to whoever | |
| 266 | + // catches the exception? | |
| 267 | + err(what); | |
| 268 | + ABORT = true; | |
| 269 | + what += ". Build with -sASSERTIONS for more info."; | |
| 270 | + // Use a wasm runtime error, because a JS error might be seen as a foreign | |
| 271 | + // exception, which means we'd run destructors on it. We need the error to | |
| 272 | + // simply make the program stop. | |
| 273 | + // FIXME This approach does not work in Wasm EH because it currently does not assume | |
| 274 | + // all RuntimeErrors are from traps; it decides whether a RuntimeError is from | |
| 275 | + // a trap or not based on a hidden field within the object. So at the moment | |
| 276 | + // we don't have a way of throwing a wasm trap from JS. TODO Make a JS API that | |
| 277 | + // allows this in the wasm spec. | |
| 278 | + // Suppress closure compiler warning here. Closure compiler's builtin extern | |
| 279 | + // definition for WebAssembly.RuntimeError claims it takes no arguments even | |
| 280 | + // though it can. | |
| 281 | + // TODO(https://github.com/google/closure-compiler/pull/3913): Remove if/when upstream closure gets fixed. | |
| 282 | + /** @suppress {checkTypes} */ var e = new WebAssembly.RuntimeError(what); | |
| 283 | + readyPromiseReject(e); | |
| 284 | + // Throw the error whether or not MODULARIZE is set because abort is used | |
| 285 | + // in code paths apart from instantiation where an exception is expected | |
| 286 | + // to be thrown when abort is called. | |
| 287 | + throw e; | |
| 288 | +} | |
| 289 | + | |
| 290 | +// include: memoryprofiler.js | |
| 291 | +// end include: memoryprofiler.js | |
| 292 | +// include: URIUtils.js | |
| 293 | +// Prefix of data URIs emitted by SINGLE_FILE and related options. | |
| 312 | 294 | var dataURIPrefix = "data:application/octet-stream;base64,"; |
| 313 | 295 | |
| 314 | -function isDataURI(filename) { | |
| 315 | - return filename.startsWith(dataURIPrefix); | |
| 296 | +/** | |
| 297 | + * Indicates whether filename is a base64 data URI. | |
| 298 | + * @noinline | |
| 299 | + */ var isDataURI = filename => filename.startsWith(dataURIPrefix); | |
| 300 | + | |
| 301 | +// end include: URIUtils.js | |
| 302 | +// include: runtime_exceptions.js | |
| 303 | +// end include: runtime_exceptions.js | |
| 304 | +function findWasmBinary() { | |
| 305 | + var f = "pikchr-v2813665466.wasm"; | |
| 306 | + if (!isDataURI(f)) { | |
| 307 | + return locateFile(f); | |
| 308 | + } | |
| 309 | + return f; | |
| 316 | 310 | } |
| 317 | 311 | |
| 318 | 312 | var wasmBinaryFile; |
| 319 | 313 | |
| 320 | -wasmBinaryFile = "pikchr.wasm"; | |
| 321 | - | |
| 322 | -if (!isDataURI(wasmBinaryFile)) { | |
| 323 | - wasmBinaryFile = locateFile(wasmBinaryFile); | |
| 324 | -} | |
| 325 | - | |
| 326 | -function getBinary(file) { | |
| 327 | - try { | |
| 328 | - if (file == wasmBinaryFile && wasmBinary) { | |
| 329 | - return new Uint8Array(wasmBinary); | |
| 330 | - } | |
| 331 | - if (readBinary) { | |
| 332 | - return readBinary(file); | |
| 333 | - } | |
| 334 | - throw "both async and sync fetching of the wasm failed"; | |
| 335 | - } catch (err) { | |
| 336 | - abort(err); | |
| 337 | - } | |
| 338 | -} | |
| 339 | - | |
| 340 | -function getBinaryPromise() { | |
| 341 | - if (!wasmBinary && (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER)) { | |
| 342 | - if (typeof fetch == "function") { | |
| 343 | - return fetch(wasmBinaryFile, { | |
| 344 | - credentials: "same-origin" | |
| 345 | - }).then(function(response) { | |
| 346 | - if (!response["ok"]) { | |
| 347 | - throw "failed to load wasm binary file at '" + wasmBinaryFile + "'"; | |
| 348 | - } | |
| 349 | - return response["arrayBuffer"](); | |
| 350 | - }).catch(function() { | |
| 351 | - return getBinary(wasmBinaryFile); | |
| 352 | - }); | |
| 353 | - } | |
| 354 | - } | |
| 355 | - return Promise.resolve().then(function() { | |
| 356 | - return getBinary(wasmBinaryFile); | |
| 357 | - }); | |
| 358 | -} | |
| 359 | - | |
| 360 | -function createWasm() { | |
| 361 | - var info = { | |
| 362 | - "a": asmLibraryArg | |
| 363 | - }; | |
| 364 | - function receiveInstance(instance, module) { | |
| 365 | - var exports = instance.exports; | |
| 366 | - Module["asm"] = exports; | |
| 367 | - wasmMemory = Module["asm"]["d"]; | |
| 368 | - updateMemoryViews(); | |
| 369 | - wasmTable = Module["asm"]["g"]; | |
| 370 | - addOnInit(Module["asm"]["e"]); | |
| 371 | - removeRunDependency("wasm-instantiate"); | |
| 372 | - } | |
| 373 | - addRunDependency("wasm-instantiate"); | |
| 374 | - function receiveInstantiationResult(result) { | |
| 375 | - receiveInstance(result["instance"]); | |
| 376 | - } | |
| 377 | - function instantiateArrayBuffer(receiver) { | |
| 378 | - return getBinaryPromise().then(function(binary) { | |
| 379 | - return WebAssembly.instantiate(binary, info); | |
| 380 | - }).then(function(instance) { | |
| 381 | - return instance; | |
| 382 | - }).then(receiver, function(reason) { | |
| 383 | - err("failed to asynchronously prepare wasm: " + reason); | |
| 384 | - abort(reason); | |
| 385 | - }); | |
| 386 | - } | |
| 387 | - function instantiateAsync() { | |
| 388 | - if (!wasmBinary && typeof WebAssembly.instantiateStreaming == "function" && !isDataURI(wasmBinaryFile) && typeof fetch == "function") { | |
| 389 | - return fetch(wasmBinaryFile, { | |
| 390 | - credentials: "same-origin" | |
| 391 | - }).then(function(response) { | |
| 392 | - var result = WebAssembly.instantiateStreaming(response, info); | |
| 393 | - return result.then(receiveInstantiationResult, function(reason) { | |
| 394 | - err("wasm streaming compile failed: " + reason); | |
| 395 | - err("falling back to ArrayBuffer instantiation"); | |
| 396 | - return instantiateArrayBuffer(receiveInstantiationResult); | |
| 397 | - }); | |
| 398 | - }); | |
| 399 | - } else { | |
| 400 | - return instantiateArrayBuffer(receiveInstantiationResult); | |
| 401 | - } | |
| 402 | - } | |
| 403 | - if (Module["instantiateWasm"]) { | |
| 404 | - try { | |
| 405 | - var exports = Module["instantiateWasm"](info, receiveInstance); | |
| 406 | - return exports; | |
| 407 | - } catch (e) { | |
| 408 | - err("Module.instantiateWasm callback failed with error: " + e); | |
| 409 | - readyPromiseReject(e); | |
| 410 | - } | |
| 411 | - } | |
| 412 | - instantiateAsync().catch(readyPromiseReject); | |
| 413 | - return {}; | |
| 414 | -} | |
| 415 | - | |
| 416 | -var tempDouble; | |
| 417 | - | |
| 418 | -var tempI64; | |
| 419 | - | |
| 420 | -function ExitStatus(status) { | |
| 421 | - this.name = "ExitStatus"; | |
| 422 | - this.message = "Program terminated with exit(" + status + ")"; | |
| 423 | - this.status = status; | |
| 424 | -} | |
| 425 | - | |
| 426 | -function callRuntimeCallbacks(callbacks) { | |
| 427 | - while (callbacks.length > 0) { | |
| 428 | - callbacks.shift()(Module); | |
| 429 | - } | |
| 430 | -} | |
| 431 | - | |
| 432 | -function getValue(ptr, type = "i8") { | |
| 433 | - if (type.endsWith("*")) type = "*"; | |
| 434 | - switch (type) { | |
| 435 | - case "i1": | |
| 436 | - return HEAP8[ptr >> 0]; | |
| 437 | - | |
| 438 | - case "i8": | |
| 439 | - return HEAP8[ptr >> 0]; | |
| 440 | - | |
| 441 | - case "i16": | |
| 442 | - return HEAP16[ptr >> 1]; | |
| 443 | - | |
| 444 | - case "i32": | |
| 445 | - return HEAP32[ptr >> 2]; | |
| 446 | - | |
| 447 | - case "i64": | |
| 448 | - return HEAP32[ptr >> 2]; | |
| 449 | - | |
| 450 | - case "float": | |
| 451 | - return HEAPF32[ptr >> 2]; | |
| 452 | - | |
| 453 | - case "double": | |
| 454 | - return HEAPF64[ptr >> 3]; | |
| 455 | - | |
| 456 | - case "*": | |
| 457 | - return HEAPU32[ptr >> 2]; | |
| 458 | - | |
| 459 | - default: | |
| 460 | - abort("invalid type for getValue: " + type); | |
| 461 | - } | |
| 462 | - return null; | |
| 463 | -} | |
| 464 | - | |
| 465 | -function setValue(ptr, value, type = "i8") { | |
| 466 | - if (type.endsWith("*")) type = "*"; | |
| 467 | - switch (type) { | |
| 468 | - case "i1": | |
| 469 | - HEAP8[ptr >> 0] = value; | |
| 470 | - break; | |
| 471 | - | |
| 472 | - case "i8": | |
| 473 | - HEAP8[ptr >> 0] = value; | |
| 474 | - break; | |
| 475 | - | |
| 476 | - case "i16": | |
| 477 | - HEAP16[ptr >> 1] = value; | |
| 478 | - break; | |
| 479 | - | |
| 480 | - case "i32": | |
| 481 | - HEAP32[ptr >> 2] = value; | |
| 482 | - break; | |
| 483 | - | |
| 484 | - case "i64": | |
| 485 | - tempI64 = [ value >>> 0, (tempDouble = value, +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil((tempDouble - +(~~tempDouble >>> 0)) / 4294967296) >>> 0 : 0) ], | |
| 486 | - HEAP32[ptr >> 2] = tempI64[0], HEAP32[ptr + 4 >> 2] = tempI64[1]; | |
| 487 | - break; | |
| 488 | - | |
| 489 | - case "float": | |
| 490 | - HEAPF32[ptr >> 2] = value; | |
| 491 | - break; | |
| 492 | - | |
| 493 | - case "double": | |
| 494 | - HEAPF64[ptr >> 3] = value; | |
| 495 | - break; | |
| 496 | - | |
| 497 | - case "*": | |
| 498 | - HEAPU32[ptr >> 2] = value; | |
| 499 | - break; | |
| 500 | - | |
| 501 | - default: | |
| 502 | - abort("invalid type for setValue: " + type); | |
| 503 | - } | |
| 504 | -} | |
| 505 | - | |
| 506 | -function ___assert_fail(condition, filename, line, func) { | |
| 507 | - abort("Assertion failed: " + UTF8ToString(condition) + ", at: " + [ filename ? UTF8ToString(filename) : "unknown filename", line, func ? UTF8ToString(func) : "unknown function" ]); | |
| 508 | -} | |
| 509 | - | |
| 510 | -function abortOnCannotGrowMemory(requestedSize) { | |
| 511 | - abort("OOM"); | |
| 512 | -} | |
| 513 | - | |
| 514 | -function _emscripten_resize_heap(requestedSize) { | |
| 515 | - var oldSize = HEAPU8.length; | |
| 516 | - requestedSize = requestedSize >>> 0; | |
| 517 | - abortOnCannotGrowMemory(requestedSize); | |
| 518 | -} | |
| 519 | - | |
| 520 | -var SYSCALLS = { | |
| 521 | - varargs: undefined, | |
| 522 | - get: function() { | |
| 523 | - SYSCALLS.varargs += 4; | |
| 524 | - var ret = HEAP32[SYSCALLS.varargs - 4 >> 2]; | |
| 525 | - return ret; | |
| 526 | - }, | |
| 527 | - getStr: function(ptr) { | |
| 528 | - var ret = UTF8ToString(ptr); | |
| 529 | - return ret; | |
| 530 | - } | |
| 531 | -}; | |
| 532 | - | |
| 533 | -function _proc_exit(code) { | |
| 534 | - EXITSTATUS = code; | |
| 535 | - if (!keepRuntimeAlive()) { | |
| 536 | - if (Module["onExit"]) Module["onExit"](code); | |
| 537 | - ABORT = true; | |
| 538 | - } | |
| 539 | - quit_(code, new ExitStatus(code)); | |
| 540 | -} | |
| 541 | - | |
| 542 | -function exitJS(status, implicit) { | |
| 543 | - EXITSTATUS = status; | |
| 544 | - _proc_exit(status); | |
| 545 | -} | |
| 314 | +function getBinarySync(file) { | |
| 315 | + if (file == wasmBinaryFile && wasmBinary) { | |
| 316 | + return new Uint8Array(wasmBinary); | |
| 317 | + } | |
| 318 | + if (readBinary) { | |
| 319 | + return readBinary(file); | |
| 320 | + } | |
| 321 | + throw "both async and sync fetching of the wasm failed"; | |
| 322 | +} | |
| 323 | + | |
| 324 | +function getBinaryPromise(binaryFile) { | |
| 325 | + // If we don't have the binary yet, load it asynchronously using readAsync. | |
| 326 | + if (!wasmBinary) { | |
| 327 | + // Fetch the binary using readAsync | |
| 328 | + return readAsync(binaryFile).then(response => new Uint8Array(/** @type{!ArrayBuffer} */ (response)), // Fall back to getBinarySync if readAsync fails | |
| 329 | + () => getBinarySync(binaryFile)); | |
| 330 | + } | |
| 331 | + // Otherwise, getBinarySync should be able to get it synchronously | |
| 332 | + return Promise.resolve().then(() => getBinarySync(binaryFile)); | |
| 333 | +} | |
| 334 | + | |
| 335 | +function instantiateArrayBuffer(binaryFile, imports, receiver) { | |
| 336 | + return getBinaryPromise(binaryFile).then(binary => WebAssembly.instantiate(binary, imports)).then(receiver, reason => { | |
| 337 | + err(`failed to asynchronously prepare wasm: ${reason}`); | |
| 338 | + abort(reason); | |
| 339 | + }); | |
| 340 | +} | |
| 341 | + | |
| 342 | +function instantiateAsync(binary, binaryFile, imports, callback) { | |
| 343 | + if (!binary && typeof WebAssembly.instantiateStreaming == "function" && !isDataURI(binaryFile) && typeof fetch == "function") { | |
| 344 | + return fetch(binaryFile, { | |
| 345 | + credentials: "same-origin" | |
| 346 | + }).then(response => { | |
| 347 | + // Suppress closure warning here since the upstream definition for | |
| 348 | + // instantiateStreaming only allows Promise<Repsponse> rather than | |
| 349 | + // an actual Response. | |
| 350 | + // TODO(https://github.com/google/closure-compiler/pull/3913): Remove if/when upstream closure is fixed. | |
| 351 | + /** @suppress {checkTypes} */ var result = WebAssembly.instantiateStreaming(response, imports); | |
| 352 | + return result.then(callback, function(reason) { | |
| 353 | + // We expect the most common failure cause to be a bad MIME type for the binary, | |
| 354 | + // in which case falling back to ArrayBuffer instantiation should work. | |
| 355 | + err(`wasm streaming compile failed: ${reason}`); | |
| 356 | + err("falling back to ArrayBuffer instantiation"); | |
| 357 | + return instantiateArrayBuffer(binaryFile, imports, callback); | |
| 358 | + }); | |
| 359 | + }); | |
| 360 | + } | |
| 361 | + return instantiateArrayBuffer(binaryFile, imports, callback); | |
| 362 | +} | |
| 363 | + | |
| 364 | +function getWasmImports() { | |
| 365 | + // prepare imports | |
| 366 | + return { | |
| 367 | + "a": wasmImports | |
| 368 | + }; | |
| 369 | +} | |
| 370 | + | |
| 371 | +// Create the wasm instance. | |
| 372 | +// Receives the wasm imports, returns the exports. | |
| 373 | +function createWasm() { | |
| 374 | + var info = getWasmImports(); | |
| 375 | + // Load the wasm module and create an instance of using native support in the JS engine. | |
| 376 | + // handle a generated wasm instance, receiving its exports and | |
| 377 | + // performing other necessary setup | |
| 378 | + /** @param {WebAssembly.Module=} module*/ function receiveInstance(instance, module) { | |
| 379 | + wasmExports = instance.exports; | |
| 380 | + wasmMemory = wasmExports["d"]; | |
| 381 | + updateMemoryViews(); | |
| 382 | + addOnInit(wasmExports["e"]); | |
| 383 | + removeRunDependency("wasm-instantiate"); | |
| 384 | + return wasmExports; | |
| 385 | + } | |
| 386 | + // wait for the pthread pool (if any) | |
| 387 | + addRunDependency("wasm-instantiate"); | |
| 388 | + // Prefer streaming instantiation if available. | |
| 389 | + function receiveInstantiationResult(result) { | |
| 390 | + // 'result' is a ResultObject object which has both the module and instance. | |
| 391 | + // receiveInstance() will swap in the exports (to Module.asm) so they can be called | |
| 392 | + // TODO: Due to Closure regression https://github.com/google/closure-compiler/issues/3193, the above line no longer optimizes out down to the following line. | |
| 393 | + // When the regression is fixed, can restore the above PTHREADS-enabled path. | |
| 394 | + receiveInstance(result["instance"]); | |
| 395 | + } | |
| 396 | + // User shell pages can write their own Module.instantiateWasm = function(imports, successCallback) callback | |
| 397 | + // to manually instantiate the Wasm module themselves. This allows pages to | |
| 398 | + // run the instantiation parallel to any other async startup actions they are | |
| 399 | + // performing. | |
| 400 | + // Also pthreads and wasm workers initialize the wasm instance through this | |
| 401 | + // path. | |
| 402 | + if (Module["instantiateWasm"]) { | |
| 403 | + try { | |
| 404 | + return Module["instantiateWasm"](info, receiveInstance); | |
| 405 | + } catch (e) { | |
| 406 | + err(`Module.instantiateWasm callback failed with error: ${e}`); | |
| 407 | + // If instantiation fails, reject the module ready promise. | |
| 408 | + readyPromiseReject(e); | |
| 409 | + } | |
| 410 | + } | |
| 411 | + wasmBinaryFile ??= findWasmBinary(); | |
| 412 | + // If instantiation fails, reject the module ready promise. | |
| 413 | + instantiateAsync(wasmBinary, wasmBinaryFile, info, receiveInstantiationResult).catch(readyPromiseReject); | |
| 414 | + return {}; | |
| 415 | +} | |
| 416 | + | |
| 417 | +// include: runtime_debug.js | |
| 418 | +// end include: runtime_debug.js | |
| 419 | +// === Body === | |
| 420 | +// end include: preamble.js | |
| 421 | +/** @constructor */ function ExitStatus(status) { | |
| 422 | + this.name = "ExitStatus"; | |
| 423 | + this.message = `Program terminated with exit(${status})`; | |
| 424 | + this.status = status; | |
| 425 | +} | |
| 426 | + | |
| 427 | +var callRuntimeCallbacks = callbacks => { | |
| 428 | + // Pass the module as the first argument. | |
| 429 | + callbacks.forEach(f => f(Module)); | |
| 430 | +}; | |
| 431 | + | |
| 432 | +/** | |
| 433 | + * @param {number} ptr | |
| 434 | + * @param {string} type | |
| 435 | + */ function getValue(ptr, type = "i8") { | |
| 436 | + if (type.endsWith("*")) type = "*"; | |
| 437 | + switch (type) { | |
| 438 | + case "i1": | |
| 439 | + return HEAP8[ptr]; | |
| 440 | + | |
| 441 | + case "i8": | |
| 442 | + return HEAP8[ptr]; | |
| 443 | + | |
| 444 | + case "i16": | |
| 445 | + return HEAP16[((ptr) >> 1)]; | |
| 446 | + | |
| 447 | + case "i32": | |
| 448 | + return HEAP32[((ptr) >> 2)]; | |
| 449 | + | |
| 450 | + case "i64": | |
| 451 | + abort("to do getValue(i64) use WASM_BIGINT"); | |
| 452 | + | |
| 453 | + case "float": | |
| 454 | + return HEAPF32[((ptr) >> 2)]; | |
| 455 | + | |
| 456 | + case "double": | |
| 457 | + return HEAPF64[((ptr) >> 3)]; | |
| 458 | + | |
| 459 | + case "*": | |
| 460 | + return HEAPU32[((ptr) >> 2)]; | |
| 461 | + | |
| 462 | + default: | |
| 463 | + abort(`invalid type for getValue: ${type}`); | |
| 464 | + } | |
| 465 | +} | |
| 466 | + | |
| 467 | +var noExitRuntime = Module["noExitRuntime"] || true; | |
| 468 | + | |
| 469 | +/** | |
| 470 | + * @param {number} ptr | |
| 471 | + * @param {number} value | |
| 472 | + * @param {string} type | |
| 473 | + */ function setValue(ptr, value, type = "i8") { | |
| 474 | + if (type.endsWith("*")) type = "*"; | |
| 475 | + switch (type) { | |
| 476 | + case "i1": | |
| 477 | + HEAP8[ptr] = value; | |
| 478 | + break; | |
| 479 | + | |
| 480 | + case "i8": | |
| 481 | + HEAP8[ptr] = value; | |
| 482 | + break; | |
| 483 | + | |
| 484 | + case "i16": | |
| 485 | + HEAP16[((ptr) >> 1)] = value; | |
| 486 | + break; | |
| 487 | + | |
| 488 | + case "i32": | |
| 489 | + HEAP32[((ptr) >> 2)] = value; | |
| 490 | + break; | |
| 491 | + | |
| 492 | + case "i64": | |
| 493 | + abort("to do setValue(i64) use WASM_BIGINT"); | |
| 494 | + | |
| 495 | + case "float": | |
| 496 | + HEAPF32[((ptr) >> 2)] = value; | |
| 497 | + break; | |
| 498 | + | |
| 499 | + case "double": | |
| 500 | + HEAPF64[((ptr) >> 3)] = value; | |
| 501 | + break; | |
| 502 | + | |
| 503 | + case "*": | |
| 504 | + HEAPU32[((ptr) >> 2)] = value; | |
| 505 | + break; | |
| 506 | + | |
| 507 | + default: | |
| 508 | + abort(`invalid type for setValue: ${type}`); | |
| 509 | + } | |
| 510 | +} | |
| 511 | + | |
| 512 | +var stackRestore = val => __emscripten_stack_restore(val); | |
| 513 | + | |
| 514 | +var stackSave = () => _emscripten_stack_get_current(); | |
| 515 | + | |
| 516 | +var UTF8Decoder = typeof TextDecoder != "undefined" ? new TextDecoder : undefined; | |
| 517 | + | |
| 518 | +/** | |
| 519 | + * Given a pointer 'idx' to a null-terminated UTF8-encoded string in the given | |
| 520 | + * array that contains uint8 values, returns a copy of that string as a | |
| 521 | + * Javascript String object. | |
| 522 | + * heapOrArray is either a regular array, or a JavaScript typed array view. | |
| 523 | + * @param {number=} idx | |
| 524 | + * @param {number=} maxBytesToRead | |
| 525 | + * @return {string} | |
| 526 | + */ var UTF8ArrayToString = (heapOrArray, idx = 0, maxBytesToRead = NaN) => { | |
| 527 | + var endIdx = idx + maxBytesToRead; | |
| 528 | + var endPtr = idx; | |
| 529 | + // TextDecoder needs to know the byte length in advance, it doesn't stop on | |
| 530 | + // null terminator by itself. Also, use the length info to avoid running tiny | |
| 531 | + // strings through TextDecoder, since .subarray() allocates garbage. | |
| 532 | + // (As a tiny code save trick, compare endPtr against endIdx using a negation, | |
| 533 | + // so that undefined/NaN means Infinity) | |
| 534 | + while (heapOrArray[endPtr] && !(endPtr >= endIdx)) ++endPtr; | |
| 535 | + if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) { | |
| 536 | + return UTF8Decoder.decode(heapOrArray.subarray(idx, endPtr)); | |
| 537 | + } | |
| 538 | + var str = ""; | |
| 539 | + // If building with TextDecoder, we have already computed the string length | |
| 540 | + // above, so test loop end condition against that | |
| 541 | + while (idx < endPtr) { | |
| 542 | + // For UTF8 byte structure, see: | |
| 543 | + // http://en.wikipedia.org/wiki/UTF-8#Description | |
| 544 | + // https://www.ietf.org/rfc/rfc2279.txt | |
| 545 | + // https://tools.ietf.org/html/rfc3629 | |
| 546 | + var u0 = heapOrArray[idx++]; | |
| 547 | + if (!(u0 & 128)) { | |
| 548 | + str += String.fromCharCode(u0); | |
| 549 | + continue; | |
| 550 | + } | |
| 551 | + var u1 = heapOrArray[idx++] & 63; | |
| 552 | + if ((u0 & 224) == 192) { | |
| 553 | + str += String.fromCharCode(((u0 & 31) << 6) | u1); | |
| 554 | + continue; | |
| 555 | + } | |
| 556 | + var u2 = heapOrArray[idx++] & 63; | |
| 557 | + if ((u0 & 240) == 224) { | |
| 558 | + u0 = ((u0 & 15) << 12) | (u1 << 6) | u2; | |
| 559 | + } else { | |
| 560 | + u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heapOrArray[idx++] & 63); | |
| 561 | + } | |
| 562 | + if (u0 < 65536) { | |
| 563 | + str += String.fromCharCode(u0); | |
| 564 | + } else { | |
| 565 | + var ch = u0 - 65536; | |
| 566 | + str += String.fromCharCode(55296 | (ch >> 10), 56320 | (ch & 1023)); | |
| 567 | + } | |
| 568 | + } | |
| 569 | + return str; | |
| 570 | +}; | |
| 571 | + | |
| 572 | +/** | |
| 573 | + * Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the | |
| 574 | + * emscripten HEAP, returns a copy of that string as a Javascript String object. | |
| 575 | + * | |
| 576 | + * @param {number} ptr | |
| 577 | + * @param {number=} maxBytesToRead - An optional length that specifies the | |
| 578 | + * maximum number of bytes to read. You can omit this parameter to scan the | |
| 579 | + * string until the first 0 byte. If maxBytesToRead is passed, and the string | |
| 580 | + * at [ptr, ptr+maxBytesToReadr[ contains a null byte in the middle, then the | |
| 581 | + * string will cut short at that byte index (i.e. maxBytesToRead will not | |
| 582 | + * produce a string of exact length [ptr, ptr+maxBytesToRead[) N.B. mixing | |
| 583 | + * frequent uses of UTF8ToString() with and without maxBytesToRead may throw | |
| 584 | + * JS JIT optimizations off, so it is worth to consider consistently using one | |
| 585 | + * @return {string} | |
| 586 | + */ var UTF8ToString = (ptr, maxBytesToRead) => ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : ""; | |
| 587 | + | |
| 588 | +var ___assert_fail = (condition, filename, line, func) => { | |
| 589 | + abort(`Assertion failed: ${UTF8ToString(condition)}, at: ` + [ filename ? UTF8ToString(filename) : "unknown filename", line, func ? UTF8ToString(func) : "unknown function" ]); | |
| 590 | +}; | |
| 591 | + | |
| 592 | +var abortOnCannotGrowMemory = requestedSize => { | |
| 593 | + abort("OOM"); | |
| 594 | +}; | |
| 595 | + | |
| 596 | +var _emscripten_resize_heap = requestedSize => { | |
| 597 | + var oldSize = HEAPU8.length; | |
| 598 | + // With CAN_ADDRESS_2GB or MEMORY64, pointers are already unsigned. | |
| 599 | + requestedSize >>>= 0; | |
| 600 | + abortOnCannotGrowMemory(requestedSize); | |
| 601 | +}; | |
| 602 | + | |
| 603 | +var runtimeKeepaliveCounter = 0; | |
| 604 | + | |
| 605 | +var keepRuntimeAlive = () => noExitRuntime || runtimeKeepaliveCounter > 0; | |
| 606 | + | |
| 607 | +var _proc_exit = code => { | |
| 608 | + EXITSTATUS = code; | |
| 609 | + if (!keepRuntimeAlive()) { | |
| 610 | + Module["onExit"]?.(code); | |
| 611 | + ABORT = true; | |
| 612 | + } | |
| 613 | + quit_(code, new ExitStatus(code)); | |
| 614 | +}; | |
| 615 | + | |
| 616 | +/** @suppress {duplicate } */ /** @param {boolean|number=} implicit */ var exitJS = (status, implicit) => { | |
| 617 | + EXITSTATUS = status; | |
| 618 | + _proc_exit(status); | |
| 619 | +}; | |
| 546 | 620 | |
| 547 | 621 | var _exit = exitJS; |
| 548 | 622 | |
| 549 | -function getCFunc(ident) { | |
| 550 | - var func = Module["_" + ident]; | |
| 551 | - return func; | |
| 552 | -} | |
| 553 | - | |
| 554 | -function writeArrayToMemory(array, buffer) { | |
| 555 | - HEAP8.set(array, buffer); | |
| 556 | -} | |
| 557 | - | |
| 558 | -function ccall(ident, returnType, argTypes, args, opts) { | |
| 559 | - var toC = { | |
| 560 | - "string": str => { | |
| 561 | - var ret = 0; | |
| 562 | - if (str !== null && str !== undefined && str !== 0) { | |
| 563 | - var len = (str.length << 2) + 1; | |
| 564 | - ret = stackAlloc(len); | |
| 565 | - stringToUTF8(str, ret, len); | |
| 566 | - } | |
| 567 | - return ret; | |
| 568 | - }, | |
| 569 | - "array": arr => { | |
| 570 | - var ret = stackAlloc(arr.length); | |
| 571 | - writeArrayToMemory(arr, ret); | |
| 572 | - return ret; | |
| 573 | - } | |
| 574 | - }; | |
| 575 | - function convertReturnValue(ret) { | |
| 576 | - if (returnType === "string") { | |
| 577 | - return UTF8ToString(ret); | |
| 578 | - } | |
| 579 | - if (returnType === "boolean") return Boolean(ret); | |
| 580 | - return ret; | |
| 581 | - } | |
| 582 | - var func = getCFunc(ident); | |
| 583 | - var cArgs = []; | |
| 584 | - var stack = 0; | |
| 585 | - if (args) { | |
| 586 | - for (var i = 0; i < args.length; i++) { | |
| 587 | - var converter = toC[argTypes[i]]; | |
| 588 | - if (converter) { | |
| 589 | - if (stack === 0) stack = stackSave(); | |
| 590 | - cArgs[i] = converter(args[i]); | |
| 591 | - } else { | |
| 592 | - cArgs[i] = args[i]; | |
| 593 | - } | |
| 594 | - } | |
| 595 | - } | |
| 596 | - var ret = func.apply(null, cArgs); | |
| 597 | - function onDone(ret) { | |
| 598 | - if (stack !== 0) stackRestore(stack); | |
| 599 | - return convertReturnValue(ret); | |
| 600 | - } | |
| 601 | - ret = onDone(ret); | |
| 602 | - return ret; | |
| 603 | -} | |
| 604 | - | |
| 605 | -function cwrap(ident, returnType, argTypes, opts) { | |
| 606 | - argTypes = argTypes || []; | |
| 607 | - var numericArgs = argTypes.every(type => type === "number" || type === "boolean"); | |
| 608 | - var numericRet = returnType !== "string"; | |
| 609 | - if (numericRet && numericArgs && !opts) { | |
| 610 | - return getCFunc(ident); | |
| 611 | - } | |
| 612 | - return function() { | |
| 613 | - return ccall(ident, returnType, argTypes, arguments, opts); | |
| 614 | - }; | |
| 615 | -} | |
| 616 | - | |
| 617 | -var asmLibraryArg = { | |
| 618 | - "a": ___assert_fail, | |
| 619 | - "b": _emscripten_resize_heap, | |
| 620 | - "c": _exit | |
| 621 | -}; | |
| 622 | - | |
| 623 | -var asm = createWasm(); | |
| 624 | - | |
| 625 | -var ___wasm_call_ctors = Module["___wasm_call_ctors"] = function() { | |
| 626 | - return (___wasm_call_ctors = Module["___wasm_call_ctors"] = Module["asm"]["e"]).apply(null, arguments); | |
| 627 | -}; | |
| 628 | - | |
| 629 | -var _pikchr = Module["_pikchr"] = function() { | |
| 630 | - return (_pikchr = Module["_pikchr"] = Module["asm"]["f"]).apply(null, arguments); | |
| 631 | -}; | |
| 632 | - | |
| 633 | -var stackSave = Module["stackSave"] = function() { | |
| 634 | - return (stackSave = Module["stackSave"] = Module["asm"]["h"]).apply(null, arguments); | |
| 635 | -}; | |
| 636 | - | |
| 637 | -var stackRestore = Module["stackRestore"] = function() { | |
| 638 | - return (stackRestore = Module["stackRestore"] = Module["asm"]["i"]).apply(null, arguments); | |
| 639 | -}; | |
| 640 | - | |
| 641 | -var stackAlloc = Module["stackAlloc"] = function() { | |
| 642 | - return (stackAlloc = Module["stackAlloc"] = Module["asm"]["j"]).apply(null, arguments); | |
| 643 | -}; | |
| 644 | - | |
| 623 | +var getCFunc = ident => { | |
| 624 | + var func = Module["_" + ident]; | |
| 625 | + // closure exported function | |
| 626 | + return func; | |
| 627 | +}; | |
| 628 | + | |
| 629 | +var writeArrayToMemory = (array, buffer) => { | |
| 630 | + HEAP8.set(array, buffer); | |
| 631 | +}; | |
| 632 | + | |
| 633 | +var lengthBytesUTF8 = str => { | |
| 634 | + var len = 0; | |
| 635 | + for (var i = 0; i < str.length; ++i) { | |
| 636 | + // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code | |
| 637 | + // unit, not a Unicode code point of the character! So decode | |
| 638 | + // UTF16->UTF32->UTF8. | |
| 639 | + // See http://unicode.org/faq/utf_bom.html#utf16-3 | |
| 640 | + var c = str.charCodeAt(i); | |
| 641 | + // possibly a lead surrogate | |
| 642 | + if (c <= 127) { | |
| 643 | + len++; | |
| 644 | + } else if (c <= 2047) { | |
| 645 | + len += 2; | |
| 646 | + } else if (c >= 55296 && c <= 57343) { | |
| 647 | + len += 4; | |
| 648 | + ++i; | |
| 649 | + } else { | |
| 650 | + len += 3; | |
| 651 | + } | |
| 652 | + } | |
| 653 | + return len; | |
| 654 | +}; | |
| 655 | + | |
| 656 | +var stringToUTF8Array = (str, heap, outIdx, maxBytesToWrite) => { | |
| 657 | + // Parameter maxBytesToWrite is not optional. Negative values, 0, null, | |
| 658 | + // undefined and false each don't write out any bytes. | |
| 659 | + if (!(maxBytesToWrite > 0)) return 0; | |
| 660 | + var startIdx = outIdx; | |
| 661 | + var endIdx = outIdx + maxBytesToWrite - 1; | |
| 662 | + // -1 for string null terminator. | |
| 663 | + for (var i = 0; i < str.length; ++i) { | |
| 664 | + // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code | |
| 665 | + // unit, not a Unicode code point of the character! So decode | |
| 666 | + // UTF16->UTF32->UTF8. | |
| 667 | + // See http://unicode.org/faq/utf_bom.html#utf16-3 | |
| 668 | + // For UTF8 byte structure, see http://en.wikipedia.org/wiki/UTF-8#Description | |
| 669 | + // and https://www.ietf.org/rfc/rfc2279.txt | |
| 670 | + // and https://tools.ietf.org/html/rfc3629 | |
| 671 | + var u = str.charCodeAt(i); | |
| 672 | + // possibly a lead surrogate | |
| 673 | + if (u >= 55296 && u <= 57343) { | |
| 674 | + var u1 = str.charCodeAt(++i); | |
| 675 | + u = 65536 + ((u & 1023) << 10) | (u1 & 1023); | |
| 676 | + } | |
| 677 | + if (u <= 127) { | |
| 678 | + if (outIdx >= endIdx) break; | |
| 679 | + heap[outIdx++] = u; | |
| 680 | + } else if (u <= 2047) { | |
| 681 | + if (outIdx + 1 >= endIdx) break; | |
| 682 | + heap[outIdx++] = 192 | (u >> 6); | |
| 683 | + heap[outIdx++] = 128 | (u & 63); | |
| 684 | + } else if (u <= 65535) { | |
| 685 | + if (outIdx + 2 >= endIdx) break; | |
| 686 | + heap[outIdx++] = 224 | (u >> 12); | |
| 687 | + heap[outIdx++] = 128 | ((u >> 6) & 63); | |
| 688 | + heap[outIdx++] = 128 | (u & 63); | |
| 689 | + } else { | |
| 690 | + if (outIdx + 3 >= endIdx) break; | |
| 691 | + heap[outIdx++] = 240 | (u >> 18); | |
| 692 | + heap[outIdx++] = 128 | ((u >> 12) & 63); | |
| 693 | + heap[outIdx++] = 128 | ((u >> 6) & 63); | |
| 694 | + heap[outIdx++] = 128 | (u & 63); | |
| 695 | + } | |
| 696 | + } | |
| 697 | + // Null-terminate the pointer to the buffer. | |
| 698 | + heap[outIdx] = 0; | |
| 699 | + return outIdx - startIdx; | |
| 700 | +}; | |
| 701 | + | |
| 702 | +var stringToUTF8 = (str, outPtr, maxBytesToWrite) => stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite); | |
| 703 | + | |
| 704 | +var stackAlloc = sz => __emscripten_stack_alloc(sz); | |
| 705 | + | |
| 706 | +var stringToUTF8OnStack = str => { | |
| 707 | + var size = lengthBytesUTF8(str) + 1; | |
| 708 | + var ret = stackAlloc(size); | |
| 709 | + stringToUTF8(str, ret, size); | |
| 710 | + return ret; | |
| 711 | +}; | |
| 712 | + | |
| 713 | +/** | |
| 714 | + * @param {string|null=} returnType | |
| 715 | + * @param {Array=} argTypes | |
| 716 | + * @param {Arguments|Array=} args | |
| 717 | + * @param {Object=} opts | |
| 718 | + */ var ccall = (ident, returnType, argTypes, args, opts) => { | |
| 719 | + // For fast lookup of conversion functions | |
| 720 | + var toC = { | |
| 721 | + "string": str => { | |
| 722 | + var ret = 0; | |
| 723 | + if (str !== null && str !== undefined && str !== 0) { | |
| 724 | + // null string | |
| 725 | + ret = stringToUTF8OnStack(str); | |
| 726 | + } | |
| 727 | + return ret; | |
| 728 | + }, | |
| 729 | + "array": arr => { | |
| 730 | + var ret = stackAlloc(arr.length); | |
| 731 | + writeArrayToMemory(arr, ret); | |
| 732 | + return ret; | |
| 733 | + } | |
| 734 | + }; | |
| 735 | + function convertReturnValue(ret) { | |
| 736 | + if (returnType === "string") { | |
| 737 | + return UTF8ToString(ret); | |
| 738 | + } | |
| 739 | + if (returnType === "boolean") return Boolean(ret); | |
| 740 | + return ret; | |
| 741 | + } | |
| 742 | + var func = getCFunc(ident); | |
| 743 | + var cArgs = []; | |
| 744 | + var stack = 0; | |
| 745 | + if (args) { | |
| 746 | + for (var i = 0; i < args.length; i++) { | |
| 747 | + var converter = toC[argTypes[i]]; | |
| 748 | + if (converter) { | |
| 749 | + if (stack === 0) stack = stackSave(); | |
| 750 | + cArgs[i] = converter(args[i]); | |
| 751 | + } else { | |
| 752 | + cArgs[i] = args[i]; | |
| 753 | + } | |
| 754 | + } | |
| 755 | + } | |
| 756 | + var ret = func(...cArgs); | |
| 757 | + function onDone(ret) { | |
| 758 | + if (stack !== 0) stackRestore(stack); | |
| 759 | + return convertReturnValue(ret); | |
| 760 | + } | |
| 761 | + ret = onDone(ret); | |
| 762 | + return ret; | |
| 763 | +}; | |
| 764 | + | |
| 765 | +/** | |
| 766 | + * @param {string=} returnType | |
| 767 | + * @param {Array=} argTypes | |
| 768 | + * @param {Object=} opts | |
| 769 | + */ var cwrap = (ident, returnType, argTypes, opts) => { | |
| 770 | + // When the function takes numbers and returns a number, we can just return | |
| 771 | + // the original function | |
| 772 | + var numericArgs = !argTypes || argTypes.every(type => type === "number" || type === "boolean"); | |
| 773 | + var numericRet = returnType !== "string"; | |
| 774 | + if (numericRet && numericArgs && !opts) { | |
| 775 | + return getCFunc(ident); | |
| 776 | + } | |
| 777 | + return (...args) => ccall(ident, returnType, argTypes, args, opts); | |
| 778 | +}; | |
| 779 | + | |
| 780 | +var wasmImports = { | |
| 781 | + /** @export */ a: ___assert_fail, | |
| 782 | + /** @export */ b: _emscripten_resize_heap, | |
| 783 | + /** @export */ c: _exit | |
| 784 | +}; | |
| 785 | + | |
| 786 | +var wasmExports = createWasm(); | |
| 787 | + | |
| 788 | +var ___wasm_call_ctors = () => (___wasm_call_ctors = wasmExports["e"])(); | |
| 789 | + | |
| 790 | +var _pikchr_version = Module["_pikchr_version"] = () => (_pikchr_version = Module["_pikchr_version"] = wasmExports["g"])(); | |
| 791 | + | |
| 792 | +var _pikchr = Module["_pikchr"] = (a0, a1, a2, a3, a4) => (_pikchr = Module["_pikchr"] = wasmExports["h"])(a0, a1, a2, a3, a4); | |
| 793 | + | |
| 794 | +var __emscripten_stack_restore = a0 => (__emscripten_stack_restore = wasmExports["i"])(a0); | |
| 795 | + | |
| 796 | +var __emscripten_stack_alloc = a0 => (__emscripten_stack_alloc = wasmExports["j"])(a0); | |
| 797 | + | |
| 798 | +var _emscripten_stack_get_current = () => (_emscripten_stack_get_current = wasmExports["k"])(); | |
| 799 | + | |
| 800 | +// include: postamble.js | |
| 801 | +// === Auto-generated postamble setup entry stuff === | |
| 645 | 802 | Module["stackSave"] = stackSave; |
| 646 | 803 | |
| 647 | 804 | Module["stackRestore"] = stackRestore; |
| 805 | + | |
| 806 | +Module["stackAlloc"] = stackAlloc; | |
| 807 | + | |
| 808 | +Module["ccall"] = ccall; | |
| 648 | 809 | |
| 649 | 810 | Module["cwrap"] = cwrap; |
| 650 | 811 | |
| 651 | 812 | Module["setValue"] = setValue; |
| 652 | 813 | |
| 653 | 814 | Module["getValue"] = getValue; |
| 654 | 815 | |
| 655 | 816 | var calledRun; |
| 817 | + | |
| 818 | +var calledPrerun; | |
| 656 | 819 | |
| 657 | 820 | dependenciesFulfilled = function runCaller() { |
| 658 | - if (!calledRun) run(); | |
| 659 | - if (!calledRun) dependenciesFulfilled = runCaller; | |
| 821 | + // If run has never been called, and we should call run (INVOKE_RUN is true, and Module.noInitialRun is not false) | |
| 822 | + if (!calledRun) run(); | |
| 823 | + if (!calledRun) dependenciesFulfilled = runCaller; | |
| 660 | 824 | }; |
| 661 | 825 | |
| 662 | -function run(args) { | |
| 663 | - args = args || arguments_; | |
| 664 | - if (runDependencies > 0) { | |
| 665 | - return; | |
| 666 | - } | |
| 667 | - preRun(); | |
| 668 | - if (runDependencies > 0) { | |
| 669 | - return; | |
| 670 | - } | |
| 671 | - function doRun() { | |
| 672 | - if (calledRun) return; | |
| 673 | - calledRun = true; | |
| 674 | - Module["calledRun"] = true; | |
| 675 | - if (ABORT) return; | |
| 676 | - initRuntime(); | |
| 677 | - readyPromiseResolve(Module); | |
| 678 | - if (Module["onRuntimeInitialized"]) Module["onRuntimeInitialized"](); | |
| 679 | - postRun(); | |
| 680 | - } | |
| 681 | - if (Module["setStatus"]) { | |
| 682 | - Module["setStatus"]("Running..."); | |
| 683 | - setTimeout(function() { | |
| 684 | - setTimeout(function() { | |
| 685 | - Module["setStatus"](""); | |
| 686 | - }, 1); | |
| 687 | - doRun(); | |
| 688 | - }, 1); | |
| 689 | - } else { | |
| 690 | - doRun(); | |
| 691 | - } | |
| 826 | +// try this again later, after new deps are fulfilled | |
| 827 | +function run() { | |
| 828 | + if (runDependencies > 0) { | |
| 829 | + return; | |
| 830 | + } | |
| 831 | + if (!calledPrerun) { | |
| 832 | + calledPrerun = 1; | |
| 833 | + preRun(); | |
| 834 | + // a preRun added a dependency, run will be called later | |
| 835 | + if (runDependencies > 0) { | |
| 836 | + return; | |
| 837 | + } | |
| 838 | + } | |
| 839 | + function doRun() { | |
| 840 | + // run may have just been called through dependencies being fulfilled just in this very frame, | |
| 841 | + // or while the async setStatus time below was happening | |
| 842 | + if (calledRun) return; | |
| 843 | + calledRun = 1; | |
| 844 | + Module["calledRun"] = 1; | |
| 845 | + if (ABORT) return; | |
| 846 | + initRuntime(); | |
| 847 | + readyPromiseResolve(Module); | |
| 848 | + Module["onRuntimeInitialized"]?.(); | |
| 849 | + postRun(); | |
| 850 | + } | |
| 851 | + if (Module["setStatus"]) { | |
| 852 | + Module["setStatus"]("Running..."); | |
| 853 | + setTimeout(() => { | |
| 854 | + setTimeout(() => Module["setStatus"](""), 1); | |
| 855 | + doRun(); | |
| 856 | + }, 1); | |
| 857 | + } else { | |
| 858 | + doRun(); | |
| 859 | + } | |
| 692 | 860 | } |
| 693 | 861 | |
| 694 | 862 | if (Module["preInit"]) { |
| 695 | - if (typeof Module["preInit"] == "function") Module["preInit"] = [ Module["preInit"] ]; | |
| 696 | - while (Module["preInit"].length > 0) { | |
| 697 | - Module["preInit"].pop()(); | |
| 698 | - } | |
| 863 | + if (typeof Module["preInit"] == "function") Module["preInit"] = [ Module["preInit"] ]; | |
| 864 | + while (Module["preInit"].length > 0) { | |
| 865 | + Module["preInit"].pop()(); | |
| 866 | + } | |
| 699 | 867 | } |
| 700 | 868 | |
| 701 | 869 | run(); |
| 702 | 870 | |
| 871 | +// end include: postamble.js | |
| 872 | +// include: postamble_modularize.js | |
| 873 | +// In MODULARIZE mode we wrap the generated code in a factory function | |
| 874 | +// and return either the Module itself, or a promise of the module. | |
| 875 | +// We assign to the `moduleRtn` global here and configure closure to see | |
| 876 | +// this as and extern so it won't get minified. | |
| 877 | +moduleRtn = readyPromise; | |
| 878 | + | |
| 703 | 879 | |
| 704 | - return initPikchrModule.ready | |
| 880 | + return moduleRtn; | |
| 705 | 881 | } |
| 706 | 882 | ); |
| 707 | 883 | })(); |
| 708 | 884 | if (typeof exports === 'object' && typeof module === 'object') |
| 709 | 885 | module.exports = initPikchrModule; |
| 710 | 886 | else if (typeof define === 'function' && define['amd']) |
| 711 | - define([], function() { return initPikchrModule; }); | |
| 712 | -else if (typeof exports === 'object') | |
| 713 | - exports["initPikchrModule"] = initPikchrModule; | |
| 887 | + define([], () => initPikchrModule); | |
| 714 | 888 |
| --- extsrc/pikchr.js | |
| +++ extsrc/pikchr.js | |
| @@ -1,713 +1,887 @@ | |
| 1 | |
| 2 | var initPikchrModule = (() => { |
| 3 | var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined; |
| 4 | |
| 5 | return ( |
| 6 | function(config) { |
| 7 | var initPikchrModule = config || {}; |
| 8 | |
| 9 | var Module = typeof initPikchrModule != "undefined" ? initPikchrModule : {}; |
| 10 | |
| 11 | var readyPromiseResolve, readyPromiseReject; |
| 12 | |
| 13 | Module["ready"] = new Promise(function(resolve, reject) { |
| 14 | readyPromiseResolve = resolve; |
| 15 | readyPromiseReject = reject; |
| 16 | }); |
| 17 | |
| 18 | var moduleOverrides = Object.assign({}, Module); |
| 19 | |
| 20 | var arguments_ = []; |
| 21 | |
| 22 | var thisProgram = "./this.program"; |
| 23 | |
| 24 | var quit_ = (status, toThrow) => { |
| 25 | throw toThrow; |
| 26 | }; |
| 27 | |
| 28 | var ENVIRONMENT_IS_WEB = true; |
| 29 | |
| 30 | var ENVIRONMENT_IS_WORKER = false; |
| 31 | |
| 32 | var scriptDirectory = ""; |
| 33 | |
| 34 | function locateFile(path) { |
| 35 | if (Module["locateFile"]) { |
| 36 | return Module["locateFile"](path, scriptDirectory); |
| 37 | } |
| 38 | return scriptDirectory + path; |
| 39 | } |
| 40 | |
| 41 | var read_, readAsync, readBinary, setWindowTitle; |
| 42 | |
| 43 | if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { |
| 44 | if (ENVIRONMENT_IS_WORKER) { |
| 45 | scriptDirectory = self.location.href; |
| 46 | } else if (typeof document != "undefined" && document.currentScript) { |
| 47 | scriptDirectory = document.currentScript.src; |
| 48 | } |
| 49 | if (_scriptDir) { |
| 50 | scriptDirectory = _scriptDir; |
| 51 | } |
| 52 | if (scriptDirectory.indexOf("blob:") !== 0) { |
| 53 | scriptDirectory = scriptDirectory.substr(0, scriptDirectory.replace(/[?#].*/, "").lastIndexOf("/") + 1); |
| 54 | } else { |
| 55 | scriptDirectory = ""; |
| 56 | } |
| 57 | { |
| 58 | read_ = url => { |
| 59 | var xhr = new XMLHttpRequest(); |
| 60 | xhr.open("GET", url, false); |
| 61 | xhr.send(null); |
| 62 | return xhr.responseText; |
| 63 | }; |
| 64 | if (ENVIRONMENT_IS_WORKER) { |
| 65 | readBinary = url => { |
| 66 | var xhr = new XMLHttpRequest(); |
| 67 | xhr.open("GET", url, false); |
| 68 | xhr.responseType = "arraybuffer"; |
| 69 | xhr.send(null); |
| 70 | return new Uint8Array(xhr.response); |
| 71 | }; |
| 72 | } |
| 73 | readAsync = (url, onload, onerror) => { |
| 74 | var xhr = new XMLHttpRequest(); |
| 75 | xhr.open("GET", url, true); |
| 76 | xhr.responseType = "arraybuffer"; |
| 77 | xhr.onload = () => { |
| 78 | if (xhr.status == 200 || xhr.status == 0 && xhr.response) { |
| 79 | onload(xhr.response); |
| 80 | return; |
| 81 | } |
| 82 | onerror(); |
| 83 | }; |
| 84 | xhr.onerror = onerror; |
| 85 | xhr.send(null); |
| 86 | }; |
| 87 | } |
| 88 | setWindowTitle = title => document.title = title; |
| 89 | } else {} |
| 90 | |
| 91 | var out = Module["print"] || console.log.bind(console); |
| 92 | |
| 93 | var err = Module["printErr"] || console.warn.bind(console); |
| 94 | |
| 95 | Object.assign(Module, moduleOverrides); |
| 96 | |
| 97 | moduleOverrides = null; |
| 98 | |
| 99 | if (Module["arguments"]) arguments_ = Module["arguments"]; |
| 100 | |
| 101 | if (Module["thisProgram"]) thisProgram = Module["thisProgram"]; |
| 102 | |
| 103 | if (Module["quit"]) quit_ = Module["quit"]; |
| 104 | |
| 105 | var wasmBinary; |
| 106 | |
| 107 | if (Module["wasmBinary"]) wasmBinary = Module["wasmBinary"]; |
| 108 | |
| 109 | var noExitRuntime = Module["noExitRuntime"] || true; |
| 110 | |
| 111 | if (typeof WebAssembly != "object") { |
| 112 | abort("no native wasm support detected"); |
| 113 | } |
| 114 | |
| 115 | var wasmMemory; |
| 116 | |
| 117 | var ABORT = false; |
| 118 | |
| 119 | var EXITSTATUS; |
| 120 | |
| 121 | var UTF8Decoder = typeof TextDecoder != "undefined" ? new TextDecoder("utf8") : undefined; |
| 122 | |
| 123 | function UTF8ArrayToString(heapOrArray, idx, maxBytesToRead) { |
| 124 | var endIdx = idx + maxBytesToRead; |
| 125 | var endPtr = idx; |
| 126 | while (heapOrArray[endPtr] && !(endPtr >= endIdx)) ++endPtr; |
| 127 | if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) { |
| 128 | return UTF8Decoder.decode(heapOrArray.subarray(idx, endPtr)); |
| 129 | } |
| 130 | var str = ""; |
| 131 | while (idx < endPtr) { |
| 132 | var u0 = heapOrArray[idx++]; |
| 133 | if (!(u0 & 128)) { |
| 134 | str += String.fromCharCode(u0); |
| 135 | continue; |
| 136 | } |
| 137 | var u1 = heapOrArray[idx++] & 63; |
| 138 | if ((u0 & 224) == 192) { |
| 139 | str += String.fromCharCode((u0 & 31) << 6 | u1); |
| 140 | continue; |
| 141 | } |
| 142 | var u2 = heapOrArray[idx++] & 63; |
| 143 | if ((u0 & 240) == 224) { |
| 144 | u0 = (u0 & 15) << 12 | u1 << 6 | u2; |
| 145 | } else { |
| 146 | u0 = (u0 & 7) << 18 | u1 << 12 | u2 << 6 | heapOrArray[idx++] & 63; |
| 147 | } |
| 148 | if (u0 < 65536) { |
| 149 | str += String.fromCharCode(u0); |
| 150 | } else { |
| 151 | var ch = u0 - 65536; |
| 152 | str += String.fromCharCode(55296 | ch >> 10, 56320 | ch & 1023); |
| 153 | } |
| 154 | } |
| 155 | return str; |
| 156 | } |
| 157 | |
| 158 | function UTF8ToString(ptr, maxBytesToRead) { |
| 159 | return ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : ""; |
| 160 | } |
| 161 | |
| 162 | function stringToUTF8Array(str, heap, outIdx, maxBytesToWrite) { |
| 163 | if (!(maxBytesToWrite > 0)) return 0; |
| 164 | var startIdx = outIdx; |
| 165 | var endIdx = outIdx + maxBytesToWrite - 1; |
| 166 | for (var i = 0; i < str.length; ++i) { |
| 167 | var u = str.charCodeAt(i); |
| 168 | if (u >= 55296 && u <= 57343) { |
| 169 | var u1 = str.charCodeAt(++i); |
| 170 | u = 65536 + ((u & 1023) << 10) | u1 & 1023; |
| 171 | } |
| 172 | if (u <= 127) { |
| 173 | if (outIdx >= endIdx) break; |
| 174 | heap[outIdx++] = u; |
| 175 | } else if (u <= 2047) { |
| 176 | if (outIdx + 1 >= endIdx) break; |
| 177 | heap[outIdx++] = 192 | u >> 6; |
| 178 | heap[outIdx++] = 128 | u & 63; |
| 179 | } else if (u <= 65535) { |
| 180 | if (outIdx + 2 >= endIdx) break; |
| 181 | heap[outIdx++] = 224 | u >> 12; |
| 182 | heap[outIdx++] = 128 | u >> 6 & 63; |
| 183 | heap[outIdx++] = 128 | u & 63; |
| 184 | } else { |
| 185 | if (outIdx + 3 >= endIdx) break; |
| 186 | heap[outIdx++] = 240 | u >> 18; |
| 187 | heap[outIdx++] = 128 | u >> 12 & 63; |
| 188 | heap[outIdx++] = 128 | u >> 6 & 63; |
| 189 | heap[outIdx++] = 128 | u & 63; |
| 190 | } |
| 191 | } |
| 192 | heap[outIdx] = 0; |
| 193 | return outIdx - startIdx; |
| 194 | } |
| 195 | |
| 196 | function stringToUTF8(str, outPtr, maxBytesToWrite) { |
| 197 | return stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite); |
| 198 | } |
| 199 | |
| 200 | var HEAP8, HEAPU8, HEAP16, HEAPU16, HEAP32, HEAPU32, HEAPF32, HEAPF64; |
| 201 | |
| 202 | function updateMemoryViews() { |
| 203 | var b = wasmMemory.buffer; |
| 204 | Module["HEAP8"] = HEAP8 = new Int8Array(b); |
| 205 | Module["HEAP16"] = HEAP16 = new Int16Array(b); |
| 206 | Module["HEAP32"] = HEAP32 = new Int32Array(b); |
| 207 | Module["HEAPU8"] = HEAPU8 = new Uint8Array(b); |
| 208 | Module["HEAPU16"] = HEAPU16 = new Uint16Array(b); |
| 209 | Module["HEAPU32"] = HEAPU32 = new Uint32Array(b); |
| 210 | Module["HEAPF32"] = HEAPF32 = new Float32Array(b); |
| 211 | Module["HEAPF64"] = HEAPF64 = new Float64Array(b); |
| 212 | } |
| 213 | |
| 214 | var INITIAL_MEMORY = Module["INITIAL_MEMORY"] || 16777216; |
| 215 | |
| 216 | var wasmTable; |
| 217 | |
| 218 | var __ATPRERUN__ = []; |
| 219 | |
| 220 | var __ATINIT__ = []; |
| 221 | |
| 222 | var __ATPOSTRUN__ = []; |
| 223 | |
| 224 | var runtimeInitialized = false; |
| 225 | |
| 226 | function keepRuntimeAlive() { |
| 227 | return noExitRuntime; |
| 228 | } |
| 229 | |
| 230 | function preRun() { |
| 231 | if (Module["preRun"]) { |
| 232 | if (typeof Module["preRun"] == "function") Module["preRun"] = [ Module["preRun"] ]; |
| 233 | while (Module["preRun"].length) { |
| 234 | addOnPreRun(Module["preRun"].shift()); |
| 235 | } |
| 236 | } |
| 237 | callRuntimeCallbacks(__ATPRERUN__); |
| 238 | } |
| 239 | |
| 240 | function initRuntime() { |
| 241 | runtimeInitialized = true; |
| 242 | callRuntimeCallbacks(__ATINIT__); |
| 243 | } |
| 244 | |
| 245 | function postRun() { |
| 246 | if (Module["postRun"]) { |
| 247 | if (typeof Module["postRun"] == "function") Module["postRun"] = [ Module["postRun"] ]; |
| 248 | while (Module["postRun"].length) { |
| 249 | addOnPostRun(Module["postRun"].shift()); |
| 250 | } |
| 251 | } |
| 252 | callRuntimeCallbacks(__ATPOSTRUN__); |
| 253 | } |
| 254 | |
| 255 | function addOnPreRun(cb) { |
| 256 | __ATPRERUN__.unshift(cb); |
| 257 | } |
| 258 | |
| 259 | function addOnInit(cb) { |
| 260 | __ATINIT__.unshift(cb); |
| 261 | } |
| 262 | |
| 263 | function addOnPostRun(cb) { |
| 264 | __ATPOSTRUN__.unshift(cb); |
| 265 | } |
| 266 | |
| 267 | var runDependencies = 0; |
| 268 | |
| 269 | var runDependencyWatcher = null; |
| 270 | |
| 271 | var dependenciesFulfilled = null; |
| 272 | |
| 273 | function addRunDependency(id) { |
| 274 | runDependencies++; |
| 275 | if (Module["monitorRunDependencies"]) { |
| 276 | Module["monitorRunDependencies"](runDependencies); |
| 277 | } |
| 278 | } |
| 279 | |
| 280 | function removeRunDependency(id) { |
| 281 | runDependencies--; |
| 282 | if (Module["monitorRunDependencies"]) { |
| 283 | Module["monitorRunDependencies"](runDependencies); |
| 284 | } |
| 285 | if (runDependencies == 0) { |
| 286 | if (runDependencyWatcher !== null) { |
| 287 | clearInterval(runDependencyWatcher); |
| 288 | runDependencyWatcher = null; |
| 289 | } |
| 290 | if (dependenciesFulfilled) { |
| 291 | var callback = dependenciesFulfilled; |
| 292 | dependenciesFulfilled = null; |
| 293 | callback(); |
| 294 | } |
| 295 | } |
| 296 | } |
| 297 | |
| 298 | function abort(what) { |
| 299 | if (Module["onAbort"]) { |
| 300 | Module["onAbort"](what); |
| 301 | } |
| 302 | what = "Aborted(" + what + ")"; |
| 303 | err(what); |
| 304 | ABORT = true; |
| 305 | EXITSTATUS = 1; |
| 306 | what += ". Build with -sASSERTIONS for more info."; |
| 307 | var e = new WebAssembly.RuntimeError(what); |
| 308 | readyPromiseReject(e); |
| 309 | throw e; |
| 310 | } |
| 311 | |
| 312 | var dataURIPrefix = "data:application/octet-stream;base64,"; |
| 313 | |
| 314 | function isDataURI(filename) { |
| 315 | return filename.startsWith(dataURIPrefix); |
| 316 | } |
| 317 | |
| 318 | var wasmBinaryFile; |
| 319 | |
| 320 | wasmBinaryFile = "pikchr.wasm"; |
| 321 | |
| 322 | if (!isDataURI(wasmBinaryFile)) { |
| 323 | wasmBinaryFile = locateFile(wasmBinaryFile); |
| 324 | } |
| 325 | |
| 326 | function getBinary(file) { |
| 327 | try { |
| 328 | if (file == wasmBinaryFile && wasmBinary) { |
| 329 | return new Uint8Array(wasmBinary); |
| 330 | } |
| 331 | if (readBinary) { |
| 332 | return readBinary(file); |
| 333 | } |
| 334 | throw "both async and sync fetching of the wasm failed"; |
| 335 | } catch (err) { |
| 336 | abort(err); |
| 337 | } |
| 338 | } |
| 339 | |
| 340 | function getBinaryPromise() { |
| 341 | if (!wasmBinary && (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER)) { |
| 342 | if (typeof fetch == "function") { |
| 343 | return fetch(wasmBinaryFile, { |
| 344 | credentials: "same-origin" |
| 345 | }).then(function(response) { |
| 346 | if (!response["ok"]) { |
| 347 | throw "failed to load wasm binary file at '" + wasmBinaryFile + "'"; |
| 348 | } |
| 349 | return response["arrayBuffer"](); |
| 350 | }).catch(function() { |
| 351 | return getBinary(wasmBinaryFile); |
| 352 | }); |
| 353 | } |
| 354 | } |
| 355 | return Promise.resolve().then(function() { |
| 356 | return getBinary(wasmBinaryFile); |
| 357 | }); |
| 358 | } |
| 359 | |
| 360 | function createWasm() { |
| 361 | var info = { |
| 362 | "a": asmLibraryArg |
| 363 | }; |
| 364 | function receiveInstance(instance, module) { |
| 365 | var exports = instance.exports; |
| 366 | Module["asm"] = exports; |
| 367 | wasmMemory = Module["asm"]["d"]; |
| 368 | updateMemoryViews(); |
| 369 | wasmTable = Module["asm"]["g"]; |
| 370 | addOnInit(Module["asm"]["e"]); |
| 371 | removeRunDependency("wasm-instantiate"); |
| 372 | } |
| 373 | addRunDependency("wasm-instantiate"); |
| 374 | function receiveInstantiationResult(result) { |
| 375 | receiveInstance(result["instance"]); |
| 376 | } |
| 377 | function instantiateArrayBuffer(receiver) { |
| 378 | return getBinaryPromise().then(function(binary) { |
| 379 | return WebAssembly.instantiate(binary, info); |
| 380 | }).then(function(instance) { |
| 381 | return instance; |
| 382 | }).then(receiver, function(reason) { |
| 383 | err("failed to asynchronously prepare wasm: " + reason); |
| 384 | abort(reason); |
| 385 | }); |
| 386 | } |
| 387 | function instantiateAsync() { |
| 388 | if (!wasmBinary && typeof WebAssembly.instantiateStreaming == "function" && !isDataURI(wasmBinaryFile) && typeof fetch == "function") { |
| 389 | return fetch(wasmBinaryFile, { |
| 390 | credentials: "same-origin" |
| 391 | }).then(function(response) { |
| 392 | var result = WebAssembly.instantiateStreaming(response, info); |
| 393 | return result.then(receiveInstantiationResult, function(reason) { |
| 394 | err("wasm streaming compile failed: " + reason); |
| 395 | err("falling back to ArrayBuffer instantiation"); |
| 396 | return instantiateArrayBuffer(receiveInstantiationResult); |
| 397 | }); |
| 398 | }); |
| 399 | } else { |
| 400 | return instantiateArrayBuffer(receiveInstantiationResult); |
| 401 | } |
| 402 | } |
| 403 | if (Module["instantiateWasm"]) { |
| 404 | try { |
| 405 | var exports = Module["instantiateWasm"](info, receiveInstance); |
| 406 | return exports; |
| 407 | } catch (e) { |
| 408 | err("Module.instantiateWasm callback failed with error: " + e); |
| 409 | readyPromiseReject(e); |
| 410 | } |
| 411 | } |
| 412 | instantiateAsync().catch(readyPromiseReject); |
| 413 | return {}; |
| 414 | } |
| 415 | |
| 416 | var tempDouble; |
| 417 | |
| 418 | var tempI64; |
| 419 | |
| 420 | function ExitStatus(status) { |
| 421 | this.name = "ExitStatus"; |
| 422 | this.message = "Program terminated with exit(" + status + ")"; |
| 423 | this.status = status; |
| 424 | } |
| 425 | |
| 426 | function callRuntimeCallbacks(callbacks) { |
| 427 | while (callbacks.length > 0) { |
| 428 | callbacks.shift()(Module); |
| 429 | } |
| 430 | } |
| 431 | |
| 432 | function getValue(ptr, type = "i8") { |
| 433 | if (type.endsWith("*")) type = "*"; |
| 434 | switch (type) { |
| 435 | case "i1": |
| 436 | return HEAP8[ptr >> 0]; |
| 437 | |
| 438 | case "i8": |
| 439 | return HEAP8[ptr >> 0]; |
| 440 | |
| 441 | case "i16": |
| 442 | return HEAP16[ptr >> 1]; |
| 443 | |
| 444 | case "i32": |
| 445 | return HEAP32[ptr >> 2]; |
| 446 | |
| 447 | case "i64": |
| 448 | return HEAP32[ptr >> 2]; |
| 449 | |
| 450 | case "float": |
| 451 | return HEAPF32[ptr >> 2]; |
| 452 | |
| 453 | case "double": |
| 454 | return HEAPF64[ptr >> 3]; |
| 455 | |
| 456 | case "*": |
| 457 | return HEAPU32[ptr >> 2]; |
| 458 | |
| 459 | default: |
| 460 | abort("invalid type for getValue: " + type); |
| 461 | } |
| 462 | return null; |
| 463 | } |
| 464 | |
| 465 | function setValue(ptr, value, type = "i8") { |
| 466 | if (type.endsWith("*")) type = "*"; |
| 467 | switch (type) { |
| 468 | case "i1": |
| 469 | HEAP8[ptr >> 0] = value; |
| 470 | break; |
| 471 | |
| 472 | case "i8": |
| 473 | HEAP8[ptr >> 0] = value; |
| 474 | break; |
| 475 | |
| 476 | case "i16": |
| 477 | HEAP16[ptr >> 1] = value; |
| 478 | break; |
| 479 | |
| 480 | case "i32": |
| 481 | HEAP32[ptr >> 2] = value; |
| 482 | break; |
| 483 | |
| 484 | case "i64": |
| 485 | tempI64 = [ value >>> 0, (tempDouble = value, +Math.abs(tempDouble) >= 1 ? tempDouble > 0 ? (Math.min(+Math.floor(tempDouble / 4294967296), 4294967295) | 0) >>> 0 : ~~+Math.ceil((tempDouble - +(~~tempDouble >>> 0)) / 4294967296) >>> 0 : 0) ], |
| 486 | HEAP32[ptr >> 2] = tempI64[0], HEAP32[ptr + 4 >> 2] = tempI64[1]; |
| 487 | break; |
| 488 | |
| 489 | case "float": |
| 490 | HEAPF32[ptr >> 2] = value; |
| 491 | break; |
| 492 | |
| 493 | case "double": |
| 494 | HEAPF64[ptr >> 3] = value; |
| 495 | break; |
| 496 | |
| 497 | case "*": |
| 498 | HEAPU32[ptr >> 2] = value; |
| 499 | break; |
| 500 | |
| 501 | default: |
| 502 | abort("invalid type for setValue: " + type); |
| 503 | } |
| 504 | } |
| 505 | |
| 506 | function ___assert_fail(condition, filename, line, func) { |
| 507 | abort("Assertion failed: " + UTF8ToString(condition) + ", at: " + [ filename ? UTF8ToString(filename) : "unknown filename", line, func ? UTF8ToString(func) : "unknown function" ]); |
| 508 | } |
| 509 | |
| 510 | function abortOnCannotGrowMemory(requestedSize) { |
| 511 | abort("OOM"); |
| 512 | } |
| 513 | |
| 514 | function _emscripten_resize_heap(requestedSize) { |
| 515 | var oldSize = HEAPU8.length; |
| 516 | requestedSize = requestedSize >>> 0; |
| 517 | abortOnCannotGrowMemory(requestedSize); |
| 518 | } |
| 519 | |
| 520 | var SYSCALLS = { |
| 521 | varargs: undefined, |
| 522 | get: function() { |
| 523 | SYSCALLS.varargs += 4; |
| 524 | var ret = HEAP32[SYSCALLS.varargs - 4 >> 2]; |
| 525 | return ret; |
| 526 | }, |
| 527 | getStr: function(ptr) { |
| 528 | var ret = UTF8ToString(ptr); |
| 529 | return ret; |
| 530 | } |
| 531 | }; |
| 532 | |
| 533 | function _proc_exit(code) { |
| 534 | EXITSTATUS = code; |
| 535 | if (!keepRuntimeAlive()) { |
| 536 | if (Module["onExit"]) Module["onExit"](code); |
| 537 | ABORT = true; |
| 538 | } |
| 539 | quit_(code, new ExitStatus(code)); |
| 540 | } |
| 541 | |
| 542 | function exitJS(status, implicit) { |
| 543 | EXITSTATUS = status; |
| 544 | _proc_exit(status); |
| 545 | } |
| 546 | |
| 547 | var _exit = exitJS; |
| 548 | |
| 549 | function getCFunc(ident) { |
| 550 | var func = Module["_" + ident]; |
| 551 | return func; |
| 552 | } |
| 553 | |
| 554 | function writeArrayToMemory(array, buffer) { |
| 555 | HEAP8.set(array, buffer); |
| 556 | } |
| 557 | |
| 558 | function ccall(ident, returnType, argTypes, args, opts) { |
| 559 | var toC = { |
| 560 | "string": str => { |
| 561 | var ret = 0; |
| 562 | if (str !== null && str !== undefined && str !== 0) { |
| 563 | var len = (str.length << 2) + 1; |
| 564 | ret = stackAlloc(len); |
| 565 | stringToUTF8(str, ret, len); |
| 566 | } |
| 567 | return ret; |
| 568 | }, |
| 569 | "array": arr => { |
| 570 | var ret = stackAlloc(arr.length); |
| 571 | writeArrayToMemory(arr, ret); |
| 572 | return ret; |
| 573 | } |
| 574 | }; |
| 575 | function convertReturnValue(ret) { |
| 576 | if (returnType === "string") { |
| 577 | return UTF8ToString(ret); |
| 578 | } |
| 579 | if (returnType === "boolean") return Boolean(ret); |
| 580 | return ret; |
| 581 | } |
| 582 | var func = getCFunc(ident); |
| 583 | var cArgs = []; |
| 584 | var stack = 0; |
| 585 | if (args) { |
| 586 | for (var i = 0; i < args.length; i++) { |
| 587 | var converter = toC[argTypes[i]]; |
| 588 | if (converter) { |
| 589 | if (stack === 0) stack = stackSave(); |
| 590 | cArgs[i] = converter(args[i]); |
| 591 | } else { |
| 592 | cArgs[i] = args[i]; |
| 593 | } |
| 594 | } |
| 595 | } |
| 596 | var ret = func.apply(null, cArgs); |
| 597 | function onDone(ret) { |
| 598 | if (stack !== 0) stackRestore(stack); |
| 599 | return convertReturnValue(ret); |
| 600 | } |
| 601 | ret = onDone(ret); |
| 602 | return ret; |
| 603 | } |
| 604 | |
| 605 | function cwrap(ident, returnType, argTypes, opts) { |
| 606 | argTypes = argTypes || []; |
| 607 | var numericArgs = argTypes.every(type => type === "number" || type === "boolean"); |
| 608 | var numericRet = returnType !== "string"; |
| 609 | if (numericRet && numericArgs && !opts) { |
| 610 | return getCFunc(ident); |
| 611 | } |
| 612 | return function() { |
| 613 | return ccall(ident, returnType, argTypes, arguments, opts); |
| 614 | }; |
| 615 | } |
| 616 | |
| 617 | var asmLibraryArg = { |
| 618 | "a": ___assert_fail, |
| 619 | "b": _emscripten_resize_heap, |
| 620 | "c": _exit |
| 621 | }; |
| 622 | |
| 623 | var asm = createWasm(); |
| 624 | |
| 625 | var ___wasm_call_ctors = Module["___wasm_call_ctors"] = function() { |
| 626 | return (___wasm_call_ctors = Module["___wasm_call_ctors"] = Module["asm"]["e"]).apply(null, arguments); |
| 627 | }; |
| 628 | |
| 629 | var _pikchr = Module["_pikchr"] = function() { |
| 630 | return (_pikchr = Module["_pikchr"] = Module["asm"]["f"]).apply(null, arguments); |
| 631 | }; |
| 632 | |
| 633 | var stackSave = Module["stackSave"] = function() { |
| 634 | return (stackSave = Module["stackSave"] = Module["asm"]["h"]).apply(null, arguments); |
| 635 | }; |
| 636 | |
| 637 | var stackRestore = Module["stackRestore"] = function() { |
| 638 | return (stackRestore = Module["stackRestore"] = Module["asm"]["i"]).apply(null, arguments); |
| 639 | }; |
| 640 | |
| 641 | var stackAlloc = Module["stackAlloc"] = function() { |
| 642 | return (stackAlloc = Module["stackAlloc"] = Module["asm"]["j"]).apply(null, arguments); |
| 643 | }; |
| 644 | |
| 645 | Module["stackSave"] = stackSave; |
| 646 | |
| 647 | Module["stackRestore"] = stackRestore; |
| 648 | |
| 649 | Module["cwrap"] = cwrap; |
| 650 | |
| 651 | Module["setValue"] = setValue; |
| 652 | |
| 653 | Module["getValue"] = getValue; |
| 654 | |
| 655 | var calledRun; |
| 656 | |
| 657 | dependenciesFulfilled = function runCaller() { |
| 658 | if (!calledRun) run(); |
| 659 | if (!calledRun) dependenciesFulfilled = runCaller; |
| 660 | }; |
| 661 | |
| 662 | function run(args) { |
| 663 | args = args || arguments_; |
| 664 | if (runDependencies > 0) { |
| 665 | return; |
| 666 | } |
| 667 | preRun(); |
| 668 | if (runDependencies > 0) { |
| 669 | return; |
| 670 | } |
| 671 | function doRun() { |
| 672 | if (calledRun) return; |
| 673 | calledRun = true; |
| 674 | Module["calledRun"] = true; |
| 675 | if (ABORT) return; |
| 676 | initRuntime(); |
| 677 | readyPromiseResolve(Module); |
| 678 | if (Module["onRuntimeInitialized"]) Module["onRuntimeInitialized"](); |
| 679 | postRun(); |
| 680 | } |
| 681 | if (Module["setStatus"]) { |
| 682 | Module["setStatus"]("Running..."); |
| 683 | setTimeout(function() { |
| 684 | setTimeout(function() { |
| 685 | Module["setStatus"](""); |
| 686 | }, 1); |
| 687 | doRun(); |
| 688 | }, 1); |
| 689 | } else { |
| 690 | doRun(); |
| 691 | } |
| 692 | } |
| 693 | |
| 694 | if (Module["preInit"]) { |
| 695 | if (typeof Module["preInit"] == "function") Module["preInit"] = [ Module["preInit"] ]; |
| 696 | while (Module["preInit"].length > 0) { |
| 697 | Module["preInit"].pop()(); |
| 698 | } |
| 699 | } |
| 700 | |
| 701 | run(); |
| 702 | |
| 703 | |
| 704 | return initPikchrModule.ready |
| 705 | } |
| 706 | ); |
| 707 | })(); |
| 708 | if (typeof exports === 'object' && typeof module === 'object') |
| 709 | module.exports = initPikchrModule; |
| 710 | else if (typeof define === 'function' && define['amd']) |
| 711 | define([], function() { return initPikchrModule; }); |
| 712 | else if (typeof exports === 'object') |
| 713 | exports["initPikchrModule"] = initPikchrModule; |
| 714 |
| --- extsrc/pikchr.js | |
| +++ extsrc/pikchr.js | |
| @@ -1,713 +1,887 @@ | |
| 1 | |
| 2 | var initPikchrModule = (() => { |
| 3 | var _scriptName = typeof document != 'undefined' ? document.currentScript?.src : undefined; |
| 4 | |
| 5 | return ( |
| 6 | function(moduleArg = {}) { |
| 7 | var moduleRtn; |
| 8 | |
| 9 | // include: shell.js |
| 10 | // The Module object: Our interface to the outside world. We import |
| 11 | // and export values on it. There are various ways Module can be used: |
| 12 | // 1. Not defined. We create it here |
| 13 | // 2. A function parameter, function(moduleArg) => Promise<Module> |
| 14 | // 3. pre-run appended it, var Module = {}; ..generated code.. |
| 15 | // 4. External script tag defines var Module. |
| 16 | // We need to check if Module already exists (e.g. case 3 above). |
| 17 | // Substitution will be replaced with actual code on later stage of the build, |
| 18 | // this way Closure Compiler will not mangle it (e.g. case 4. above). |
| 19 | // Note that if you want to run closure, and also to use Module |
| 20 | // after the generated code, you will need to define var Module = {}; |
| 21 | // before the code. Then that object will be used in the code, and you |
| 22 | // can continue to use Module afterwards as well. |
| 23 | var Module = moduleArg; |
| 24 | |
| 25 | // Set up the promise that indicates the Module is initialized |
| 26 | var readyPromiseResolve, readyPromiseReject; |
| 27 | |
| 28 | var readyPromise = new Promise((resolve, reject) => { |
| 29 | readyPromiseResolve = resolve; |
| 30 | readyPromiseReject = reject; |
| 31 | }); |
| 32 | |
| 33 | // Determine the runtime environment we are in. You can customize this by |
| 34 | // setting the ENVIRONMENT setting at compile time (see settings.js). |
| 35 | var ENVIRONMENT_IS_WEB = true; |
| 36 | |
| 37 | var ENVIRONMENT_IS_WORKER = false; |
| 38 | |
| 39 | // --pre-jses are emitted after the Module integration code, so that they can |
| 40 | // refer to Module (if they choose; they can also define Module) |
| 41 | // Sometimes an existing Module object exists with properties |
| 42 | // meant to overwrite the default module functionality. Here |
| 43 | // we collect those properties and reapply _after_ we configure |
| 44 | // the current environment's defaults to avoid having to be so |
| 45 | // defensive during initialization. |
| 46 | var moduleOverrides = Object.assign({}, Module); |
| 47 | |
| 48 | var arguments_ = []; |
| 49 | |
| 50 | var thisProgram = "./this.program"; |
| 51 | |
| 52 | var quit_ = (status, toThrow) => { |
| 53 | throw toThrow; |
| 54 | }; |
| 55 | |
| 56 | // `/` should be present at the end if `scriptDirectory` is not empty |
| 57 | var scriptDirectory = ""; |
| 58 | |
| 59 | function locateFile(path) { |
| 60 | if (Module["locateFile"]) { |
| 61 | return Module["locateFile"](path, scriptDirectory); |
| 62 | } |
| 63 | return scriptDirectory + path; |
| 64 | } |
| 65 | |
| 66 | // Hooks that are implemented differently in different runtime environments. |
| 67 | var readAsync, readBinary; |
| 68 | |
| 69 | // Note that this includes Node.js workers when relevant (pthreads is enabled). |
| 70 | // Node.js workers are detected as a combination of ENVIRONMENT_IS_WORKER and |
| 71 | // ENVIRONMENT_IS_NODE. |
| 72 | if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) { |
| 73 | if (ENVIRONMENT_IS_WORKER) { |
| 74 | // Check worker, not web, since window could be polyfilled |
| 75 | scriptDirectory = self.location.href; |
| 76 | } else if (typeof document != "undefined" && document.currentScript) { |
| 77 | // web |
| 78 | scriptDirectory = document.currentScript.src; |
| 79 | } |
| 80 | // When MODULARIZE, this JS may be executed later, after document.currentScript |
| 81 | // is gone, so we saved it, and we use it here instead of any other info. |
| 82 | if (_scriptName) { |
| 83 | scriptDirectory = _scriptName; |
| 84 | } |
| 85 | // blob urls look like blob:http://site.com/etc/etc and we cannot infer anything from them. |
| 86 | // otherwise, slice off the final part of the url to find the script directory. |
| 87 | // if scriptDirectory does not contain a slash, lastIndexOf will return -1, |
| 88 | // and scriptDirectory will correctly be replaced with an empty string. |
| 89 | // If scriptDirectory contains a query (starting with ?) or a fragment (starting with #), |
| 90 | // they are removed because they could contain a slash. |
| 91 | if (scriptDirectory.startsWith("blob:")) { |
| 92 | scriptDirectory = ""; |
| 93 | } else { |
| 94 | scriptDirectory = scriptDirectory.substr(0, scriptDirectory.replace(/[?#].*/, "").lastIndexOf("/") + 1); |
| 95 | } |
| 96 | { |
| 97 | // include: web_or_worker_shell_read.js |
| 98 | readAsync = url => fetch(url, { |
| 99 | credentials: "same-origin" |
| 100 | }).then(response => { |
| 101 | if (response.ok) { |
| 102 | return response.arrayBuffer(); |
| 103 | } |
| 104 | return Promise.reject(new Error(response.status + " : " + response.url)); |
| 105 | }); |
| 106 | } |
| 107 | } else // end include: web_or_worker_shell_read.js |
| 108 | {} |
| 109 | |
| 110 | var out = Module["print"] || console.log.bind(console); |
| 111 | |
| 112 | var err = Module["printErr"] || console.error.bind(console); |
| 113 | |
| 114 | // Merge back in the overrides |
| 115 | Object.assign(Module, moduleOverrides); |
| 116 | |
| 117 | // Free the object hierarchy contained in the overrides, this lets the GC |
| 118 | // reclaim data used. |
| 119 | moduleOverrides = null; |
| 120 | |
| 121 | // Emit code to handle expected values on the Module object. This applies Module.x |
| 122 | // to the proper local x. This has two benefits: first, we only emit it if it is |
| 123 | // expected to arrive, and second, by using a local everywhere else that can be |
| 124 | // minified. |
| 125 | if (Module["arguments"]) arguments_ = Module["arguments"]; |
| 126 | |
| 127 | if (Module["thisProgram"]) thisProgram = Module["thisProgram"]; |
| 128 | |
| 129 | // perform assertions in shell.js after we set up out() and err(), as otherwise if an assertion fails it cannot print the message |
| 130 | // end include: shell.js |
| 131 | // include: preamble.js |
| 132 | // === Preamble library stuff === |
| 133 | // Documentation for the public APIs defined in this file must be updated in: |
| 134 | // site/source/docs/api_reference/preamble.js.rst |
| 135 | // A prebuilt local version of the documentation is available at: |
| 136 | // site/build/text/docs/api_reference/preamble.js.txt |
| 137 | // You can also build docs locally as HTML or other formats in site/ |
| 138 | // An online HTML version (which may be of a different version of Emscripten) |
| 139 | // is up at http://kripken.github.io/emscripten-site/docs/api_reference/preamble.js.html |
| 140 | var wasmBinary = Module["wasmBinary"]; |
| 141 | |
| 142 | // Wasm globals |
| 143 | var wasmMemory; |
| 144 | |
| 145 | //======================================== |
| 146 | // Runtime essentials |
| 147 | //======================================== |
| 148 | // whether we are quitting the application. no code should run after this. |
| 149 | // set in exit() and abort() |
| 150 | var ABORT = false; |
| 151 | |
| 152 | // set by exit() and abort(). Passed to 'onExit' handler. |
| 153 | // NOTE: This is also used as the process return code code in shell environments |
| 154 | // but only when noExitRuntime is false. |
| 155 | var EXITSTATUS; |
| 156 | |
| 157 | // Memory management |
| 158 | var /** @type {!Int8Array} */ HEAP8, /** @type {!Uint8Array} */ HEAPU8, /** @type {!Int16Array} */ HEAP16, /** @type {!Uint16Array} */ HEAPU16, /** @type {!Int32Array} */ HEAP32, /** @type {!Uint32Array} */ HEAPU32, /** @type {!Float32Array} */ HEAPF32, /** @type {!Float64Array} */ HEAPF64; |
| 159 | |
| 160 | // include: runtime_shared.js |
| 161 | function updateMemoryViews() { |
| 162 | var b = wasmMemory.buffer; |
| 163 | Module["HEAP8"] = HEAP8 = new Int8Array(b); |
| 164 | Module["HEAP16"] = HEAP16 = new Int16Array(b); |
| 165 | Module["HEAPU8"] = HEAPU8 = new Uint8Array(b); |
| 166 | Module["HEAPU16"] = HEAPU16 = new Uint16Array(b); |
| 167 | Module["HEAP32"] = HEAP32 = new Int32Array(b); |
| 168 | Module["HEAPU32"] = HEAPU32 = new Uint32Array(b); |
| 169 | Module["HEAPF32"] = HEAPF32 = new Float32Array(b); |
| 170 | Module["HEAPF64"] = HEAPF64 = new Float64Array(b); |
| 171 | } |
| 172 | |
| 173 | // end include: runtime_shared.js |
| 174 | // include: runtime_stack_check.js |
| 175 | // end include: runtime_stack_check.js |
| 176 | var __ATPRERUN__ = []; |
| 177 | |
| 178 | // functions called before the runtime is initialized |
| 179 | var __ATINIT__ = []; |
| 180 | |
| 181 | // functions called during shutdown |
| 182 | var __ATPOSTRUN__ = []; |
| 183 | |
| 184 | // functions called after the main() is called |
| 185 | var runtimeInitialized = false; |
| 186 | |
| 187 | function preRun() { |
| 188 | var preRuns = Module["preRun"]; |
| 189 | if (preRuns) { |
| 190 | if (typeof preRuns == "function") preRuns = [ preRuns ]; |
| 191 | preRuns.forEach(addOnPreRun); |
| 192 | } |
| 193 | callRuntimeCallbacks(__ATPRERUN__); |
| 194 | } |
| 195 | |
| 196 | function initRuntime() { |
| 197 | runtimeInitialized = true; |
| 198 | callRuntimeCallbacks(__ATINIT__); |
| 199 | } |
| 200 | |
| 201 | function postRun() { |
| 202 | var postRuns = Module["postRun"]; |
| 203 | if (postRuns) { |
| 204 | if (typeof postRuns == "function") postRuns = [ postRuns ]; |
| 205 | postRuns.forEach(addOnPostRun); |
| 206 | } |
| 207 | callRuntimeCallbacks(__ATPOSTRUN__); |
| 208 | } |
| 209 | |
| 210 | function addOnPreRun(cb) { |
| 211 | __ATPRERUN__.unshift(cb); |
| 212 | } |
| 213 | |
| 214 | function addOnInit(cb) { |
| 215 | __ATINIT__.unshift(cb); |
| 216 | } |
| 217 | |
| 218 | function addOnPostRun(cb) { |
| 219 | __ATPOSTRUN__.unshift(cb); |
| 220 | } |
| 221 | |
| 222 | // include: runtime_math.js |
| 223 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul |
| 224 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/fround |
| 225 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/clz32 |
| 226 | // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc |
| 227 | // end include: runtime_math.js |
| 228 | // A counter of dependencies for calling run(). If we need to |
| 229 | // do asynchronous work before running, increment this and |
| 230 | // decrement it. Incrementing must happen in a place like |
| 231 | // Module.preRun (used by emcc to add file preloading). |
| 232 | // Note that you can add dependencies in preRun, even though |
| 233 | // it happens right before run - run will be postponed until |
| 234 | // the dependencies are met. |
| 235 | var runDependencies = 0; |
| 236 | |
| 237 | var runDependencyWatcher = null; |
| 238 | |
| 239 | var dependenciesFulfilled = null; |
| 240 | |
| 241 | function addRunDependency(id) { |
| 242 | runDependencies++; |
| 243 | Module["monitorRunDependencies"]?.(runDependencies); |
| 244 | } |
| 245 | |
| 246 | function removeRunDependency(id) { |
| 247 | runDependencies--; |
| 248 | Module["monitorRunDependencies"]?.(runDependencies); |
| 249 | if (runDependencies == 0) { |
| 250 | if (runDependencyWatcher !== null) { |
| 251 | clearInterval(runDependencyWatcher); |
| 252 | runDependencyWatcher = null; |
| 253 | } |
| 254 | if (dependenciesFulfilled) { |
| 255 | var callback = dependenciesFulfilled; |
| 256 | dependenciesFulfilled = null; |
| 257 | callback(); |
| 258 | } |
| 259 | } |
| 260 | } |
| 261 | |
| 262 | /** @param {string|number=} what */ function abort(what) { |
| 263 | Module["onAbort"]?.(what); |
| 264 | what = "Aborted(" + what + ")"; |
| 265 | // TODO(sbc): Should we remove printing and leave it up to whoever |
| 266 | // catches the exception? |
| 267 | err(what); |
| 268 | ABORT = true; |
| 269 | what += ". Build with -sASSERTIONS for more info."; |
| 270 | // Use a wasm runtime error, because a JS error might be seen as a foreign |
| 271 | // exception, which means we'd run destructors on it. We need the error to |
| 272 | // simply make the program stop. |
| 273 | // FIXME This approach does not work in Wasm EH because it currently does not assume |
| 274 | // all RuntimeErrors are from traps; it decides whether a RuntimeError is from |
| 275 | // a trap or not based on a hidden field within the object. So at the moment |
| 276 | // we don't have a way of throwing a wasm trap from JS. TODO Make a JS API that |
| 277 | // allows this in the wasm spec. |
| 278 | // Suppress closure compiler warning here. Closure compiler's builtin extern |
| 279 | // definition for WebAssembly.RuntimeError claims it takes no arguments even |
| 280 | // though it can. |
| 281 | // TODO(https://github.com/google/closure-compiler/pull/3913): Remove if/when upstream closure gets fixed. |
| 282 | /** @suppress {checkTypes} */ var e = new WebAssembly.RuntimeError(what); |
| 283 | readyPromiseReject(e); |
| 284 | // Throw the error whether or not MODULARIZE is set because abort is used |
| 285 | // in code paths apart from instantiation where an exception is expected |
| 286 | // to be thrown when abort is called. |
| 287 | throw e; |
| 288 | } |
| 289 | |
| 290 | // include: memoryprofiler.js |
| 291 | // end include: memoryprofiler.js |
| 292 | // include: URIUtils.js |
| 293 | // Prefix of data URIs emitted by SINGLE_FILE and related options. |
| 294 | var dataURIPrefix = "data:application/octet-stream;base64,"; |
| 295 | |
| 296 | /** |
| 297 | * Indicates whether filename is a base64 data URI. |
| 298 | * @noinline |
| 299 | */ var isDataURI = filename => filename.startsWith(dataURIPrefix); |
| 300 | |
| 301 | // end include: URIUtils.js |
| 302 | // include: runtime_exceptions.js |
| 303 | // end include: runtime_exceptions.js |
| 304 | function findWasmBinary() { |
| 305 | var f = "pikchr-v2813665466.wasm"; |
| 306 | if (!isDataURI(f)) { |
| 307 | return locateFile(f); |
| 308 | } |
| 309 | return f; |
| 310 | } |
| 311 | |
| 312 | var wasmBinaryFile; |
| 313 | |
| 314 | function getBinarySync(file) { |
| 315 | if (file == wasmBinaryFile && wasmBinary) { |
| 316 | return new Uint8Array(wasmBinary); |
| 317 | } |
| 318 | if (readBinary) { |
| 319 | return readBinary(file); |
| 320 | } |
| 321 | throw "both async and sync fetching of the wasm failed"; |
| 322 | } |
| 323 | |
| 324 | function getBinaryPromise(binaryFile) { |
| 325 | // If we don't have the binary yet, load it asynchronously using readAsync. |
| 326 | if (!wasmBinary) { |
| 327 | // Fetch the binary using readAsync |
| 328 | return readAsync(binaryFile).then(response => new Uint8Array(/** @type{!ArrayBuffer} */ (response)), // Fall back to getBinarySync if readAsync fails |
| 329 | () => getBinarySync(binaryFile)); |
| 330 | } |
| 331 | // Otherwise, getBinarySync should be able to get it synchronously |
| 332 | return Promise.resolve().then(() => getBinarySync(binaryFile)); |
| 333 | } |
| 334 | |
| 335 | function instantiateArrayBuffer(binaryFile, imports, receiver) { |
| 336 | return getBinaryPromise(binaryFile).then(binary => WebAssembly.instantiate(binary, imports)).then(receiver, reason => { |
| 337 | err(`failed to asynchronously prepare wasm: ${reason}`); |
| 338 | abort(reason); |
| 339 | }); |
| 340 | } |
| 341 | |
| 342 | function instantiateAsync(binary, binaryFile, imports, callback) { |
| 343 | if (!binary && typeof WebAssembly.instantiateStreaming == "function" && !isDataURI(binaryFile) && typeof fetch == "function") { |
| 344 | return fetch(binaryFile, { |
| 345 | credentials: "same-origin" |
| 346 | }).then(response => { |
| 347 | // Suppress closure warning here since the upstream definition for |
| 348 | // instantiateStreaming only allows Promise<Repsponse> rather than |
| 349 | // an actual Response. |
| 350 | // TODO(https://github.com/google/closure-compiler/pull/3913): Remove if/when upstream closure is fixed. |
| 351 | /** @suppress {checkTypes} */ var result = WebAssembly.instantiateStreaming(response, imports); |
| 352 | return result.then(callback, function(reason) { |
| 353 | // We expect the most common failure cause to be a bad MIME type for the binary, |
| 354 | // in which case falling back to ArrayBuffer instantiation should work. |
| 355 | err(`wasm streaming compile failed: ${reason}`); |
| 356 | err("falling back to ArrayBuffer instantiation"); |
| 357 | return instantiateArrayBuffer(binaryFile, imports, callback); |
| 358 | }); |
| 359 | }); |
| 360 | } |
| 361 | return instantiateArrayBuffer(binaryFile, imports, callback); |
| 362 | } |
| 363 | |
| 364 | function getWasmImports() { |
| 365 | // prepare imports |
| 366 | return { |
| 367 | "a": wasmImports |
| 368 | }; |
| 369 | } |
| 370 | |
| 371 | // Create the wasm instance. |
| 372 | // Receives the wasm imports, returns the exports. |
| 373 | function createWasm() { |
| 374 | var info = getWasmImports(); |
| 375 | // Load the wasm module and create an instance of using native support in the JS engine. |
| 376 | // handle a generated wasm instance, receiving its exports and |
| 377 | // performing other necessary setup |
| 378 | /** @param {WebAssembly.Module=} module*/ function receiveInstance(instance, module) { |
| 379 | wasmExports = instance.exports; |
| 380 | wasmMemory = wasmExports["d"]; |
| 381 | updateMemoryViews(); |
| 382 | addOnInit(wasmExports["e"]); |
| 383 | removeRunDependency("wasm-instantiate"); |
| 384 | return wasmExports; |
| 385 | } |
| 386 | // wait for the pthread pool (if any) |
| 387 | addRunDependency("wasm-instantiate"); |
| 388 | // Prefer streaming instantiation if available. |
| 389 | function receiveInstantiationResult(result) { |
| 390 | // 'result' is a ResultObject object which has both the module and instance. |
| 391 | // receiveInstance() will swap in the exports (to Module.asm) so they can be called |
| 392 | // TODO: Due to Closure regression https://github.com/google/closure-compiler/issues/3193, the above line no longer optimizes out down to the following line. |
| 393 | // When the regression is fixed, can restore the above PTHREADS-enabled path. |
| 394 | receiveInstance(result["instance"]); |
| 395 | } |
| 396 | // User shell pages can write their own Module.instantiateWasm = function(imports, successCallback) callback |
| 397 | // to manually instantiate the Wasm module themselves. This allows pages to |
| 398 | // run the instantiation parallel to any other async startup actions they are |
| 399 | // performing. |
| 400 | // Also pthreads and wasm workers initialize the wasm instance through this |
| 401 | // path. |
| 402 | if (Module["instantiateWasm"]) { |
| 403 | try { |
| 404 | return Module["instantiateWasm"](info, receiveInstance); |
| 405 | } catch (e) { |
| 406 | err(`Module.instantiateWasm callback failed with error: ${e}`); |
| 407 | // If instantiation fails, reject the module ready promise. |
| 408 | readyPromiseReject(e); |
| 409 | } |
| 410 | } |
| 411 | wasmBinaryFile ??= findWasmBinary(); |
| 412 | // If instantiation fails, reject the module ready promise. |
| 413 | instantiateAsync(wasmBinary, wasmBinaryFile, info, receiveInstantiationResult).catch(readyPromiseReject); |
| 414 | return {}; |
| 415 | } |
| 416 | |
| 417 | // include: runtime_debug.js |
| 418 | // end include: runtime_debug.js |
| 419 | // === Body === |
| 420 | // end include: preamble.js |
| 421 | /** @constructor */ function ExitStatus(status) { |
| 422 | this.name = "ExitStatus"; |
| 423 | this.message = `Program terminated with exit(${status})`; |
| 424 | this.status = status; |
| 425 | } |
| 426 | |
| 427 | var callRuntimeCallbacks = callbacks => { |
| 428 | // Pass the module as the first argument. |
| 429 | callbacks.forEach(f => f(Module)); |
| 430 | }; |
| 431 | |
| 432 | /** |
| 433 | * @param {number} ptr |
| 434 | * @param {string} type |
| 435 | */ function getValue(ptr, type = "i8") { |
| 436 | if (type.endsWith("*")) type = "*"; |
| 437 | switch (type) { |
| 438 | case "i1": |
| 439 | return HEAP8[ptr]; |
| 440 | |
| 441 | case "i8": |
| 442 | return HEAP8[ptr]; |
| 443 | |
| 444 | case "i16": |
| 445 | return HEAP16[((ptr) >> 1)]; |
| 446 | |
| 447 | case "i32": |
| 448 | return HEAP32[((ptr) >> 2)]; |
| 449 | |
| 450 | case "i64": |
| 451 | abort("to do getValue(i64) use WASM_BIGINT"); |
| 452 | |
| 453 | case "float": |
| 454 | return HEAPF32[((ptr) >> 2)]; |
| 455 | |
| 456 | case "double": |
| 457 | return HEAPF64[((ptr) >> 3)]; |
| 458 | |
| 459 | case "*": |
| 460 | return HEAPU32[((ptr) >> 2)]; |
| 461 | |
| 462 | default: |
| 463 | abort(`invalid type for getValue: ${type}`); |
| 464 | } |
| 465 | } |
| 466 | |
| 467 | var noExitRuntime = Module["noExitRuntime"] || true; |
| 468 | |
| 469 | /** |
| 470 | * @param {number} ptr |
| 471 | * @param {number} value |
| 472 | * @param {string} type |
| 473 | */ function setValue(ptr, value, type = "i8") { |
| 474 | if (type.endsWith("*")) type = "*"; |
| 475 | switch (type) { |
| 476 | case "i1": |
| 477 | HEAP8[ptr] = value; |
| 478 | break; |
| 479 | |
| 480 | case "i8": |
| 481 | HEAP8[ptr] = value; |
| 482 | break; |
| 483 | |
| 484 | case "i16": |
| 485 | HEAP16[((ptr) >> 1)] = value; |
| 486 | break; |
| 487 | |
| 488 | case "i32": |
| 489 | HEAP32[((ptr) >> 2)] = value; |
| 490 | break; |
| 491 | |
| 492 | case "i64": |
| 493 | abort("to do setValue(i64) use WASM_BIGINT"); |
| 494 | |
| 495 | case "float": |
| 496 | HEAPF32[((ptr) >> 2)] = value; |
| 497 | break; |
| 498 | |
| 499 | case "double": |
| 500 | HEAPF64[((ptr) >> 3)] = value; |
| 501 | break; |
| 502 | |
| 503 | case "*": |
| 504 | HEAPU32[((ptr) >> 2)] = value; |
| 505 | break; |
| 506 | |
| 507 | default: |
| 508 | abort(`invalid type for setValue: ${type}`); |
| 509 | } |
| 510 | } |
| 511 | |
| 512 | var stackRestore = val => __emscripten_stack_restore(val); |
| 513 | |
| 514 | var stackSave = () => _emscripten_stack_get_current(); |
| 515 | |
| 516 | var UTF8Decoder = typeof TextDecoder != "undefined" ? new TextDecoder : undefined; |
| 517 | |
| 518 | /** |
| 519 | * Given a pointer 'idx' to a null-terminated UTF8-encoded string in the given |
| 520 | * array that contains uint8 values, returns a copy of that string as a |
| 521 | * Javascript String object. |
| 522 | * heapOrArray is either a regular array, or a JavaScript typed array view. |
| 523 | * @param {number=} idx |
| 524 | * @param {number=} maxBytesToRead |
| 525 | * @return {string} |
| 526 | */ var UTF8ArrayToString = (heapOrArray, idx = 0, maxBytesToRead = NaN) => { |
| 527 | var endIdx = idx + maxBytesToRead; |
| 528 | var endPtr = idx; |
| 529 | // TextDecoder needs to know the byte length in advance, it doesn't stop on |
| 530 | // null terminator by itself. Also, use the length info to avoid running tiny |
| 531 | // strings through TextDecoder, since .subarray() allocates garbage. |
| 532 | // (As a tiny code save trick, compare endPtr against endIdx using a negation, |
| 533 | // so that undefined/NaN means Infinity) |
| 534 | while (heapOrArray[endPtr] && !(endPtr >= endIdx)) ++endPtr; |
| 535 | if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) { |
| 536 | return UTF8Decoder.decode(heapOrArray.subarray(idx, endPtr)); |
| 537 | } |
| 538 | var str = ""; |
| 539 | // If building with TextDecoder, we have already computed the string length |
| 540 | // above, so test loop end condition against that |
| 541 | while (idx < endPtr) { |
| 542 | // For UTF8 byte structure, see: |
| 543 | // http://en.wikipedia.org/wiki/UTF-8#Description |
| 544 | // https://www.ietf.org/rfc/rfc2279.txt |
| 545 | // https://tools.ietf.org/html/rfc3629 |
| 546 | var u0 = heapOrArray[idx++]; |
| 547 | if (!(u0 & 128)) { |
| 548 | str += String.fromCharCode(u0); |
| 549 | continue; |
| 550 | } |
| 551 | var u1 = heapOrArray[idx++] & 63; |
| 552 | if ((u0 & 224) == 192) { |
| 553 | str += String.fromCharCode(((u0 & 31) << 6) | u1); |
| 554 | continue; |
| 555 | } |
| 556 | var u2 = heapOrArray[idx++] & 63; |
| 557 | if ((u0 & 240) == 224) { |
| 558 | u0 = ((u0 & 15) << 12) | (u1 << 6) | u2; |
| 559 | } else { |
| 560 | u0 = ((u0 & 7) << 18) | (u1 << 12) | (u2 << 6) | (heapOrArray[idx++] & 63); |
| 561 | } |
| 562 | if (u0 < 65536) { |
| 563 | str += String.fromCharCode(u0); |
| 564 | } else { |
| 565 | var ch = u0 - 65536; |
| 566 | str += String.fromCharCode(55296 | (ch >> 10), 56320 | (ch & 1023)); |
| 567 | } |
| 568 | } |
| 569 | return str; |
| 570 | }; |
| 571 | |
| 572 | /** |
| 573 | * Given a pointer 'ptr' to a null-terminated UTF8-encoded string in the |
| 574 | * emscripten HEAP, returns a copy of that string as a Javascript String object. |
| 575 | * |
| 576 | * @param {number} ptr |
| 577 | * @param {number=} maxBytesToRead - An optional length that specifies the |
| 578 | * maximum number of bytes to read. You can omit this parameter to scan the |
| 579 | * string until the first 0 byte. If maxBytesToRead is passed, and the string |
| 580 | * at [ptr, ptr+maxBytesToReadr[ contains a null byte in the middle, then the |
| 581 | * string will cut short at that byte index (i.e. maxBytesToRead will not |
| 582 | * produce a string of exact length [ptr, ptr+maxBytesToRead[) N.B. mixing |
| 583 | * frequent uses of UTF8ToString() with and without maxBytesToRead may throw |
| 584 | * JS JIT optimizations off, so it is worth to consider consistently using one |
| 585 | * @return {string} |
| 586 | */ var UTF8ToString = (ptr, maxBytesToRead) => ptr ? UTF8ArrayToString(HEAPU8, ptr, maxBytesToRead) : ""; |
| 587 | |
| 588 | var ___assert_fail = (condition, filename, line, func) => { |
| 589 | abort(`Assertion failed: ${UTF8ToString(condition)}, at: ` + [ filename ? UTF8ToString(filename) : "unknown filename", line, func ? UTF8ToString(func) : "unknown function" ]); |
| 590 | }; |
| 591 | |
| 592 | var abortOnCannotGrowMemory = requestedSize => { |
| 593 | abort("OOM"); |
| 594 | }; |
| 595 | |
| 596 | var _emscripten_resize_heap = requestedSize => { |
| 597 | var oldSize = HEAPU8.length; |
| 598 | // With CAN_ADDRESS_2GB or MEMORY64, pointers are already unsigned. |
| 599 | requestedSize >>>= 0; |
| 600 | abortOnCannotGrowMemory(requestedSize); |
| 601 | }; |
| 602 | |
| 603 | var runtimeKeepaliveCounter = 0; |
| 604 | |
| 605 | var keepRuntimeAlive = () => noExitRuntime || runtimeKeepaliveCounter > 0; |
| 606 | |
| 607 | var _proc_exit = code => { |
| 608 | EXITSTATUS = code; |
| 609 | if (!keepRuntimeAlive()) { |
| 610 | Module["onExit"]?.(code); |
| 611 | ABORT = true; |
| 612 | } |
| 613 | quit_(code, new ExitStatus(code)); |
| 614 | }; |
| 615 | |
| 616 | /** @suppress {duplicate } */ /** @param {boolean|number=} implicit */ var exitJS = (status, implicit) => { |
| 617 | EXITSTATUS = status; |
| 618 | _proc_exit(status); |
| 619 | }; |
| 620 | |
| 621 | var _exit = exitJS; |
| 622 | |
| 623 | var getCFunc = ident => { |
| 624 | var func = Module["_" + ident]; |
| 625 | // closure exported function |
| 626 | return func; |
| 627 | }; |
| 628 | |
| 629 | var writeArrayToMemory = (array, buffer) => { |
| 630 | HEAP8.set(array, buffer); |
| 631 | }; |
| 632 | |
| 633 | var lengthBytesUTF8 = str => { |
| 634 | var len = 0; |
| 635 | for (var i = 0; i < str.length; ++i) { |
| 636 | // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code |
| 637 | // unit, not a Unicode code point of the character! So decode |
| 638 | // UTF16->UTF32->UTF8. |
| 639 | // See http://unicode.org/faq/utf_bom.html#utf16-3 |
| 640 | var c = str.charCodeAt(i); |
| 641 | // possibly a lead surrogate |
| 642 | if (c <= 127) { |
| 643 | len++; |
| 644 | } else if (c <= 2047) { |
| 645 | len += 2; |
| 646 | } else if (c >= 55296 && c <= 57343) { |
| 647 | len += 4; |
| 648 | ++i; |
| 649 | } else { |
| 650 | len += 3; |
| 651 | } |
| 652 | } |
| 653 | return len; |
| 654 | }; |
| 655 | |
| 656 | var stringToUTF8Array = (str, heap, outIdx, maxBytesToWrite) => { |
| 657 | // Parameter maxBytesToWrite is not optional. Negative values, 0, null, |
| 658 | // undefined and false each don't write out any bytes. |
| 659 | if (!(maxBytesToWrite > 0)) return 0; |
| 660 | var startIdx = outIdx; |
| 661 | var endIdx = outIdx + maxBytesToWrite - 1; |
| 662 | // -1 for string null terminator. |
| 663 | for (var i = 0; i < str.length; ++i) { |
| 664 | // Gotcha: charCodeAt returns a 16-bit word that is a UTF-16 encoded code |
| 665 | // unit, not a Unicode code point of the character! So decode |
| 666 | // UTF16->UTF32->UTF8. |
| 667 | // See http://unicode.org/faq/utf_bom.html#utf16-3 |
| 668 | // For UTF8 byte structure, see http://en.wikipedia.org/wiki/UTF-8#Description |
| 669 | // and https://www.ietf.org/rfc/rfc2279.txt |
| 670 | // and https://tools.ietf.org/html/rfc3629 |
| 671 | var u = str.charCodeAt(i); |
| 672 | // possibly a lead surrogate |
| 673 | if (u >= 55296 && u <= 57343) { |
| 674 | var u1 = str.charCodeAt(++i); |
| 675 | u = 65536 + ((u & 1023) << 10) | (u1 & 1023); |
| 676 | } |
| 677 | if (u <= 127) { |
| 678 | if (outIdx >= endIdx) break; |
| 679 | heap[outIdx++] = u; |
| 680 | } else if (u <= 2047) { |
| 681 | if (outIdx + 1 >= endIdx) break; |
| 682 | heap[outIdx++] = 192 | (u >> 6); |
| 683 | heap[outIdx++] = 128 | (u & 63); |
| 684 | } else if (u <= 65535) { |
| 685 | if (outIdx + 2 >= endIdx) break; |
| 686 | heap[outIdx++] = 224 | (u >> 12); |
| 687 | heap[outIdx++] = 128 | ((u >> 6) & 63); |
| 688 | heap[outIdx++] = 128 | (u & 63); |
| 689 | } else { |
| 690 | if (outIdx + 3 >= endIdx) break; |
| 691 | heap[outIdx++] = 240 | (u >> 18); |
| 692 | heap[outIdx++] = 128 | ((u >> 12) & 63); |
| 693 | heap[outIdx++] = 128 | ((u >> 6) & 63); |
| 694 | heap[outIdx++] = 128 | (u & 63); |
| 695 | } |
| 696 | } |
| 697 | // Null-terminate the pointer to the buffer. |
| 698 | heap[outIdx] = 0; |
| 699 | return outIdx - startIdx; |
| 700 | }; |
| 701 | |
| 702 | var stringToUTF8 = (str, outPtr, maxBytesToWrite) => stringToUTF8Array(str, HEAPU8, outPtr, maxBytesToWrite); |
| 703 | |
| 704 | var stackAlloc = sz => __emscripten_stack_alloc(sz); |
| 705 | |
| 706 | var stringToUTF8OnStack = str => { |
| 707 | var size = lengthBytesUTF8(str) + 1; |
| 708 | var ret = stackAlloc(size); |
| 709 | stringToUTF8(str, ret, size); |
| 710 | return ret; |
| 711 | }; |
| 712 | |
| 713 | /** |
| 714 | * @param {string|null=} returnType |
| 715 | * @param {Array=} argTypes |
| 716 | * @param {Arguments|Array=} args |
| 717 | * @param {Object=} opts |
| 718 | */ var ccall = (ident, returnType, argTypes, args, opts) => { |
| 719 | // For fast lookup of conversion functions |
| 720 | var toC = { |
| 721 | "string": str => { |
| 722 | var ret = 0; |
| 723 | if (str !== null && str !== undefined && str !== 0) { |
| 724 | // null string |
| 725 | ret = stringToUTF8OnStack(str); |
| 726 | } |
| 727 | return ret; |
| 728 | }, |
| 729 | "array": arr => { |
| 730 | var ret = stackAlloc(arr.length); |
| 731 | writeArrayToMemory(arr, ret); |
| 732 | return ret; |
| 733 | } |
| 734 | }; |
| 735 | function convertReturnValue(ret) { |
| 736 | if (returnType === "string") { |
| 737 | return UTF8ToString(ret); |
| 738 | } |
| 739 | if (returnType === "boolean") return Boolean(ret); |
| 740 | return ret; |
| 741 | } |
| 742 | var func = getCFunc(ident); |
| 743 | var cArgs = []; |
| 744 | var stack = 0; |
| 745 | if (args) { |
| 746 | for (var i = 0; i < args.length; i++) { |
| 747 | var converter = toC[argTypes[i]]; |
| 748 | if (converter) { |
| 749 | if (stack === 0) stack = stackSave(); |
| 750 | cArgs[i] = converter(args[i]); |
| 751 | } else { |
| 752 | cArgs[i] = args[i]; |
| 753 | } |
| 754 | } |
| 755 | } |
| 756 | var ret = func(...cArgs); |
| 757 | function onDone(ret) { |
| 758 | if (stack !== 0) stackRestore(stack); |
| 759 | return convertReturnValue(ret); |
| 760 | } |
| 761 | ret = onDone(ret); |
| 762 | return ret; |
| 763 | }; |
| 764 | |
| 765 | /** |
| 766 | * @param {string=} returnType |
| 767 | * @param {Array=} argTypes |
| 768 | * @param {Object=} opts |
| 769 | */ var cwrap = (ident, returnType, argTypes, opts) => { |
| 770 | // When the function takes numbers and returns a number, we can just return |
| 771 | // the original function |
| 772 | var numericArgs = !argTypes || argTypes.every(type => type === "number" || type === "boolean"); |
| 773 | var numericRet = returnType !== "string"; |
| 774 | if (numericRet && numericArgs && !opts) { |
| 775 | return getCFunc(ident); |
| 776 | } |
| 777 | return (...args) => ccall(ident, returnType, argTypes, args, opts); |
| 778 | }; |
| 779 | |
| 780 | var wasmImports = { |
| 781 | /** @export */ a: ___assert_fail, |
| 782 | /** @export */ b: _emscripten_resize_heap, |
| 783 | /** @export */ c: _exit |
| 784 | }; |
| 785 | |
| 786 | var wasmExports = createWasm(); |
| 787 | |
| 788 | var ___wasm_call_ctors = () => (___wasm_call_ctors = wasmExports["e"])(); |
| 789 | |
| 790 | var _pikchr_version = Module["_pikchr_version"] = () => (_pikchr_version = Module["_pikchr_version"] = wasmExports["g"])(); |
| 791 | |
| 792 | var _pikchr = Module["_pikchr"] = (a0, a1, a2, a3, a4) => (_pikchr = Module["_pikchr"] = wasmExports["h"])(a0, a1, a2, a3, a4); |
| 793 | |
| 794 | var __emscripten_stack_restore = a0 => (__emscripten_stack_restore = wasmExports["i"])(a0); |
| 795 | |
| 796 | var __emscripten_stack_alloc = a0 => (__emscripten_stack_alloc = wasmExports["j"])(a0); |
| 797 | |
| 798 | var _emscripten_stack_get_current = () => (_emscripten_stack_get_current = wasmExports["k"])(); |
| 799 | |
| 800 | // include: postamble.js |
| 801 | // === Auto-generated postamble setup entry stuff === |
| 802 | Module["stackSave"] = stackSave; |
| 803 | |
| 804 | Module["stackRestore"] = stackRestore; |
| 805 | |
| 806 | Module["stackAlloc"] = stackAlloc; |
| 807 | |
| 808 | Module["ccall"] = ccall; |
| 809 | |
| 810 | Module["cwrap"] = cwrap; |
| 811 | |
| 812 | Module["setValue"] = setValue; |
| 813 | |
| 814 | Module["getValue"] = getValue; |
| 815 | |
| 816 | var calledRun; |
| 817 | |
| 818 | var calledPrerun; |
| 819 | |
| 820 | dependenciesFulfilled = function runCaller() { |
| 821 | // If run has never been called, and we should call run (INVOKE_RUN is true, and Module.noInitialRun is not false) |
| 822 | if (!calledRun) run(); |
| 823 | if (!calledRun) dependenciesFulfilled = runCaller; |
| 824 | }; |
| 825 | |
| 826 | // try this again later, after new deps are fulfilled |
| 827 | function run() { |
| 828 | if (runDependencies > 0) { |
| 829 | return; |
| 830 | } |
| 831 | if (!calledPrerun) { |
| 832 | calledPrerun = 1; |
| 833 | preRun(); |
| 834 | // a preRun added a dependency, run will be called later |
| 835 | if (runDependencies > 0) { |
| 836 | return; |
| 837 | } |
| 838 | } |
| 839 | function doRun() { |
| 840 | // run may have just been called through dependencies being fulfilled just in this very frame, |
| 841 | // or while the async setStatus time below was happening |
| 842 | if (calledRun) return; |
| 843 | calledRun = 1; |
| 844 | Module["calledRun"] = 1; |
| 845 | if (ABORT) return; |
| 846 | initRuntime(); |
| 847 | readyPromiseResolve(Module); |
| 848 | Module["onRuntimeInitialized"]?.(); |
| 849 | postRun(); |
| 850 | } |
| 851 | if (Module["setStatus"]) { |
| 852 | Module["setStatus"]("Running..."); |
| 853 | setTimeout(() => { |
| 854 | setTimeout(() => Module["setStatus"](""), 1); |
| 855 | doRun(); |
| 856 | }, 1); |
| 857 | } else { |
| 858 | doRun(); |
| 859 | } |
| 860 | } |
| 861 | |
| 862 | if (Module["preInit"]) { |
| 863 | if (typeof Module["preInit"] == "function") Module["preInit"] = [ Module["preInit"] ]; |
| 864 | while (Module["preInit"].length > 0) { |
| 865 | Module["preInit"].pop()(); |
| 866 | } |
| 867 | } |
| 868 | |
| 869 | run(); |
| 870 | |
| 871 | // end include: postamble.js |
| 872 | // include: postamble_modularize.js |
| 873 | // In MODULARIZE mode we wrap the generated code in a factory function |
| 874 | // and return either the Module itself, or a promise of the module. |
| 875 | // We assign to the `moduleRtn` global here and configure closure to see |
| 876 | // this as and extern so it won't get minified. |
| 877 | moduleRtn = readyPromise; |
| 878 | |
| 879 | |
| 880 | return moduleRtn; |
| 881 | } |
| 882 | ); |
| 883 | })(); |
| 884 | if (typeof exports === 'object' && typeof module === 'object') |
| 885 | module.exports = initPikchrModule; |
| 886 | else if (typeof define === 'function' && define['amd']) |
| 887 | define([], () => initPikchrModule); |
| 888 |
| --- extsrc/pikchr.wasm | ||
| +++ extsrc/pikchr.wasm | ||
| cannot compute difference between binary files | ||
| 1 | 1 |
| --- extsrc/pikchr.wasm | |
| +++ extsrc/pikchr.wasm | |
| 0 | annot compute difference between binary files |
| 1 |
| --- extsrc/pikchr.wasm | |
| +++ extsrc/pikchr.wasm | |
| 0 | annot compute difference between binary files |
| 1 |
+378
-147
| --- extsrc/shell.c | ||
| +++ extsrc/shell.c | ||
| @@ -234,10 +234,12 @@ | ||
| 234 | 234 | |
| 235 | 235 | /* ctype macros that work with signed characters */ |
| 236 | 236 | #define IsSpace(X) isspace((unsigned char)X) |
| 237 | 237 | #define IsDigit(X) isdigit((unsigned char)X) |
| 238 | 238 | #define ToLower(X) (char)tolower((unsigned char)X) |
| 239 | +#define IsAlnum(X) isalnum((unsigned char)X) | |
| 240 | +#define IsAlpha(X) isalpha((unsigned char)X) | |
| 239 | 241 | |
| 240 | 242 | #if defined(_WIN32) || defined(WIN32) |
| 241 | 243 | #if SQLITE_OS_WINRT |
| 242 | 244 | #include <intrin.h> |
| 243 | 245 | #endif |
| @@ -849,11 +851,11 @@ | ||
| 849 | 851 | |
| 850 | 852 | /* |
| 851 | 853 | ** Prompt strings. Initialized in main. Settable with |
| 852 | 854 | ** .prompt main continue |
| 853 | 855 | */ |
| 854 | -#define PROMPT_LEN_MAX 20 | |
| 856 | +#define PROMPT_LEN_MAX 128 | |
| 855 | 857 | /* First line prompt. default: "sqlite> " */ |
| 856 | 858 | static char mainPrompt[PROMPT_LEN_MAX]; |
| 857 | 859 | /* Continuation prompt. default: " ...> " */ |
| 858 | 860 | static char continuePrompt[PROMPT_LEN_MAX]; |
| 859 | 861 | |
| @@ -1506,13 +1508,13 @@ | ||
| 1506 | 1508 | ** Return '"' if quoting is required. Return 0 if no quoting is required. |
| 1507 | 1509 | */ |
| 1508 | 1510 | static char quoteChar(const char *zName){ |
| 1509 | 1511 | int i; |
| 1510 | 1512 | if( zName==0 ) return '"'; |
| 1511 | - if( !isalpha((unsigned char)zName[0]) && zName[0]!='_' ) return '"'; | |
| 1513 | + if( !IsAlpha(zName[0]) && zName[0]!='_' ) return '"'; | |
| 1512 | 1514 | for(i=0; zName[i]; i++){ |
| 1513 | - if( !isalnum((unsigned char)zName[i]) && zName[i]!='_' ) return '"'; | |
| 1515 | + if( !IsAlnum(zName[i]) && zName[i]!='_' ) return '"'; | |
| 1514 | 1516 | } |
| 1515 | 1517 | return sqlite3_keyword_check(zName, i) ? '"' : 0; |
| 1516 | 1518 | } |
| 1517 | 1519 | |
| 1518 | 1520 | /* |
| @@ -2425,11 +2427,11 @@ | ||
| 2425 | 2427 | ** content in cases where the content starts |
| 2426 | 2428 | ** with a digit. |
| 2427 | 2429 | ** |
| 2428 | 2430 | ** typeof(Y)='blob' The hash is taken over prefix "Bnnn:" followed |
| 2429 | 2431 | ** by the binary content of the blob. The "nnn" |
| 2430 | -** in the prefix is the mimimum-length decimal | |
| 2432 | +** in the prefix is the minimum-length decimal | |
| 2431 | 2433 | ** representation of the byte-length of the blob. |
| 2432 | 2434 | ** |
| 2433 | 2435 | ** According to the rules above, all of the following SELECT statements |
| 2434 | 2436 | ** should return TRUE: |
| 2435 | 2437 | ** |
| @@ -3646,11 +3648,11 @@ | ||
| 3646 | 3648 | ** |
| 3647 | 3649 | ** UINT works like BINARY for text, except that embedded strings |
| 3648 | 3650 | ** of digits compare in numeric order. |
| 3649 | 3651 | ** |
| 3650 | 3652 | ** * Leading zeros are handled properly, in the sense that |
| 3651 | -** they do not mess of the maginitude comparison of embedded | |
| 3653 | +** they do not mess of the magnitude comparison of embedded | |
| 3652 | 3654 | ** strings of digits. "x00123y" is equal to "x123y". |
| 3653 | 3655 | ** |
| 3654 | 3656 | ** * Only unsigned integers are recognized. Plus and minus |
| 3655 | 3657 | ** signs are ignored. Decimal points and exponential notation |
| 3656 | 3658 | ** are ignored. |
| @@ -3752,10 +3754,13 @@ | ||
| 3752 | 3754 | ** warnings. */ |
| 3753 | 3755 | #ifndef UNUSED_PARAMETER |
| 3754 | 3756 | # define UNUSED_PARAMETER(X) (void)(X) |
| 3755 | 3757 | #endif |
| 3756 | 3758 | |
| 3759 | +#ifndef IsSpace | |
| 3760 | +#define IsSpace(X) isspace((unsigned char)X) | |
| 3761 | +#endif | |
| 3757 | 3762 | |
| 3758 | 3763 | /* A decimal object */ |
| 3759 | 3764 | typedef struct Decimal Decimal; |
| 3760 | 3765 | struct Decimal { |
| 3761 | 3766 | char sign; /* 0 for positive, 1 for negative */ |
| @@ -3801,11 +3806,11 @@ | ||
| 3801 | 3806 | p->isNull = 0; |
| 3802 | 3807 | p->nDigit = 0; |
| 3803 | 3808 | p->nFrac = 0; |
| 3804 | 3809 | p->a = sqlite3_malloc64( n+1 ); |
| 3805 | 3810 | if( p->a==0 ) goto new_from_text_failed; |
| 3806 | - for(i=0; isspace(zIn[i]); i++){} | |
| 3811 | + for(i=0; IsSpace(zIn[i]); i++){} | |
| 3807 | 3812 | if( zIn[i]=='-' ){ |
| 3808 | 3813 | p->sign = 1; |
| 3809 | 3814 | i++; |
| 3810 | 3815 | }else if( zIn[i]=='+' ){ |
| 3811 | 3816 | i++; |
| @@ -4456,11 +4461,11 @@ | ||
| 4456 | 4461 | } |
| 4457 | 4462 | decimal_free(pA); |
| 4458 | 4463 | decimal_free(pB); |
| 4459 | 4464 | } |
| 4460 | 4465 | |
| 4461 | -/* Aggregate funcion: decimal_sum(X) | |
| 4466 | +/* Aggregate function: decimal_sum(X) | |
| 4462 | 4467 | ** |
| 4463 | 4468 | ** Works like sum() except that it uses decimal arithmetic for unlimited |
| 4464 | 4469 | ** precision. |
| 4465 | 4470 | */ |
| 4466 | 4471 | static void decimalSumStep( |
| @@ -4817,11 +4822,11 @@ | ||
| 4817 | 4822 | } |
| 4818 | 4823 | |
| 4819 | 4824 | /* |
| 4820 | 4825 | ** Generate an error for a percentile function. |
| 4821 | 4826 | ** |
| 4822 | -** The error format string must have exactly one occurrance of "%%s()" | |
| 4827 | +** The error format string must have exactly one occurrence of "%%s()" | |
| 4823 | 4828 | ** (with two '%' characters). That substring will be replaced by the name |
| 4824 | 4829 | ** of the function. |
| 4825 | 4830 | */ |
| 4826 | 4831 | static void percentError(sqlite3_context *pCtx, const char *zFormat, ...){ |
| 4827 | 4832 | PercentileFunc *pFunc = (PercentileFunc*)sqlite3_user_data(pCtx); |
| @@ -5957,11 +5962,11 @@ | ||
| 5957 | 5962 | ** number format: |
| 5958 | 5963 | ** |
| 5959 | 5964 | ** WITH c(name,bin) AS (VALUES |
| 5960 | 5965 | ** ('minimum positive value', x'0000000000000001'), |
| 5961 | 5966 | ** ('maximum subnormal value', x'000fffffffffffff'), |
| 5962 | -** ('mininum positive nornal value', x'0010000000000000'), | |
| 5967 | +** ('minimum positive normal value', x'0010000000000000'), | |
| 5963 | 5968 | ** ('maximum value', x'7fefffffffffffff')) |
| 5964 | 5969 | ** SELECT c.name, decimal_mul(ieee754_mantissa(c.bin),pow2.v) |
| 5965 | 5970 | ** FROM pow2, c WHERE pow2.x=ieee754_exponent(c.bin); |
| 5966 | 5971 | ** |
| 5967 | 5972 | */ |
| @@ -6344,11 +6349,11 @@ | ||
| 6344 | 6349 | * 2 for UBSAN's satisfaction (and hypothetical 1's complement ALUs.) */ |
| 6345 | 6350 | smBase += (mxI64/2) * smStep; |
| 6346 | 6351 | smBase += (mxI64 - mxI64/2) * smStep; |
| 6347 | 6352 | } |
| 6348 | 6353 | /* Under UBSAN (or on 1's complement machines), must do this last term |
| 6349 | - * in steps to avoid the dreaded (and harmless) signed multiply overlow. */ | |
| 6354 | + * in steps to avoid the dreaded (and harmless) signed multiply overflow. */ | |
| 6350 | 6355 | if( ix>=2 ){ |
| 6351 | 6356 | sqlite3_int64 ix2 = (sqlite3_int64)ix/2; |
| 6352 | 6357 | smBase += ix2*smStep; |
| 6353 | 6358 | ix -= ix2; |
| 6354 | 6359 | } |
| @@ -6724,23 +6729,21 @@ | ||
| 6724 | 6729 | if( pCur->ss.iBase<iMin ){ |
| 6725 | 6730 | sqlite3_uint64 d = iMin - pCur->ss.iBase; |
| 6726 | 6731 | pCur->ss.iBase += ((d+szStep-1)/szStep)*szStep; |
| 6727 | 6732 | } |
| 6728 | 6733 | if( pCur->ss.iTerm>iMax ){ |
| 6729 | - sqlite3_uint64 d = pCur->ss.iTerm - iMax; | |
| 6730 | - pCur->ss.iTerm -= ((d+szStep-1)/szStep)*szStep; | |
| 6734 | + pCur->ss.iTerm = iMax; | |
| 6731 | 6735 | } |
| 6732 | 6736 | }else{ |
| 6733 | 6737 | sqlite3_int64 szStep = -pCur->ss.iStep; |
| 6734 | 6738 | assert( szStep>0 ); |
| 6735 | 6739 | if( pCur->ss.iBase>iMax ){ |
| 6736 | 6740 | sqlite3_uint64 d = pCur->ss.iBase - iMax; |
| 6737 | 6741 | pCur->ss.iBase -= ((d+szStep-1)/szStep)*szStep; |
| 6738 | 6742 | } |
| 6739 | 6743 | if( pCur->ss.iTerm<iMin ){ |
| 6740 | - sqlite3_uint64 d = iMin - pCur->ss.iTerm; | |
| 6741 | - pCur->ss.iTerm += ((d+szStep-1)/szStep)*szStep; | |
| 6744 | + pCur->ss.iTerm = iMin; | |
| 6742 | 6745 | } |
| 6743 | 6746 | } |
| 6744 | 6747 | } |
| 6745 | 6748 | |
| 6746 | 6749 | /* Apply LIMIT and OFFSET constraints, if any */ |
| @@ -9024,10 +9027,15 @@ | ||
| 9024 | 9027 | #include <assert.h> |
| 9025 | 9028 | #include <string.h> |
| 9026 | 9029 | #include <ctype.h> |
| 9027 | 9030 | |
| 9028 | 9031 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 9032 | + | |
| 9033 | +#ifndef IsAlnum | |
| 9034 | +#define IsAlnum(X) isalnum((unsigned char)X) | |
| 9035 | +#endif | |
| 9036 | + | |
| 9029 | 9037 | |
| 9030 | 9038 | /* completion_vtab is a subclass of sqlite3_vtab which will |
| 9031 | 9039 | ** serve as the underlying representation of a completion virtual table |
| 9032 | 9040 | */ |
| 9033 | 9041 | typedef struct completion_vtab completion_vtab; |
| @@ -9361,11 +9369,11 @@ | ||
| 9361 | 9369 | if( pCur->zLine==0 ) return SQLITE_NOMEM; |
| 9362 | 9370 | } |
| 9363 | 9371 | } |
| 9364 | 9372 | if( pCur->zLine!=0 && pCur->zPrefix==0 ){ |
| 9365 | 9373 | int i = pCur->nLine; |
| 9366 | - while( i>0 && (isalnum(pCur->zLine[i-1]) || pCur->zLine[i-1]=='_') ){ | |
| 9374 | + while( i>0 && (IsAlnum(pCur->zLine[i-1]) || pCur->zLine[i-1]=='_') ){ | |
| 9367 | 9375 | i--; |
| 9368 | 9376 | } |
| 9369 | 9377 | pCur->nPrefix = pCur->nLine - i; |
| 9370 | 9378 | if( pCur->nPrefix>0 ){ |
| 9371 | 9379 | pCur->zPrefix = sqlite3_mprintf("%.*s", pCur->nPrefix, pCur->zLine + i); |
| @@ -14768,11 +14776,11 @@ | ||
| 14768 | 14776 | /* Register the auth callback with dbv */ |
| 14769 | 14777 | if( rc==SQLITE_OK ){ |
| 14770 | 14778 | sqlite3_set_authorizer(pNew->dbv, idxAuthCallback, (void*)pNew); |
| 14771 | 14779 | } |
| 14772 | 14780 | |
| 14773 | - /* If an error has occurred, free the new object and reutrn NULL. Otherwise, | |
| 14781 | + /* If an error has occurred, free the new object and return NULL. Otherwise, | |
| 14774 | 14782 | ** return the new sqlite3expert handle. */ |
| 14775 | 14783 | if( rc!=SQLITE_OK ){ |
| 14776 | 14784 | sqlite3_expert_destroy(pNew); |
| 14777 | 14785 | pNew = 0; |
| 14778 | 14786 | } |
| @@ -16290,11 +16298,11 @@ | ||
| 16290 | 16298 | ** |
| 16291 | 16299 | ** PRAGMA vfstrace('+all'); |
| 16292 | 16300 | ** |
| 16293 | 16301 | ** Individual APIs can be enabled or disabled by name, with or without |
| 16294 | 16302 | ** the initial "x" character. For example, to set up for tracing lock |
| 16295 | -** primatives only: | |
| 16303 | +** primitives only: | |
| 16296 | 16304 | ** |
| 16297 | 16305 | ** PRAGMA vfstrace('-all, +Lock,Unlock,ShmLock'); |
| 16298 | 16306 | ** |
| 16299 | 16307 | ** The argument to the vfstrace pragma ignores capitalization and any |
| 16300 | 16308 | ** characters other than alphabetics, '+', and '-'. |
| @@ -18700,10 +18708,20 @@ | ||
| 18700 | 18708 | |
| 18701 | 18709 | /* typedef unsigned int u32; */ |
| 18702 | 18710 | /* typedef unsigned char u8; */ |
| 18703 | 18711 | /* typedef sqlite3_int64 i64; */ |
| 18704 | 18712 | |
| 18713 | +/* | |
| 18714 | +** Work around C99 "flex-array" syntax for pre-C99 compilers, so as | |
| 18715 | +** to avoid complaints from -fsanitize=strict-bounds. | |
| 18716 | +*/ | |
| 18717 | +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) | |
| 18718 | +# define FLEXARRAY | |
| 18719 | +#else | |
| 18720 | +# define FLEXARRAY 1 | |
| 18721 | +#endif | |
| 18722 | + | |
| 18705 | 18723 | typedef struct RecoverTable RecoverTable; |
| 18706 | 18724 | typedef struct RecoverColumn RecoverColumn; |
| 18707 | 18725 | |
| 18708 | 18726 | /* |
| 18709 | 18727 | ** When recovering rows of data that can be associated with table |
| @@ -18807,12 +18825,15 @@ | ||
| 18807 | 18825 | ** (aElem[iKey/32] & (1 << (iKey%32))) ? 1 : 0 |
| 18808 | 18826 | */ |
| 18809 | 18827 | typedef struct RecoverBitmap RecoverBitmap; |
| 18810 | 18828 | struct RecoverBitmap { |
| 18811 | 18829 | i64 nPg; /* Size of bitmap */ |
| 18812 | - u32 aElem[1]; /* Array of 32-bit bitmasks */ | |
| 18830 | + u32 aElem[FLEXARRAY]; /* Array of 32-bit bitmasks */ | |
| 18813 | 18831 | }; |
| 18832 | + | |
| 18833 | +/* Size in bytes of a RecoverBitmap object sufficient to cover 32 pages */ | |
| 18834 | +#define SZ_RECOVERBITMAP_32 (16) | |
| 18814 | 18835 | |
| 18815 | 18836 | /* |
| 18816 | 18837 | ** State variables (part of the sqlite3_recover structure) used while |
| 18817 | 18838 | ** recovering data for tables identified in the recovered schema (state |
| 18818 | 18839 | ** RECOVER_STATE_WRITING). |
| @@ -19049,11 +19070,11 @@ | ||
| 19049 | 19070 | ** large enough to store a bit for all page numbers between 1 and nPg, |
| 19050 | 19071 | ** inclusive. The bitmap is initially zeroed. |
| 19051 | 19072 | */ |
| 19052 | 19073 | static RecoverBitmap *recoverBitmapAlloc(sqlite3_recover *p, i64 nPg){ |
| 19053 | 19074 | int nElem = (nPg+1+31) / 32; |
| 19054 | - int nByte = sizeof(RecoverBitmap) + nElem*sizeof(u32); | |
| 19075 | + int nByte = SZ_RECOVERBITMAP_32 + nElem*sizeof(u32); | |
| 19055 | 19076 | RecoverBitmap *pRet = (RecoverBitmap*)recoverMalloc(p, nByte); |
| 19056 | 19077 | |
| 19057 | 19078 | if( pRet ){ |
| 19058 | 19079 | pRet->nPg = nPg; |
| 19059 | 19080 | } |
| @@ -21241,41 +21262,57 @@ | ||
| 21241 | 21262 | ** function is called. |
| 21242 | 21263 | */ |
| 21243 | 21264 | static void recoverStep(sqlite3_recover *p){ |
| 21244 | 21265 | assert( p && p->errCode==SQLITE_OK ); |
| 21245 | 21266 | switch( p->eState ){ |
| 21246 | - case RECOVER_STATE_INIT: | |
| 21267 | + case RECOVER_STATE_INIT: { | |
| 21268 | + int bUseWrapper = 1; | |
| 21247 | 21269 | /* This is the very first call to sqlite3_recover_step() on this object. |
| 21248 | 21270 | */ |
| 21249 | 21271 | recoverSqlCallback(p, "BEGIN"); |
| 21250 | 21272 | recoverSqlCallback(p, "PRAGMA writable_schema = on"); |
| 21273 | + recoverSqlCallback(p, "PRAGMA foreign_keys = off"); | |
| 21251 | 21274 | |
| 21252 | 21275 | recoverEnterMutex(); |
| 21253 | - recoverInstallWrapper(p); | |
| 21254 | 21276 | |
| 21255 | 21277 | /* Open the output database. And register required virtual tables and |
| 21256 | 21278 | ** user functions with the new handle. */ |
| 21257 | 21279 | recoverOpenOutput(p); |
| 21258 | 21280 | |
| 21259 | - /* Open transactions on both the input and output databases. */ | |
| 21260 | - sqlite3_file_control(p->dbIn, p->zDb, SQLITE_FCNTL_RESET_CACHE, 0); | |
| 21261 | - recoverExec(p, p->dbIn, "PRAGMA writable_schema = on"); | |
| 21262 | - recoverExec(p, p->dbIn, "BEGIN"); | |
| 21263 | - if( p->errCode==SQLITE_OK ) p->bCloseTransaction = 1; | |
| 21264 | - recoverExec(p, p->dbIn, "SELECT 1 FROM sqlite_schema"); | |
| 21265 | - recoverTransferSettings(p); | |
| 21266 | - recoverOpenRecovery(p); | |
| 21267 | - recoverCacheSchema(p); | |
| 21268 | - | |
| 21269 | - recoverUninstallWrapper(p); | |
| 21281 | + /* Attempt to open a transaction and read page 1 of the input database. | |
| 21282 | + ** Two attempts may be made - one with a wrapper installed to ensure | |
| 21283 | + ** that the database header is sane, and then if that attempt returns | |
| 21284 | + ** SQLITE_NOTADB, then again with no wrapper. The second attempt is | |
| 21285 | + ** required for encrypted databases. */ | |
| 21286 | + if( p->errCode==SQLITE_OK ){ | |
| 21287 | + do{ | |
| 21288 | + p->errCode = SQLITE_OK; | |
| 21289 | + if( bUseWrapper ) recoverInstallWrapper(p); | |
| 21290 | + | |
| 21291 | + /* Open a transaction on the input database. */ | |
| 21292 | + sqlite3_file_control(p->dbIn, p->zDb, SQLITE_FCNTL_RESET_CACHE, 0); | |
| 21293 | + recoverExec(p, p->dbIn, "PRAGMA writable_schema = on"); | |
| 21294 | + recoverExec(p, p->dbIn, "BEGIN"); | |
| 21295 | + if( p->errCode==SQLITE_OK ) p->bCloseTransaction = 1; | |
| 21296 | + recoverExec(p, p->dbIn, "SELECT 1 FROM sqlite_schema"); | |
| 21297 | + recoverTransferSettings(p); | |
| 21298 | + recoverOpenRecovery(p); | |
| 21299 | + recoverCacheSchema(p); | |
| 21300 | + | |
| 21301 | + if( bUseWrapper ) recoverUninstallWrapper(p); | |
| 21302 | + }while( p->errCode==SQLITE_NOTADB | |
| 21303 | + && (bUseWrapper--) | |
| 21304 | + && SQLITE_OK==sqlite3_exec(p->dbIn, "ROLLBACK", 0, 0, 0) | |
| 21305 | + ); | |
| 21306 | + } | |
| 21307 | + | |
| 21270 | 21308 | recoverLeaveMutex(); |
| 21271 | - | |
| 21272 | 21309 | recoverExec(p, p->dbOut, "BEGIN"); |
| 21273 | - | |
| 21274 | 21310 | recoverWriteSchema1(p); |
| 21275 | 21311 | p->eState = RECOVER_STATE_WRITING; |
| 21276 | 21312 | break; |
| 21313 | + } | |
| 21277 | 21314 | |
| 21278 | 21315 | case RECOVER_STATE_WRITING: { |
| 21279 | 21316 | if( p->w1.pTbls==0 ){ |
| 21280 | 21317 | recoverWriteDataInit(p); |
| 21281 | 21318 | } |
| @@ -21610,10 +21647,11 @@ | ||
| 21610 | 21647 | u8 eTraceType; /* SHELL_TRACE_* value for type of trace */ |
| 21611 | 21648 | u8 bSafeMode; /* True to prohibit unsafe operations */ |
| 21612 | 21649 | u8 bSafeModePersist; /* The long-term value of bSafeMode */ |
| 21613 | 21650 | u8 eRestoreState; /* See comments above doAutoDetectRestore() */ |
| 21614 | 21651 | u8 crlfMode; /* Do NL-to-CRLF translations when enabled (maybe) */ |
| 21652 | + u8 eEscMode; /* Escape mode for text output */ | |
| 21615 | 21653 | ColModeOpts cmOpts; /* Option values affecting columnar mode output */ |
| 21616 | 21654 | unsigned statsOn; /* True to display memory stats before each finalize */ |
| 21617 | 21655 | unsigned mEqpLines; /* Mask of vertical lines in the EQP output graph */ |
| 21618 | 21656 | int inputNesting; /* Track nesting level of .read and other redirects */ |
| 21619 | 21657 | int outCount; /* Revert to stdout when reaching zero */ |
| @@ -21710,10 +21748,19 @@ | ||
| 21710 | 21748 | #define SHELL_PROGRESS_RESET 0x02 /* Reset the count when the progress |
| 21711 | 21749 | ** callback limit is reached, and for each |
| 21712 | 21750 | ** top-level SQL statement */ |
| 21713 | 21751 | #define SHELL_PROGRESS_ONCE 0x04 /* Cancel the --limit after firing once */ |
| 21714 | 21752 | |
| 21753 | +/* Allowed values for ShellState.eEscMode. The default value should | |
| 21754 | +** be 0, so to change the default, reorder the names. | |
| 21755 | +*/ | |
| 21756 | +#define SHELL_ESC_ASCII 0 /* Substitute ^Y for X where Y=X+0x40 */ | |
| 21757 | +#define SHELL_ESC_SYMBOL 1 /* Substitute U+2400 graphics */ | |
| 21758 | +#define SHELL_ESC_OFF 2 /* Send characters verbatim */ | |
| 21759 | + | |
| 21760 | +static const char *shell_EscModeNames[] = { "ascii", "symbol", "off" }; | |
| 21761 | + | |
| 21715 | 21762 | /* |
| 21716 | 21763 | ** These are the allowed shellFlgs values |
| 21717 | 21764 | */ |
| 21718 | 21765 | #define SHFLG_Pagecache 0x00000001 /* The --pagecache option is used */ |
| 21719 | 21766 | #define SHFLG_Lookaside 0x00000002 /* Lookaside memory is used */ |
| @@ -22047,63 +22094,79 @@ | ||
| 22047 | 22094 | sqlite3_fprintf(out, "X'%s'", zStr); |
| 22048 | 22095 | sqlite3_free(zStr); |
| 22049 | 22096 | } |
| 22050 | 22097 | |
| 22051 | 22098 | /* |
| 22052 | -** Find a string that is not found anywhere in z[]. Return a pointer | |
| 22053 | -** to that string. | |
| 22054 | -** | |
| 22055 | -** Try to use zA and zB first. If both of those are already found in z[] | |
| 22056 | -** then make up some string and store it in the buffer zBuf. | |
| 22057 | -*/ | |
| 22058 | -static const char *unused_string( | |
| 22059 | - const char *z, /* Result must not appear anywhere in z */ | |
| 22060 | - const char *zA, const char *zB, /* Try these first */ | |
| 22061 | - char *zBuf /* Space to store a generated string */ | |
| 22062 | -){ | |
| 22063 | - unsigned i = 0; | |
| 22064 | - if( strstr(z, zA)==0 ) return zA; | |
| 22065 | - if( strstr(z, zB)==0 ) return zB; | |
| 22066 | - do{ | |
| 22067 | - sqlite3_snprintf(20,zBuf,"(%s%u)", zA, i++); | |
| 22068 | - }while( strstr(z,zBuf)!=0 ); | |
| 22069 | - return zBuf; | |
| 22070 | -} | |
| 22071 | - | |
| 22072 | -/* | |
| 22073 | -** Output the given string as a quoted string using SQL quoting conventions. | |
| 22074 | -** | |
| 22075 | -** See also: output_quoted_escaped_string() | |
| 22076 | -*/ | |
| 22077 | -static void output_quoted_string(ShellState *p, const char *z){ | |
| 22099 | +** Output the given string as a quoted string using SQL quoting conventions: | |
| 22100 | +** | |
| 22101 | +** (1) Single quotes (') within the string are doubled | |
| 22102 | +** (2) The whle string is enclosed in '...' | |
| 22103 | +** (3) Control characters other than \n, \t, and \r\n are escaped | |
| 22104 | +** using \u00XX notation and if such substitutions occur, | |
| 22105 | +** the whole string is enclosed in unistr('...') instead of '...'. | |
| 22106 | +** | |
| 22107 | +** Step (3) is omitted if the control-character escape mode is OFF. | |
| 22108 | +** | |
| 22109 | +** See also: output_quoted_escaped_string() which does the same except | |
| 22110 | +** that it does not make exceptions for \n, \t, and \r\n in step (3). | |
| 22111 | +*/ | |
| 22112 | +static void output_quoted_string(ShellState *p, const char *zInX){ | |
| 22078 | 22113 | int i; |
| 22079 | - char c; | |
| 22114 | + int needUnistr = 0; | |
| 22115 | + int needDblQuote = 0; | |
| 22116 | + const unsigned char *z = (const unsigned char*)zInX; | |
| 22117 | + unsigned char c; | |
| 22080 | 22118 | FILE *out = p->out; |
| 22081 | 22119 | sqlite3_fsetmode(out, _O_BINARY); |
| 22082 | 22120 | if( z==0 ) return; |
| 22083 | - for(i=0; (c = z[i])!=0 && c!='\''; i++){} | |
| 22084 | - if( c==0 ){ | |
| 22121 | + for(i=0; (c = z[i])!=0; i++){ | |
| 22122 | + if( c=='\'' ){ needDblQuote = 1; } | |
| 22123 | + if( c>0x1f ) continue; | |
| 22124 | + if( c=='\t' || c=='\n' ) continue; | |
| 22125 | + if( c=='\r' && z[i+1]=='\n' ) continue; | |
| 22126 | + needUnistr = 1; | |
| 22127 | + break; | |
| 22128 | + } | |
| 22129 | + if( (needDblQuote==0 && needUnistr==0) | |
| 22130 | + || (needDblQuote==0 && p->eEscMode==SHELL_ESC_OFF) | |
| 22131 | + ){ | |
| 22085 | 22132 | sqlite3_fprintf(out, "'%s'",z); |
| 22133 | + }else if( p->eEscMode==SHELL_ESC_OFF ){ | |
| 22134 | + char *zEncoded = sqlite3_mprintf("%Q", z); | |
| 22135 | + sqlite3_fputs(zEncoded, out); | |
| 22136 | + sqlite3_free(zEncoded); | |
| 22086 | 22137 | }else{ |
| 22087 | - sqlite3_fputs("'", out); | |
| 22138 | + if( needUnistr ){ | |
| 22139 | + sqlite3_fputs("unistr('", out); | |
| 22140 | + }else{ | |
| 22141 | + sqlite3_fputs("'", out); | |
| 22142 | + } | |
| 22088 | 22143 | while( *z ){ |
| 22089 | - for(i=0; (c = z[i])!=0 && c!='\''; i++){} | |
| 22090 | - if( c=='\'' ) i++; | |
| 22144 | + for(i=0; (c = z[i])!=0; i++){ | |
| 22145 | + if( c=='\'' ) break; | |
| 22146 | + if( c>0x1f ) continue; | |
| 22147 | + if( c=='\t' || c=='\n' ) continue; | |
| 22148 | + if( c=='\r' && z[i+1]=='\n' ) continue; | |
| 22149 | + break; | |
| 22150 | + } | |
| 22091 | 22151 | if( i ){ |
| 22092 | 22152 | sqlite3_fprintf(out, "%.*s", i, z); |
| 22093 | 22153 | z += i; |
| 22094 | 22154 | } |
| 22155 | + if( c==0 ) break; | |
| 22095 | 22156 | if( c=='\'' ){ |
| 22096 | - sqlite3_fputs("'", out); | |
| 22097 | - continue; | |
| 22098 | - } | |
| 22099 | - if( c==0 ){ | |
| 22100 | - break; | |
| 22157 | + sqlite3_fputs("''", out); | |
| 22158 | + }else{ | |
| 22159 | + sqlite3_fprintf(out, "\\u%04x", c); | |
| 22101 | 22160 | } |
| 22102 | 22161 | z++; |
| 22103 | 22162 | } |
| 22104 | - sqlite3_fputs("'", out); | |
| 22163 | + if( needUnistr ){ | |
| 22164 | + sqlite3_fputs("')", out); | |
| 22165 | + }else{ | |
| 22166 | + sqlite3_fputs("'", out); | |
| 22167 | + } | |
| 22105 | 22168 | } |
| 22106 | 22169 | setCrlfMode(p); |
| 22107 | 22170 | } |
| 22108 | 22171 | |
| 22109 | 22172 | /* |
| @@ -22114,65 +22177,19 @@ | ||
| 22114 | 22177 | ** |
| 22115 | 22178 | ** This is like output_quoted_string() but with the addition of the \r\n |
| 22116 | 22179 | ** escape mechanism. |
| 22117 | 22180 | */ |
| 22118 | 22181 | static void output_quoted_escaped_string(ShellState *p, const char *z){ |
| 22119 | - int i; | |
| 22120 | - char c; | |
| 22121 | - FILE *out = p->out; | |
| 22122 | - sqlite3_fsetmode(out, _O_BINARY); | |
| 22123 | - for(i=0; (c = z[i])!=0 && c!='\'' && c!='\n' && c!='\r'; i++){} | |
| 22124 | - if( c==0 ){ | |
| 22125 | - sqlite3_fprintf(out, "'%s'",z); | |
| 22182 | + char *zEscaped; | |
| 22183 | + sqlite3_fsetmode(p->out, _O_BINARY); | |
| 22184 | + if( p->eEscMode==SHELL_ESC_OFF ){ | |
| 22185 | + zEscaped = sqlite3_mprintf("%Q", z); | |
| 22126 | 22186 | }else{ |
| 22127 | - const char *zNL = 0; | |
| 22128 | - const char *zCR = 0; | |
| 22129 | - int nNL = 0; | |
| 22130 | - int nCR = 0; | |
| 22131 | - char zBuf1[20], zBuf2[20]; | |
| 22132 | - for(i=0; z[i]; i++){ | |
| 22133 | - if( z[i]=='\n' ) nNL++; | |
| 22134 | - if( z[i]=='\r' ) nCR++; | |
| 22135 | - } | |
| 22136 | - if( nNL ){ | |
| 22137 | - sqlite3_fputs("replace(", out); | |
| 22138 | - zNL = unused_string(z, "\\n", "\\012", zBuf1); | |
| 22139 | - } | |
| 22140 | - if( nCR ){ | |
| 22141 | - sqlite3_fputs("replace(", out); | |
| 22142 | - zCR = unused_string(z, "\\r", "\\015", zBuf2); | |
| 22143 | - } | |
| 22144 | - sqlite3_fputs("'", out); | |
| 22145 | - while( *z ){ | |
| 22146 | - for(i=0; (c = z[i])!=0 && c!='\n' && c!='\r' && c!='\''; i++){} | |
| 22147 | - if( c=='\'' ) i++; | |
| 22148 | - if( i ){ | |
| 22149 | - sqlite3_fprintf(out, "%.*s", i, z); | |
| 22150 | - z += i; | |
| 22151 | - } | |
| 22152 | - if( c=='\'' ){ | |
| 22153 | - sqlite3_fputs("'", out); | |
| 22154 | - continue; | |
| 22155 | - } | |
| 22156 | - if( c==0 ){ | |
| 22157 | - break; | |
| 22158 | - } | |
| 22159 | - z++; | |
| 22160 | - if( c=='\n' ){ | |
| 22161 | - sqlite3_fputs(zNL, out); | |
| 22162 | - continue; | |
| 22163 | - } | |
| 22164 | - sqlite3_fputs(zCR, out); | |
| 22165 | - } | |
| 22166 | - sqlite3_fputs("'", out); | |
| 22167 | - if( nCR ){ | |
| 22168 | - sqlite3_fprintf(out, ",'%s',char(13))", zCR); | |
| 22169 | - } | |
| 22170 | - if( nNL ){ | |
| 22171 | - sqlite3_fprintf(out, ",'%s',char(10))", zNL); | |
| 22172 | - } | |
| 22173 | - } | |
| 22187 | + zEscaped = sqlite3_mprintf("%#Q", z); | |
| 22188 | + } | |
| 22189 | + sqlite3_fputs(zEscaped, p->out); | |
| 22190 | + sqlite3_free(zEscaped); | |
| 22174 | 22191 | setCrlfMode(p); |
| 22175 | 22192 | } |
| 22176 | 22193 | |
| 22177 | 22194 | /* |
| 22178 | 22195 | ** Find earliest of chars within s specified in zAny. |
| @@ -22317,10 +22334,97 @@ | ||
| 22317 | 22334 | sqlite3_fputs(ace+1, out); |
| 22318 | 22335 | } |
| 22319 | 22336 | } |
| 22320 | 22337 | sqlite3_fputs(zq, out); |
| 22321 | 22338 | } |
| 22339 | + | |
| 22340 | +/* | |
| 22341 | +** Escape the input string if it is needed and in accordance with | |
| 22342 | +** eEscMode. | |
| 22343 | +** | |
| 22344 | +** Escaping is needed if the string contains any control characters | |
| 22345 | +** other than \t, \n, and \r\n | |
| 22346 | +** | |
| 22347 | +** If no escaping is needed (the common case) then set *ppFree to NULL | |
| 22348 | +** and return the original string. If escapingn is needed, write the | |
| 22349 | +** escaped string into memory obtained from sqlite3_malloc64() or the | |
| 22350 | +** equivalent, and return the new string and set *ppFree to the new string | |
| 22351 | +** as well. | |
| 22352 | +** | |
| 22353 | +** The caller is responsible for freeing *ppFree if it is non-NULL in order | |
| 22354 | +** to reclaim memory. | |
| 22355 | +*/ | |
| 22356 | +static const char *escapeOutput( | |
| 22357 | + ShellState *p, | |
| 22358 | + const char *zInX, | |
| 22359 | + char **ppFree | |
| 22360 | +){ | |
| 22361 | + i64 i, j; | |
| 22362 | + i64 nCtrl = 0; | |
| 22363 | + unsigned char *zIn; | |
| 22364 | + unsigned char c; | |
| 22365 | + unsigned char *zOut; | |
| 22366 | + | |
| 22367 | + | |
| 22368 | + /* No escaping if disabled */ | |
| 22369 | + if( p->eEscMode==SHELL_ESC_OFF ){ | |
| 22370 | + *ppFree = 0; | |
| 22371 | + return zInX; | |
| 22372 | + } | |
| 22373 | + | |
| 22374 | + /* Count the number of control characters in the string. */ | |
| 22375 | + zIn = (unsigned char*)zInX; | |
| 22376 | + for(i=0; (c = zIn[i])!=0; i++){ | |
| 22377 | + if( c<=0x1f | |
| 22378 | + && c!='\t' | |
| 22379 | + && c!='\n' | |
| 22380 | + && (c!='\r' || zIn[i+1]!='\n') | |
| 22381 | + ){ | |
| 22382 | + nCtrl++; | |
| 22383 | + } | |
| 22384 | + } | |
| 22385 | + if( nCtrl==0 ){ | |
| 22386 | + *ppFree = 0; | |
| 22387 | + return zInX; | |
| 22388 | + } | |
| 22389 | + if( p->eEscMode==SHELL_ESC_SYMBOL ) nCtrl *= 2; | |
| 22390 | + zOut = sqlite3_malloc64( i + nCtrl + 1 ); | |
| 22391 | + shell_check_oom(zOut); | |
| 22392 | + for(i=j=0; (c = zIn[i])!=0; i++){ | |
| 22393 | + if( c>0x1f | |
| 22394 | + || c=='\t' | |
| 22395 | + || c=='\n' | |
| 22396 | + || (c=='\r' && zIn[i+1]=='\n') | |
| 22397 | + ){ | |
| 22398 | + continue; | |
| 22399 | + } | |
| 22400 | + if( i>0 ){ | |
| 22401 | + memcpy(&zOut[j], zIn, i); | |
| 22402 | + j += i; | |
| 22403 | + } | |
| 22404 | + zIn += i+1; | |
| 22405 | + i = -1; | |
| 22406 | + switch( p->eEscMode ){ | |
| 22407 | + case SHELL_ESC_SYMBOL: | |
| 22408 | + zOut[j++] = 0xe2; | |
| 22409 | + zOut[j++] = 0x90; | |
| 22410 | + zOut[j++] = 0x80+c; | |
| 22411 | + break; | |
| 22412 | + case SHELL_ESC_ASCII: | |
| 22413 | + zOut[j++] = '^'; | |
| 22414 | + zOut[j++] = 0x40+c; | |
| 22415 | + break; | |
| 22416 | + } | |
| 22417 | + } | |
| 22418 | + if( i>0 ){ | |
| 22419 | + memcpy(&zOut[j], zIn, i); | |
| 22420 | + j += i; | |
| 22421 | + } | |
| 22422 | + zOut[j] = 0; | |
| 22423 | + *ppFree = (char*)zOut; | |
| 22424 | + return (char*)zOut; | |
| 22425 | +} | |
| 22322 | 22426 | |
| 22323 | 22427 | /* |
| 22324 | 22428 | ** Output the given string with characters that are special to |
| 22325 | 22429 | ** HTML escaped. |
| 22326 | 22430 | */ |
| @@ -22761,12 +22865,16 @@ | ||
| 22761 | 22865 | int len = strlen30(azCol[i] ? azCol[i] : ""); |
| 22762 | 22866 | if( len>w ) w = len; |
| 22763 | 22867 | } |
| 22764 | 22868 | if( p->cnt++>0 ) sqlite3_fputs(p->rowSeparator, p->out); |
| 22765 | 22869 | for(i=0; i<nArg; i++){ |
| 22870 | + char *pFree = 0; | |
| 22871 | + const char *pDisplay; | |
| 22872 | + pDisplay = escapeOutput(p, azArg[i] ? azArg[i] : p->nullValue, &pFree); | |
| 22766 | 22873 | sqlite3_fprintf(p->out, "%*s = %s%s", w, azCol[i], |
| 22767 | - azArg[i] ? azArg[i] : p->nullValue, p->rowSeparator); | |
| 22874 | + pDisplay, p->rowSeparator); | |
| 22875 | + if( pFree ) sqlite3_free(pFree); | |
| 22768 | 22876 | } |
| 22769 | 22877 | break; |
| 22770 | 22878 | } |
| 22771 | 22879 | case MODE_ScanExp: |
| 22772 | 22880 | case MODE_Explain: { |
| @@ -22832,18 +22940,22 @@ | ||
| 22832 | 22940 | int j; |
| 22833 | 22941 | int nParen = 0; |
| 22834 | 22942 | char cEnd = 0; |
| 22835 | 22943 | char c; |
| 22836 | 22944 | int nLine = 0; |
| 22945 | + int isIndex; | |
| 22946 | + int isWhere = 0; | |
| 22837 | 22947 | assert( nArg==1 ); |
| 22838 | 22948 | if( azArg[0]==0 ) break; |
| 22839 | 22949 | if( sqlite3_strlike("CREATE VIEW%", azArg[0], 0)==0 |
| 22840 | 22950 | || sqlite3_strlike("CREATE TRIG%", azArg[0], 0)==0 |
| 22841 | 22951 | ){ |
| 22842 | 22952 | sqlite3_fprintf(p->out, "%s;\n", azArg[0]); |
| 22843 | 22953 | break; |
| 22844 | 22954 | } |
| 22955 | + isIndex = sqlite3_strlike("CREATE INDEX%", azArg[0], 0)==0 | |
| 22956 | + || sqlite3_strlike("CREATE UNIQUE INDEX%", azArg[0], 0)==0; | |
| 22845 | 22957 | z = sqlite3_mprintf("%s", azArg[0]); |
| 22846 | 22958 | shell_check_oom(z); |
| 22847 | 22959 | j = 0; |
| 22848 | 22960 | for(i=0; IsSpace(z[i]); i++){} |
| 22849 | 22961 | for(; (c = z[i])!=0; i++){ |
| @@ -22869,18 +22981,30 @@ | ||
| 22869 | 22981 | cEnd = '\n'; |
| 22870 | 22982 | }else if( c=='(' ){ |
| 22871 | 22983 | nParen++; |
| 22872 | 22984 | }else if( c==')' ){ |
| 22873 | 22985 | nParen--; |
| 22874 | - if( nLine>0 && nParen==0 && j>0 ){ | |
| 22986 | + if( nLine>0 && nParen==0 && j>0 && !isWhere ){ | |
| 22875 | 22987 | printSchemaLineN(p->out, z, j, "\n"); |
| 22876 | 22988 | j = 0; |
| 22877 | 22989 | } |
| 22990 | + }else if( (c=='w' || c=='W') | |
| 22991 | + && nParen==0 && isIndex | |
| 22992 | + && sqlite3_strnicmp("WHERE",&z[i],5)==0 | |
| 22993 | + && !IsAlnum(z[i+5]) && z[i+5]!='_' ){ | |
| 22994 | + isWhere = 1; | |
| 22995 | + }else if( isWhere && (c=='A' || c=='a') | |
| 22996 | + && nParen==0 | |
| 22997 | + && sqlite3_strnicmp("AND",&z[i],3)==0 | |
| 22998 | + && !IsAlnum(z[i+3]) && z[i+3]!='_' ){ | |
| 22999 | + printSchemaLineN(p->out, z, j, "\n "); | |
| 23000 | + j = 0; | |
| 22878 | 23001 | } |
| 22879 | 23002 | z[j++] = c; |
| 22880 | 23003 | if( nParen==1 && cEnd==0 |
| 22881 | 23004 | && (c=='(' || c=='\n' || (c==',' && !wsToEol(z+i+1))) |
| 23005 | + && !isWhere | |
| 22882 | 23006 | ){ |
| 22883 | 23007 | if( c=='\n' ) j--; |
| 22884 | 23008 | printSchemaLineN(p->out, z, j, "\n "); |
| 22885 | 23009 | j = 0; |
| 22886 | 23010 | nLine++; |
| @@ -22894,19 +23018,27 @@ | ||
| 22894 | 23018 | break; |
| 22895 | 23019 | } |
| 22896 | 23020 | case MODE_List: { |
| 22897 | 23021 | if( p->cnt++==0 && p->showHeader ){ |
| 22898 | 23022 | for(i=0; i<nArg; i++){ |
| 22899 | - sqlite3_fprintf(p->out, "%s%s", azCol[i], | |
| 23023 | + char *z = azCol[i]; | |
| 23024 | + char *pFree; | |
| 23025 | + const char *zOut = escapeOutput(p, z, &pFree); | |
| 23026 | + sqlite3_fprintf(p->out, "%s%s", zOut, | |
| 22900 | 23027 | i==nArg-1 ? p->rowSeparator : p->colSeparator); |
| 23028 | + if( pFree ) sqlite3_free(pFree); | |
| 22901 | 23029 | } |
| 22902 | 23030 | } |
| 22903 | 23031 | if( azArg==0 ) break; |
| 22904 | 23032 | for(i=0; i<nArg; i++){ |
| 22905 | 23033 | char *z = azArg[i]; |
| 23034 | + char *pFree; | |
| 23035 | + const char *zOut; | |
| 22906 | 23036 | if( z==0 ) z = p->nullValue; |
| 22907 | - sqlite3_fputs(z, p->out); | |
| 23037 | + zOut = escapeOutput(p, z, &pFree); | |
| 23038 | + sqlite3_fputs(zOut, p->out); | |
| 23039 | + if( pFree ) sqlite3_free(pFree); | |
| 22908 | 23040 | sqlite3_fputs((i<nArg-1)? p->colSeparator : p->rowSeparator, p->out); |
| 22909 | 23041 | } |
| 22910 | 23042 | break; |
| 22911 | 23043 | } |
| 22912 | 23044 | case MODE_Www: |
| @@ -24021,10 +24153,11 @@ | ||
| 24021 | 24153 | ** from malloc()) of that first line, which caller should free sometime. |
| 24022 | 24154 | ** Write anything to display on the next line into *pzTail. If this is |
| 24023 | 24155 | ** the last line, write a NULL into *pzTail. (*pzTail is not allocated.) |
| 24024 | 24156 | */ |
| 24025 | 24157 | static char *translateForDisplayAndDup( |
| 24158 | + ShellState *p, /* To access current settings */ | |
| 24026 | 24159 | const unsigned char *z, /* Input text to be transformed */ |
| 24027 | 24160 | const unsigned char **pzTail, /* OUT: Tail of the input for next line */ |
| 24028 | 24161 | int mxWidth, /* Max width. 0 means no limit */ |
| 24029 | 24162 | u8 bWordWrap /* If true, avoid breaking mid-word */ |
| 24030 | 24163 | ){ |
| @@ -24055,28 +24188,31 @@ | ||
| 24055 | 24188 | n++; |
| 24056 | 24189 | i++; |
| 24057 | 24190 | j++; |
| 24058 | 24191 | continue; |
| 24059 | 24192 | } |
| 24193 | + if( c==0 || c=='\n' || (c=='\r' && z[i+1]=='\n') ) break; | |
| 24060 | 24194 | if( c=='\t' ){ |
| 24061 | 24195 | do{ |
| 24062 | 24196 | n++; |
| 24063 | 24197 | j++; |
| 24064 | 24198 | }while( (n&7)!=0 && n<mxWidth ); |
| 24065 | 24199 | i++; |
| 24066 | 24200 | continue; |
| 24067 | 24201 | } |
| 24068 | - break; | |
| 24202 | + n++; | |
| 24203 | + j += 3; | |
| 24204 | + i++; | |
| 24069 | 24205 | } |
| 24070 | 24206 | if( n>=mxWidth && bWordWrap ){ |
| 24071 | 24207 | /* Perhaps try to back up to a better place to break the line */ |
| 24072 | 24208 | for(k=i; k>i/2; k--){ |
| 24073 | - if( isspace(z[k-1]) ) break; | |
| 24209 | + if( IsSpace(z[k-1]) ) break; | |
| 24074 | 24210 | } |
| 24075 | 24211 | if( k<=i/2 ){ |
| 24076 | 24212 | for(k=i; k>i/2; k--){ |
| 24077 | - if( isalnum(z[k-1])!=isalnum(z[k]) && (z[k]&0xc0)!=0x80 ) break; | |
| 24213 | + if( IsAlnum(z[k-1])!=IsAlnum(z[k]) && (z[k]&0xc0)!=0x80 ) break; | |
| 24078 | 24214 | } |
| 24079 | 24215 | } |
| 24080 | 24216 | if( k<=i/2 ){ |
| 24081 | 24217 | k = i; |
| 24082 | 24218 | }else{ |
| @@ -24110,23 +24246,48 @@ | ||
| 24110 | 24246 | if( c>=' ' ){ |
| 24111 | 24247 | n++; |
| 24112 | 24248 | zOut[j++] = z[i++]; |
| 24113 | 24249 | continue; |
| 24114 | 24250 | } |
| 24251 | + if( c==0 ) break; | |
| 24115 | 24252 | if( z[i]=='\t' ){ |
| 24116 | 24253 | do{ |
| 24117 | 24254 | n++; |
| 24118 | 24255 | zOut[j++] = ' '; |
| 24119 | 24256 | }while( (n&7)!=0 && n<mxWidth ); |
| 24120 | 24257 | i++; |
| 24121 | 24258 | continue; |
| 24122 | 24259 | } |
| 24123 | - break; | |
| 24260 | + switch( p->eEscMode ){ | |
| 24261 | + case SHELL_ESC_SYMBOL: | |
| 24262 | + zOut[j++] = 0xe2; | |
| 24263 | + zOut[j++] = 0x90; | |
| 24264 | + zOut[j++] = 0x80 + c; | |
| 24265 | + break; | |
| 24266 | + case SHELL_ESC_ASCII: | |
| 24267 | + zOut[j++] = '^'; | |
| 24268 | + zOut[j++] = 0x40 + c; | |
| 24269 | + break; | |
| 24270 | + case SHELL_ESC_OFF: | |
| 24271 | + zOut[j++] = c; | |
| 24272 | + break; | |
| 24273 | + } | |
| 24274 | + i++; | |
| 24124 | 24275 | } |
| 24125 | 24276 | zOut[j] = 0; |
| 24126 | 24277 | return (char*)zOut; |
| 24127 | 24278 | } |
| 24279 | + | |
| 24280 | +/* Return true if the text string z[] contains characters that need | |
| 24281 | +** unistr() escaping. | |
| 24282 | +*/ | |
| 24283 | +static int needUnistr(const unsigned char *z){ | |
| 24284 | + unsigned char c; | |
| 24285 | + if( z==0 ) return 0; | |
| 24286 | + while( (c = *z)>0x1f || c=='\t' || c=='\n' || (c=='\r' && z[1]=='\n') ){ z++; } | |
| 24287 | + return c!=0; | |
| 24288 | +} | |
| 24128 | 24289 | |
| 24129 | 24290 | /* Extract the value of the i-th current column for pStmt as an SQL literal |
| 24130 | 24291 | ** value. Memory is obtained from sqlite3_malloc64() and must be freed by |
| 24131 | 24292 | ** the caller. |
| 24132 | 24293 | */ |
| @@ -24138,11 +24299,12 @@ | ||
| 24138 | 24299 | case SQLITE_INTEGER: |
| 24139 | 24300 | case SQLITE_FLOAT: { |
| 24140 | 24301 | return sqlite3_mprintf("%s",sqlite3_column_text(pStmt,i)); |
| 24141 | 24302 | } |
| 24142 | 24303 | case SQLITE_TEXT: { |
| 24143 | - return sqlite3_mprintf("%Q",sqlite3_column_text(pStmt,i)); | |
| 24304 | + const unsigned char *zText = sqlite3_column_text(pStmt,i); | |
| 24305 | + return sqlite3_mprintf(needUnistr(zText)?"%#Q":"%Q",zText); | |
| 24144 | 24306 | } |
| 24145 | 24307 | case SQLITE_BLOB: { |
| 24146 | 24308 | int j; |
| 24147 | 24309 | sqlite3_str *pStr = sqlite3_str_new(0); |
| 24148 | 24310 | const unsigned char *a = sqlite3_column_blob(pStmt,i); |
| @@ -24230,11 +24392,11 @@ | ||
| 24230 | 24392 | wx = p->cmOpts.iWrap; |
| 24231 | 24393 | } |
| 24232 | 24394 | if( wx<0 ) wx = -wx; |
| 24233 | 24395 | uz = (const unsigned char*)sqlite3_column_name(pStmt,i); |
| 24234 | 24396 | if( uz==0 ) uz = (u8*)""; |
| 24235 | - azData[i] = translateForDisplayAndDup(uz, &zNotUsed, wx, bw); | |
| 24397 | + azData[i] = translateForDisplayAndDup(p, uz, &zNotUsed, wx, bw); | |
| 24236 | 24398 | } |
| 24237 | 24399 | do{ |
| 24238 | 24400 | int useNextLine = bNextLine; |
| 24239 | 24401 | bNextLine = 0; |
| 24240 | 24402 | if( (nRow+2)*nColumn >= nAlloc ){ |
| @@ -24254,19 +24416,20 @@ | ||
| 24254 | 24416 | if( wx<0 ) wx = -wx; |
| 24255 | 24417 | if( useNextLine ){ |
| 24256 | 24418 | uz = azNextLine[i]; |
| 24257 | 24419 | if( uz==0 ) uz = (u8*)zEmpty; |
| 24258 | 24420 | }else if( p->cmOpts.bQuote ){ |
| 24421 | + assert( azQuoted!=0 ); | |
| 24259 | 24422 | sqlite3_free(azQuoted[i]); |
| 24260 | 24423 | azQuoted[i] = quoted_column(pStmt,i); |
| 24261 | 24424 | uz = (const unsigned char*)azQuoted[i]; |
| 24262 | 24425 | }else{ |
| 24263 | 24426 | uz = (const unsigned char*)sqlite3_column_text(pStmt,i); |
| 24264 | 24427 | if( uz==0 ) uz = (u8*)zShowNull; |
| 24265 | 24428 | } |
| 24266 | 24429 | azData[nRow*nColumn + i] |
| 24267 | - = translateForDisplayAndDup(uz, &azNextLine[i], wx, bw); | |
| 24430 | + = translateForDisplayAndDup(p, uz, &azNextLine[i], wx, bw); | |
| 24268 | 24431 | if( azNextLine[i] ){ |
| 24269 | 24432 | bNextLine = 1; |
| 24270 | 24433 | abRowDiv[nRow-1] = 0; |
| 24271 | 24434 | bMultiLineRowExists = 1; |
| 24272 | 24435 | } |
| @@ -25185,11 +25348,11 @@ | ||
| 25185 | 25348 | #if !defined(SQLITE_SHELL_FIDDLE) |
| 25186 | 25349 | ".log FILE|on|off Turn logging on or off. FILE can be stderr/stdout", |
| 25187 | 25350 | #else |
| 25188 | 25351 | ".log on|off Turn logging on or off.", |
| 25189 | 25352 | #endif |
| 25190 | - ".mode MODE ?OPTIONS? Set output mode", | |
| 25353 | + ".mode ?MODE? ?OPTIONS? Set output mode", | |
| 25191 | 25354 | " MODE is one of:", |
| 25192 | 25355 | " ascii Columns/rows delimited by 0x1F and 0x1E", |
| 25193 | 25356 | " box Tables using unicode box-drawing characters", |
| 25194 | 25357 | " csv Comma-separated values", |
| 25195 | 25358 | " column Output in columns. (See .width)", |
| @@ -25203,10 +25366,11 @@ | ||
| 25203 | 25366 | " quote Escape answers as for SQL", |
| 25204 | 25367 | " table ASCII-art table", |
| 25205 | 25368 | " tabs Tab-separated values", |
| 25206 | 25369 | " tcl TCL list elements", |
| 25207 | 25370 | " OPTIONS: (for columnar modes or insert mode):", |
| 25371 | + " --escape T ctrl-char escape; T is one of: symbol, ascii, off", | |
| 25208 | 25372 | " --wrap N Wrap output lines to no longer than N characters", |
| 25209 | 25373 | " --wordwrap B Wrap or not at word boundaries per B (on/off)", |
| 25210 | 25374 | " --ww Shorthand for \"--wordwrap 1\"", |
| 25211 | 25375 | " --quote Quote output text as SQL literals", |
| 25212 | 25376 | " --noquote Do not quote output text", |
| @@ -25978,11 +26142,11 @@ | ||
| 25978 | 26142 | #if HAVE_LINENOISE==2 |
| 25979 | 26143 | UNUSED_PARAMETER(pUserData); |
| 25980 | 26144 | #endif |
| 25981 | 26145 | if( nLine>(i64)sizeof(zBuf)-30 ) return; |
| 25982 | 26146 | if( zLine[0]=='.' || zLine[0]=='#') return; |
| 25983 | - for(i=nLine-1; i>=0 && (isalnum(zLine[i]) || zLine[i]=='_'); i--){} | |
| 26147 | + for(i=nLine-1; i>=0 && (IsAlnum(zLine[i]) || zLine[i]=='_'); i--){} | |
| 25984 | 26148 | if( i==nLine-1 ) return; |
| 25985 | 26149 | iStart = i+1; |
| 25986 | 26150 | memcpy(zBuf, zLine, iStart); |
| 25987 | 26151 | zSql = sqlite3_mprintf("SELECT DISTINCT candidate COLLATE nocase" |
| 25988 | 26152 | " FROM completion(%Q,%Q) ORDER BY 1", |
| @@ -28218,10 +28382,11 @@ | ||
| 28218 | 28382 | sqlite3_recover_config(p, 789, (void*)zRecoveryDb); /* Debug use only */ |
| 28219 | 28383 | sqlite3_recover_config(p, SQLITE_RECOVER_LOST_AND_FOUND, (void*)zLAF); |
| 28220 | 28384 | sqlite3_recover_config(p, SQLITE_RECOVER_ROWIDS, (void*)&bRowids); |
| 28221 | 28385 | sqlite3_recover_config(p, SQLITE_RECOVER_FREELIST_CORRUPT,(void*)&bFreelist); |
| 28222 | 28386 | |
| 28387 | + sqlite3_fprintf(pState->out, ".dbconfig defensive off\n"); | |
| 28223 | 28388 | sqlite3_recover_run(p); |
| 28224 | 28389 | if( sqlite3_recover_errcode(p)!=SQLITE_OK ){ |
| 28225 | 28390 | const char *zErr = sqlite3_recover_errmsg(p); |
| 28226 | 28391 | int errCode = sqlite3_recover_errcode(p); |
| 28227 | 28392 | sqlite3_fprintf(stderr,"sql error: %s (%d)\n", zErr, errCode); |
| @@ -29943,28 +30108,56 @@ | ||
| 29943 | 30108 | |
| 29944 | 30109 | if( c=='m' && cli_strncmp(azArg[0], "mode", n)==0 ){ |
| 29945 | 30110 | const char *zMode = 0; |
| 29946 | 30111 | const char *zTabname = 0; |
| 29947 | 30112 | int i, n2; |
| 30113 | + int chng = 0; /* 0x01: change to cmopts. 0x02: Any other change */ | |
| 29948 | 30114 | ColModeOpts cmOpts = ColModeOpts_default; |
| 29949 | 30115 | for(i=1; i<nArg; i++){ |
| 29950 | 30116 | const char *z = azArg[i]; |
| 29951 | 30117 | if( optionMatch(z,"wrap") && i+1<nArg ){ |
| 29952 | 30118 | cmOpts.iWrap = integerValue(azArg[++i]); |
| 30119 | + chng |= 1; | |
| 29953 | 30120 | }else if( optionMatch(z,"ww") ){ |
| 29954 | 30121 | cmOpts.bWordWrap = 1; |
| 30122 | + chng |= 1; | |
| 29955 | 30123 | }else if( optionMatch(z,"wordwrap") && i+1<nArg ){ |
| 29956 | 30124 | cmOpts.bWordWrap = (u8)booleanValue(azArg[++i]); |
| 30125 | + chng |= 1; | |
| 29957 | 30126 | }else if( optionMatch(z,"quote") ){ |
| 29958 | 30127 | cmOpts.bQuote = 1; |
| 30128 | + chng |= 1; | |
| 29959 | 30129 | }else if( optionMatch(z,"noquote") ){ |
| 29960 | 30130 | cmOpts.bQuote = 0; |
| 30131 | + chng |= 1; | |
| 30132 | + }else if( optionMatch(z,"escape") && i+1<nArg ){ | |
| 30133 | + /* See similar code at tag-20250224-1 */ | |
| 30134 | + const char *zEsc = azArg[++i]; | |
| 30135 | + int k; | |
| 30136 | + for(k=0; k<ArraySize(shell_EscModeNames); k++){ | |
| 30137 | + if( sqlite3_stricmp(zEsc,shell_EscModeNames[k])==0 ){ | |
| 30138 | + p->eEscMode = k; | |
| 30139 | + chng |= 2; | |
| 30140 | + break; | |
| 30141 | + } | |
| 30142 | + } | |
| 30143 | + if( k>=ArraySize(shell_EscModeNames) ){ | |
| 30144 | + sqlite3_fprintf(stderr, "unknown control character escape mode \"%s\"" | |
| 30145 | + " - choices:", zEsc); | |
| 30146 | + for(k=0; k<ArraySize(shell_EscModeNames); k++){ | |
| 30147 | + sqlite3_fprintf(stderr, " %s", shell_EscModeNames[k]); | |
| 30148 | + } | |
| 30149 | + sqlite3_fprintf(stderr, "\n"); | |
| 30150 | + rc = 1; | |
| 30151 | + goto meta_command_exit; | |
| 30152 | + } | |
| 29961 | 30153 | }else if( zMode==0 ){ |
| 29962 | 30154 | zMode = z; |
| 29963 | 30155 | /* Apply defaults for qbox pseudo-mode. If that |
| 29964 | 30156 | * overwrites already-set values, user was informed of this. |
| 29965 | 30157 | */ |
| 30158 | + chng |= 1; | |
| 29966 | 30159 | if( cli_strcmp(z, "qbox")==0 ){ |
| 29967 | 30160 | ColModeOpts cmo = ColModeOpts_default_qbox; |
| 29968 | 30161 | zMode = "box"; |
| 29969 | 30162 | cmOpts = cmo; |
| 29970 | 30163 | } |
| @@ -29971,10 +30164,11 @@ | ||
| 29971 | 30164 | }else if( zTabname==0 ){ |
| 29972 | 30165 | zTabname = z; |
| 29973 | 30166 | }else if( z[0]=='-' ){ |
| 29974 | 30167 | sqlite3_fprintf(stderr,"unknown option: %s\n", z); |
| 29975 | 30168 | eputz("options:\n" |
| 30169 | + " --escape MODE\n" | |
| 29976 | 30170 | " --noquote\n" |
| 29977 | 30171 | " --quote\n" |
| 29978 | 30172 | " --wordwrap on/off\n" |
| 29979 | 30173 | " --wrap N\n" |
| 29980 | 30174 | " --ww\n"); |
| @@ -29984,24 +30178,33 @@ | ||
| 29984 | 30178 | sqlite3_fprintf(stderr,"extra argument: \"%s\"\n", z); |
| 29985 | 30179 | rc = 1; |
| 29986 | 30180 | goto meta_command_exit; |
| 29987 | 30181 | } |
| 29988 | 30182 | } |
| 29989 | - if( zMode==0 ){ | |
| 30183 | + if( !chng ){ | |
| 29990 | 30184 | if( p->mode==MODE_Column |
| 29991 | 30185 | || (p->mode>=MODE_Markdown && p->mode<=MODE_Box) |
| 29992 | 30186 | ){ |
| 29993 | 30187 | sqlite3_fprintf(p->out, |
| 29994 | - "current output mode: %s --wrap %d --wordwrap %s --%squote\n", | |
| 30188 | + "current output mode: %s --wrap %d --wordwrap %s " | |
| 30189 | + "--%squote --escape %s\n", | |
| 29995 | 30190 | modeDescr[p->mode], p->cmOpts.iWrap, |
| 29996 | 30191 | p->cmOpts.bWordWrap ? "on" : "off", |
| 29997 | - p->cmOpts.bQuote ? "" : "no"); | |
| 30192 | + p->cmOpts.bQuote ? "" : "no", | |
| 30193 | + shell_EscModeNames[p->eEscMode] | |
| 30194 | + ); | |
| 29998 | 30195 | }else{ |
| 29999 | 30196 | sqlite3_fprintf(p->out, |
| 30000 | - "current output mode: %s\n", modeDescr[p->mode]); | |
| 30197 | + "current output mode: %s --escape %s\n", | |
| 30198 | + modeDescr[p->mode], | |
| 30199 | + shell_EscModeNames[p->eEscMode] | |
| 30200 | + ); | |
| 30001 | 30201 | } |
| 30202 | + } | |
| 30203 | + if( zMode==0 ){ | |
| 30002 | 30204 | zMode = modeDescr[p->mode]; |
| 30205 | + if( (chng&1)==0 ) cmOpts = p->cmOpts; | |
| 30003 | 30206 | } |
| 30004 | 30207 | n2 = strlen30(zMode); |
| 30005 | 30208 | if( cli_strncmp(zMode,"lines",n2)==0 ){ |
| 30006 | 30209 | p->mode = MODE_Line; |
| 30007 | 30210 | sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); |
| @@ -30030,10 +30233,15 @@ | ||
| 30030 | 30233 | p->mode = MODE_List; |
| 30031 | 30234 | sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Tab); |
| 30032 | 30235 | }else if( cli_strncmp(zMode,"insert",n2)==0 ){ |
| 30033 | 30236 | p->mode = MODE_Insert; |
| 30034 | 30237 | set_table_name(p, zTabname ? zTabname : "table"); |
| 30238 | + if( p->eEscMode==SHELL_ESC_OFF ){ | |
| 30239 | + ShellSetFlag(p, SHFLG_Newlines); | |
| 30240 | + }else{ | |
| 30241 | + ShellClearFlag(p, SHFLG_Newlines); | |
| 30242 | + } | |
| 30035 | 30243 | }else if( cli_strncmp(zMode,"quote",n2)==0 ){ |
| 30036 | 30244 | p->mode = MODE_Quote; |
| 30037 | 30245 | sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma); |
| 30038 | 30246 | sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); |
| 30039 | 30247 | }else if( cli_strncmp(zMode,"ascii",n2)==0 ){ |
| @@ -32266,11 +32474,11 @@ | ||
| 32266 | 32474 | /* |
| 32267 | 32475 | ** The CLI needs a working sqlite3_complete() to work properly. So error |
| 32268 | 32476 | ** out of the build if compiling with SQLITE_OMIT_COMPLETE. |
| 32269 | 32477 | */ |
| 32270 | 32478 | #ifdef SQLITE_OMIT_COMPLETE |
| 32271 | -# error the CLI application is imcompatable with SQLITE_OMIT_COMPLETE. | |
| 32479 | +# error the CLI application is incompatible with SQLITE_OMIT_COMPLETE. | |
| 32272 | 32480 | #endif |
| 32273 | 32481 | |
| 32274 | 32482 | /* |
| 32275 | 32483 | ** Return true if zSql is a complete SQL statement. Return false if it |
| 32276 | 32484 | ** ends in the middle of a string literal or C-style comment. |
| @@ -32445,11 +32653,11 @@ | ||
| 32445 | 32653 | UNUSED_PARAMETER(in); |
| 32446 | 32654 | UNUSED_PARAMETER(isContinuation); |
| 32447 | 32655 | if(!z || !*z){ |
| 32448 | 32656 | return 0; |
| 32449 | 32657 | } |
| 32450 | - while(*z && isspace(*z)) ++z; | |
| 32658 | + while(*z && IsSpace(*z)) ++z; | |
| 32451 | 32659 | zBegin = z; |
| 32452 | 32660 | for(; *z && '\n'!=*z; ++nZ, ++z){} |
| 32453 | 32661 | if(nZ>0 && '\r'==zBegin[nZ-1]){ |
| 32454 | 32662 | --nZ; |
| 32455 | 32663 | } |
| @@ -32750,10 +32958,11 @@ | ||
| 32750 | 32958 | " -csv set output mode to 'csv'\n" |
| 32751 | 32959 | #if !defined(SQLITE_OMIT_DESERIALIZE) |
| 32752 | 32960 | " -deserialize open the database using sqlite3_deserialize()\n" |
| 32753 | 32961 | #endif |
| 32754 | 32962 | " -echo print inputs before execution\n" |
| 32963 | + " -escape T ctrl-char escape; T is one of: symbol, ascii, off\n" | |
| 32755 | 32964 | " -init FILENAME read/process named file\n" |
| 32756 | 32965 | " -[no]header turn headers on or off\n" |
| 32757 | 32966 | #if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5) |
| 32758 | 32967 | " -heap SIZE Size of heap for memsys3 or memsys5\n" |
| 32759 | 32968 | #endif |
| @@ -32797,11 +33006,11 @@ | ||
| 32797 | 33006 | #ifdef SQLITE_HAVE_ZLIB |
| 32798 | 33007 | " -zip open the file as a ZIP Archive\n" |
| 32799 | 33008 | #endif |
| 32800 | 33009 | ; |
| 32801 | 33010 | static void usage(int showDetail){ |
| 32802 | - sqlite3_fprintf(stderr,"Usage: %s [OPTIONS] [FILENAME [SQL]]\n" | |
| 33011 | + sqlite3_fprintf(stderr,"Usage: %s [OPTIONS] [FILENAME [SQL...]]\n" | |
| 32803 | 33012 | "FILENAME is the name of an SQLite database. A new database is created\n" |
| 32804 | 33013 | "if the file does not previously exist. Defaults to :memory:.\n", Argv0); |
| 32805 | 33014 | if( showDetail ){ |
| 32806 | 33015 | sqlite3_fprintf(stderr,"OPTIONS include:\n%s", zOptions); |
| 32807 | 33016 | }else{ |
| @@ -33183,10 +33392,13 @@ | ||
| 33183 | 33392 | data.zNonce = strdup(cmdline_option_value(argc, argv, ++i)); |
| 33184 | 33393 | }else if( cli_strcmp(z,"-unsafe-testing")==0 ){ |
| 33185 | 33394 | ShellSetFlag(&data,SHFLG_TestingMode); |
| 33186 | 33395 | }else if( cli_strcmp(z,"-safe")==0 ){ |
| 33187 | 33396 | /* no-op - catch this on the second pass */ |
| 33397 | + }else if( cli_strcmp(z,"-escape")==0 && i+1<argc ){ | |
| 33398 | + /* skip over the argument */ | |
| 33399 | + i++; | |
| 33188 | 33400 | } |
| 33189 | 33401 | } |
| 33190 | 33402 | #ifndef SQLITE_SHELL_FIDDLE |
| 33191 | 33403 | if( !bEnableVfstrace ) verify_uninitialized(); |
| 33192 | 33404 | #endif |
| @@ -33282,10 +33494,29 @@ | ||
| 33282 | 33494 | }else if( cli_strcmp(z,"-box")==0 ){ |
| 33283 | 33495 | data.mode = MODE_Box; |
| 33284 | 33496 | }else if( cli_strcmp(z,"-csv")==0 ){ |
| 33285 | 33497 | data.mode = MODE_Csv; |
| 33286 | 33498 | memcpy(data.colSeparator,",",2); |
| 33499 | + }else if( cli_strcmp(z,"-escape")==0 && i+1<argc ){ | |
| 33500 | + /* See similar code at tag-20250224-1 */ | |
| 33501 | + const char *zEsc = argv[++i]; | |
| 33502 | + int k; | |
| 33503 | + for(k=0; k<ArraySize(shell_EscModeNames); k++){ | |
| 33504 | + if( sqlite3_stricmp(zEsc,shell_EscModeNames[k])==0 ){ | |
| 33505 | + data.eEscMode = k; | |
| 33506 | + break; | |
| 33507 | + } | |
| 33508 | + } | |
| 33509 | + if( k>=ArraySize(shell_EscModeNames) ){ | |
| 33510 | + sqlite3_fprintf(stderr, "unknown control character escape mode \"%s\"" | |
| 33511 | + " - choices:", zEsc); | |
| 33512 | + for(k=0; k<ArraySize(shell_EscModeNames); k++){ | |
| 33513 | + sqlite3_fprintf(stderr, " %s", shell_EscModeNames[k]); | |
| 33514 | + } | |
| 33515 | + sqlite3_fprintf(stderr, "\n"); | |
| 33516 | + exit(1); | |
| 33517 | + } | |
| 33287 | 33518 | #ifdef SQLITE_HAVE_ZLIB |
| 33288 | 33519 | }else if( cli_strcmp(z,"-zip")==0 ){ |
| 33289 | 33520 | data.openMode = SHELL_OPEN_ZIPFILE; |
| 33290 | 33521 | #endif |
| 33291 | 33522 | }else if( cli_strcmp(z,"-append")==0 ){ |
| @@ -33443,19 +33674,19 @@ | ||
| 33443 | 33674 | /* Run all arguments that do not begin with '-' as if they were separate |
| 33444 | 33675 | ** command-line inputs, except for the argToSkip argument which contains |
| 33445 | 33676 | ** the database filename. |
| 33446 | 33677 | */ |
| 33447 | 33678 | for(i=0; i<nCmd; i++){ |
| 33679 | + echo_group_input(&data, azCmd[i]); | |
| 33448 | 33680 | if( azCmd[i][0]=='.' ){ |
| 33449 | 33681 | rc = do_meta_command(azCmd[i], &data); |
| 33450 | 33682 | if( rc ){ |
| 33451 | 33683 | if( rc==2 ) rc = 0; |
| 33452 | 33684 | goto shell_main_exit; |
| 33453 | 33685 | } |
| 33454 | 33686 | }else{ |
| 33455 | 33687 | open_db(&data, 0); |
| 33456 | - echo_group_input(&data, azCmd[i]); | |
| 33457 | 33688 | rc = shell_exec(&data, azCmd[i], &zErrMsg); |
| 33458 | 33689 | if( zErrMsg || rc ){ |
| 33459 | 33690 | if( zErrMsg!=0 ){ |
| 33460 | 33691 | shellEmitError(zErrMsg); |
| 33461 | 33692 | }else{ |
| 33462 | 33693 |
| --- extsrc/shell.c | |
| +++ extsrc/shell.c | |
| @@ -234,10 +234,12 @@ | |
| 234 | |
| 235 | /* ctype macros that work with signed characters */ |
| 236 | #define IsSpace(X) isspace((unsigned char)X) |
| 237 | #define IsDigit(X) isdigit((unsigned char)X) |
| 238 | #define ToLower(X) (char)tolower((unsigned char)X) |
| 239 | |
| 240 | #if defined(_WIN32) || defined(WIN32) |
| 241 | #if SQLITE_OS_WINRT |
| 242 | #include <intrin.h> |
| 243 | #endif |
| @@ -849,11 +851,11 @@ | |
| 849 | |
| 850 | /* |
| 851 | ** Prompt strings. Initialized in main. Settable with |
| 852 | ** .prompt main continue |
| 853 | */ |
| 854 | #define PROMPT_LEN_MAX 20 |
| 855 | /* First line prompt. default: "sqlite> " */ |
| 856 | static char mainPrompt[PROMPT_LEN_MAX]; |
| 857 | /* Continuation prompt. default: " ...> " */ |
| 858 | static char continuePrompt[PROMPT_LEN_MAX]; |
| 859 | |
| @@ -1506,13 +1508,13 @@ | |
| 1506 | ** Return '"' if quoting is required. Return 0 if no quoting is required. |
| 1507 | */ |
| 1508 | static char quoteChar(const char *zName){ |
| 1509 | int i; |
| 1510 | if( zName==0 ) return '"'; |
| 1511 | if( !isalpha((unsigned char)zName[0]) && zName[0]!='_' ) return '"'; |
| 1512 | for(i=0; zName[i]; i++){ |
| 1513 | if( !isalnum((unsigned char)zName[i]) && zName[i]!='_' ) return '"'; |
| 1514 | } |
| 1515 | return sqlite3_keyword_check(zName, i) ? '"' : 0; |
| 1516 | } |
| 1517 | |
| 1518 | /* |
| @@ -2425,11 +2427,11 @@ | |
| 2425 | ** content in cases where the content starts |
| 2426 | ** with a digit. |
| 2427 | ** |
| 2428 | ** typeof(Y)='blob' The hash is taken over prefix "Bnnn:" followed |
| 2429 | ** by the binary content of the blob. The "nnn" |
| 2430 | ** in the prefix is the mimimum-length decimal |
| 2431 | ** representation of the byte-length of the blob. |
| 2432 | ** |
| 2433 | ** According to the rules above, all of the following SELECT statements |
| 2434 | ** should return TRUE: |
| 2435 | ** |
| @@ -3646,11 +3648,11 @@ | |
| 3646 | ** |
| 3647 | ** UINT works like BINARY for text, except that embedded strings |
| 3648 | ** of digits compare in numeric order. |
| 3649 | ** |
| 3650 | ** * Leading zeros are handled properly, in the sense that |
| 3651 | ** they do not mess of the maginitude comparison of embedded |
| 3652 | ** strings of digits. "x00123y" is equal to "x123y". |
| 3653 | ** |
| 3654 | ** * Only unsigned integers are recognized. Plus and minus |
| 3655 | ** signs are ignored. Decimal points and exponential notation |
| 3656 | ** are ignored. |
| @@ -3752,10 +3754,13 @@ | |
| 3752 | ** warnings. */ |
| 3753 | #ifndef UNUSED_PARAMETER |
| 3754 | # define UNUSED_PARAMETER(X) (void)(X) |
| 3755 | #endif |
| 3756 | |
| 3757 | |
| 3758 | /* A decimal object */ |
| 3759 | typedef struct Decimal Decimal; |
| 3760 | struct Decimal { |
| 3761 | char sign; /* 0 for positive, 1 for negative */ |
| @@ -3801,11 +3806,11 @@ | |
| 3801 | p->isNull = 0; |
| 3802 | p->nDigit = 0; |
| 3803 | p->nFrac = 0; |
| 3804 | p->a = sqlite3_malloc64( n+1 ); |
| 3805 | if( p->a==0 ) goto new_from_text_failed; |
| 3806 | for(i=0; isspace(zIn[i]); i++){} |
| 3807 | if( zIn[i]=='-' ){ |
| 3808 | p->sign = 1; |
| 3809 | i++; |
| 3810 | }else if( zIn[i]=='+' ){ |
| 3811 | i++; |
| @@ -4456,11 +4461,11 @@ | |
| 4456 | } |
| 4457 | decimal_free(pA); |
| 4458 | decimal_free(pB); |
| 4459 | } |
| 4460 | |
| 4461 | /* Aggregate funcion: decimal_sum(X) |
| 4462 | ** |
| 4463 | ** Works like sum() except that it uses decimal arithmetic for unlimited |
| 4464 | ** precision. |
| 4465 | */ |
| 4466 | static void decimalSumStep( |
| @@ -4817,11 +4822,11 @@ | |
| 4817 | } |
| 4818 | |
| 4819 | /* |
| 4820 | ** Generate an error for a percentile function. |
| 4821 | ** |
| 4822 | ** The error format string must have exactly one occurrance of "%%s()" |
| 4823 | ** (with two '%' characters). That substring will be replaced by the name |
| 4824 | ** of the function. |
| 4825 | */ |
| 4826 | static void percentError(sqlite3_context *pCtx, const char *zFormat, ...){ |
| 4827 | PercentileFunc *pFunc = (PercentileFunc*)sqlite3_user_data(pCtx); |
| @@ -5957,11 +5962,11 @@ | |
| 5957 | ** number format: |
| 5958 | ** |
| 5959 | ** WITH c(name,bin) AS (VALUES |
| 5960 | ** ('minimum positive value', x'0000000000000001'), |
| 5961 | ** ('maximum subnormal value', x'000fffffffffffff'), |
| 5962 | ** ('mininum positive nornal value', x'0010000000000000'), |
| 5963 | ** ('maximum value', x'7fefffffffffffff')) |
| 5964 | ** SELECT c.name, decimal_mul(ieee754_mantissa(c.bin),pow2.v) |
| 5965 | ** FROM pow2, c WHERE pow2.x=ieee754_exponent(c.bin); |
| 5966 | ** |
| 5967 | */ |
| @@ -6344,11 +6349,11 @@ | |
| 6344 | * 2 for UBSAN's satisfaction (and hypothetical 1's complement ALUs.) */ |
| 6345 | smBase += (mxI64/2) * smStep; |
| 6346 | smBase += (mxI64 - mxI64/2) * smStep; |
| 6347 | } |
| 6348 | /* Under UBSAN (or on 1's complement machines), must do this last term |
| 6349 | * in steps to avoid the dreaded (and harmless) signed multiply overlow. */ |
| 6350 | if( ix>=2 ){ |
| 6351 | sqlite3_int64 ix2 = (sqlite3_int64)ix/2; |
| 6352 | smBase += ix2*smStep; |
| 6353 | ix -= ix2; |
| 6354 | } |
| @@ -6724,23 +6729,21 @@ | |
| 6724 | if( pCur->ss.iBase<iMin ){ |
| 6725 | sqlite3_uint64 d = iMin - pCur->ss.iBase; |
| 6726 | pCur->ss.iBase += ((d+szStep-1)/szStep)*szStep; |
| 6727 | } |
| 6728 | if( pCur->ss.iTerm>iMax ){ |
| 6729 | sqlite3_uint64 d = pCur->ss.iTerm - iMax; |
| 6730 | pCur->ss.iTerm -= ((d+szStep-1)/szStep)*szStep; |
| 6731 | } |
| 6732 | }else{ |
| 6733 | sqlite3_int64 szStep = -pCur->ss.iStep; |
| 6734 | assert( szStep>0 ); |
| 6735 | if( pCur->ss.iBase>iMax ){ |
| 6736 | sqlite3_uint64 d = pCur->ss.iBase - iMax; |
| 6737 | pCur->ss.iBase -= ((d+szStep-1)/szStep)*szStep; |
| 6738 | } |
| 6739 | if( pCur->ss.iTerm<iMin ){ |
| 6740 | sqlite3_uint64 d = iMin - pCur->ss.iTerm; |
| 6741 | pCur->ss.iTerm += ((d+szStep-1)/szStep)*szStep; |
| 6742 | } |
| 6743 | } |
| 6744 | } |
| 6745 | |
| 6746 | /* Apply LIMIT and OFFSET constraints, if any */ |
| @@ -9024,10 +9027,15 @@ | |
| 9024 | #include <assert.h> |
| 9025 | #include <string.h> |
| 9026 | #include <ctype.h> |
| 9027 | |
| 9028 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 9029 | |
| 9030 | /* completion_vtab is a subclass of sqlite3_vtab which will |
| 9031 | ** serve as the underlying representation of a completion virtual table |
| 9032 | */ |
| 9033 | typedef struct completion_vtab completion_vtab; |
| @@ -9361,11 +9369,11 @@ | |
| 9361 | if( pCur->zLine==0 ) return SQLITE_NOMEM; |
| 9362 | } |
| 9363 | } |
| 9364 | if( pCur->zLine!=0 && pCur->zPrefix==0 ){ |
| 9365 | int i = pCur->nLine; |
| 9366 | while( i>0 && (isalnum(pCur->zLine[i-1]) || pCur->zLine[i-1]=='_') ){ |
| 9367 | i--; |
| 9368 | } |
| 9369 | pCur->nPrefix = pCur->nLine - i; |
| 9370 | if( pCur->nPrefix>0 ){ |
| 9371 | pCur->zPrefix = sqlite3_mprintf("%.*s", pCur->nPrefix, pCur->zLine + i); |
| @@ -14768,11 +14776,11 @@ | |
| 14768 | /* Register the auth callback with dbv */ |
| 14769 | if( rc==SQLITE_OK ){ |
| 14770 | sqlite3_set_authorizer(pNew->dbv, idxAuthCallback, (void*)pNew); |
| 14771 | } |
| 14772 | |
| 14773 | /* If an error has occurred, free the new object and reutrn NULL. Otherwise, |
| 14774 | ** return the new sqlite3expert handle. */ |
| 14775 | if( rc!=SQLITE_OK ){ |
| 14776 | sqlite3_expert_destroy(pNew); |
| 14777 | pNew = 0; |
| 14778 | } |
| @@ -16290,11 +16298,11 @@ | |
| 16290 | ** |
| 16291 | ** PRAGMA vfstrace('+all'); |
| 16292 | ** |
| 16293 | ** Individual APIs can be enabled or disabled by name, with or without |
| 16294 | ** the initial "x" character. For example, to set up for tracing lock |
| 16295 | ** primatives only: |
| 16296 | ** |
| 16297 | ** PRAGMA vfstrace('-all, +Lock,Unlock,ShmLock'); |
| 16298 | ** |
| 16299 | ** The argument to the vfstrace pragma ignores capitalization and any |
| 16300 | ** characters other than alphabetics, '+', and '-'. |
| @@ -18700,10 +18708,20 @@ | |
| 18700 | |
| 18701 | /* typedef unsigned int u32; */ |
| 18702 | /* typedef unsigned char u8; */ |
| 18703 | /* typedef sqlite3_int64 i64; */ |
| 18704 | |
| 18705 | typedef struct RecoverTable RecoverTable; |
| 18706 | typedef struct RecoverColumn RecoverColumn; |
| 18707 | |
| 18708 | /* |
| 18709 | ** When recovering rows of data that can be associated with table |
| @@ -18807,12 +18825,15 @@ | |
| 18807 | ** (aElem[iKey/32] & (1 << (iKey%32))) ? 1 : 0 |
| 18808 | */ |
| 18809 | typedef struct RecoverBitmap RecoverBitmap; |
| 18810 | struct RecoverBitmap { |
| 18811 | i64 nPg; /* Size of bitmap */ |
| 18812 | u32 aElem[1]; /* Array of 32-bit bitmasks */ |
| 18813 | }; |
| 18814 | |
| 18815 | /* |
| 18816 | ** State variables (part of the sqlite3_recover structure) used while |
| 18817 | ** recovering data for tables identified in the recovered schema (state |
| 18818 | ** RECOVER_STATE_WRITING). |
| @@ -19049,11 +19070,11 @@ | |
| 19049 | ** large enough to store a bit for all page numbers between 1 and nPg, |
| 19050 | ** inclusive. The bitmap is initially zeroed. |
| 19051 | */ |
| 19052 | static RecoverBitmap *recoverBitmapAlloc(sqlite3_recover *p, i64 nPg){ |
| 19053 | int nElem = (nPg+1+31) / 32; |
| 19054 | int nByte = sizeof(RecoverBitmap) + nElem*sizeof(u32); |
| 19055 | RecoverBitmap *pRet = (RecoverBitmap*)recoverMalloc(p, nByte); |
| 19056 | |
| 19057 | if( pRet ){ |
| 19058 | pRet->nPg = nPg; |
| 19059 | } |
| @@ -21241,41 +21262,57 @@ | |
| 21241 | ** function is called. |
| 21242 | */ |
| 21243 | static void recoverStep(sqlite3_recover *p){ |
| 21244 | assert( p && p->errCode==SQLITE_OK ); |
| 21245 | switch( p->eState ){ |
| 21246 | case RECOVER_STATE_INIT: |
| 21247 | /* This is the very first call to sqlite3_recover_step() on this object. |
| 21248 | */ |
| 21249 | recoverSqlCallback(p, "BEGIN"); |
| 21250 | recoverSqlCallback(p, "PRAGMA writable_schema = on"); |
| 21251 | |
| 21252 | recoverEnterMutex(); |
| 21253 | recoverInstallWrapper(p); |
| 21254 | |
| 21255 | /* Open the output database. And register required virtual tables and |
| 21256 | ** user functions with the new handle. */ |
| 21257 | recoverOpenOutput(p); |
| 21258 | |
| 21259 | /* Open transactions on both the input and output databases. */ |
| 21260 | sqlite3_file_control(p->dbIn, p->zDb, SQLITE_FCNTL_RESET_CACHE, 0); |
| 21261 | recoverExec(p, p->dbIn, "PRAGMA writable_schema = on"); |
| 21262 | recoverExec(p, p->dbIn, "BEGIN"); |
| 21263 | if( p->errCode==SQLITE_OK ) p->bCloseTransaction = 1; |
| 21264 | recoverExec(p, p->dbIn, "SELECT 1 FROM sqlite_schema"); |
| 21265 | recoverTransferSettings(p); |
| 21266 | recoverOpenRecovery(p); |
| 21267 | recoverCacheSchema(p); |
| 21268 | |
| 21269 | recoverUninstallWrapper(p); |
| 21270 | recoverLeaveMutex(); |
| 21271 | |
| 21272 | recoverExec(p, p->dbOut, "BEGIN"); |
| 21273 | |
| 21274 | recoverWriteSchema1(p); |
| 21275 | p->eState = RECOVER_STATE_WRITING; |
| 21276 | break; |
| 21277 | |
| 21278 | case RECOVER_STATE_WRITING: { |
| 21279 | if( p->w1.pTbls==0 ){ |
| 21280 | recoverWriteDataInit(p); |
| 21281 | } |
| @@ -21610,10 +21647,11 @@ | |
| 21610 | u8 eTraceType; /* SHELL_TRACE_* value for type of trace */ |
| 21611 | u8 bSafeMode; /* True to prohibit unsafe operations */ |
| 21612 | u8 bSafeModePersist; /* The long-term value of bSafeMode */ |
| 21613 | u8 eRestoreState; /* See comments above doAutoDetectRestore() */ |
| 21614 | u8 crlfMode; /* Do NL-to-CRLF translations when enabled (maybe) */ |
| 21615 | ColModeOpts cmOpts; /* Option values affecting columnar mode output */ |
| 21616 | unsigned statsOn; /* True to display memory stats before each finalize */ |
| 21617 | unsigned mEqpLines; /* Mask of vertical lines in the EQP output graph */ |
| 21618 | int inputNesting; /* Track nesting level of .read and other redirects */ |
| 21619 | int outCount; /* Revert to stdout when reaching zero */ |
| @@ -21710,10 +21748,19 @@ | |
| 21710 | #define SHELL_PROGRESS_RESET 0x02 /* Reset the count when the progress |
| 21711 | ** callback limit is reached, and for each |
| 21712 | ** top-level SQL statement */ |
| 21713 | #define SHELL_PROGRESS_ONCE 0x04 /* Cancel the --limit after firing once */ |
| 21714 | |
| 21715 | /* |
| 21716 | ** These are the allowed shellFlgs values |
| 21717 | */ |
| 21718 | #define SHFLG_Pagecache 0x00000001 /* The --pagecache option is used */ |
| 21719 | #define SHFLG_Lookaside 0x00000002 /* Lookaside memory is used */ |
| @@ -22047,63 +22094,79 @@ | |
| 22047 | sqlite3_fprintf(out, "X'%s'", zStr); |
| 22048 | sqlite3_free(zStr); |
| 22049 | } |
| 22050 | |
| 22051 | /* |
| 22052 | ** Find a string that is not found anywhere in z[]. Return a pointer |
| 22053 | ** to that string. |
| 22054 | ** |
| 22055 | ** Try to use zA and zB first. If both of those are already found in z[] |
| 22056 | ** then make up some string and store it in the buffer zBuf. |
| 22057 | */ |
| 22058 | static const char *unused_string( |
| 22059 | const char *z, /* Result must not appear anywhere in z */ |
| 22060 | const char *zA, const char *zB, /* Try these first */ |
| 22061 | char *zBuf /* Space to store a generated string */ |
| 22062 | ){ |
| 22063 | unsigned i = 0; |
| 22064 | if( strstr(z, zA)==0 ) return zA; |
| 22065 | if( strstr(z, zB)==0 ) return zB; |
| 22066 | do{ |
| 22067 | sqlite3_snprintf(20,zBuf,"(%s%u)", zA, i++); |
| 22068 | }while( strstr(z,zBuf)!=0 ); |
| 22069 | return zBuf; |
| 22070 | } |
| 22071 | |
| 22072 | /* |
| 22073 | ** Output the given string as a quoted string using SQL quoting conventions. |
| 22074 | ** |
| 22075 | ** See also: output_quoted_escaped_string() |
| 22076 | */ |
| 22077 | static void output_quoted_string(ShellState *p, const char *z){ |
| 22078 | int i; |
| 22079 | char c; |
| 22080 | FILE *out = p->out; |
| 22081 | sqlite3_fsetmode(out, _O_BINARY); |
| 22082 | if( z==0 ) return; |
| 22083 | for(i=0; (c = z[i])!=0 && c!='\''; i++){} |
| 22084 | if( c==0 ){ |
| 22085 | sqlite3_fprintf(out, "'%s'",z); |
| 22086 | }else{ |
| 22087 | sqlite3_fputs("'", out); |
| 22088 | while( *z ){ |
| 22089 | for(i=0; (c = z[i])!=0 && c!='\''; i++){} |
| 22090 | if( c=='\'' ) i++; |
| 22091 | if( i ){ |
| 22092 | sqlite3_fprintf(out, "%.*s", i, z); |
| 22093 | z += i; |
| 22094 | } |
| 22095 | if( c=='\'' ){ |
| 22096 | sqlite3_fputs("'", out); |
| 22097 | continue; |
| 22098 | } |
| 22099 | if( c==0 ){ |
| 22100 | break; |
| 22101 | } |
| 22102 | z++; |
| 22103 | } |
| 22104 | sqlite3_fputs("'", out); |
| 22105 | } |
| 22106 | setCrlfMode(p); |
| 22107 | } |
| 22108 | |
| 22109 | /* |
| @@ -22114,65 +22177,19 @@ | |
| 22114 | ** |
| 22115 | ** This is like output_quoted_string() but with the addition of the \r\n |
| 22116 | ** escape mechanism. |
| 22117 | */ |
| 22118 | static void output_quoted_escaped_string(ShellState *p, const char *z){ |
| 22119 | int i; |
| 22120 | char c; |
| 22121 | FILE *out = p->out; |
| 22122 | sqlite3_fsetmode(out, _O_BINARY); |
| 22123 | for(i=0; (c = z[i])!=0 && c!='\'' && c!='\n' && c!='\r'; i++){} |
| 22124 | if( c==0 ){ |
| 22125 | sqlite3_fprintf(out, "'%s'",z); |
| 22126 | }else{ |
| 22127 | const char *zNL = 0; |
| 22128 | const char *zCR = 0; |
| 22129 | int nNL = 0; |
| 22130 | int nCR = 0; |
| 22131 | char zBuf1[20], zBuf2[20]; |
| 22132 | for(i=0; z[i]; i++){ |
| 22133 | if( z[i]=='\n' ) nNL++; |
| 22134 | if( z[i]=='\r' ) nCR++; |
| 22135 | } |
| 22136 | if( nNL ){ |
| 22137 | sqlite3_fputs("replace(", out); |
| 22138 | zNL = unused_string(z, "\\n", "\\012", zBuf1); |
| 22139 | } |
| 22140 | if( nCR ){ |
| 22141 | sqlite3_fputs("replace(", out); |
| 22142 | zCR = unused_string(z, "\\r", "\\015", zBuf2); |
| 22143 | } |
| 22144 | sqlite3_fputs("'", out); |
| 22145 | while( *z ){ |
| 22146 | for(i=0; (c = z[i])!=0 && c!='\n' && c!='\r' && c!='\''; i++){} |
| 22147 | if( c=='\'' ) i++; |
| 22148 | if( i ){ |
| 22149 | sqlite3_fprintf(out, "%.*s", i, z); |
| 22150 | z += i; |
| 22151 | } |
| 22152 | if( c=='\'' ){ |
| 22153 | sqlite3_fputs("'", out); |
| 22154 | continue; |
| 22155 | } |
| 22156 | if( c==0 ){ |
| 22157 | break; |
| 22158 | } |
| 22159 | z++; |
| 22160 | if( c=='\n' ){ |
| 22161 | sqlite3_fputs(zNL, out); |
| 22162 | continue; |
| 22163 | } |
| 22164 | sqlite3_fputs(zCR, out); |
| 22165 | } |
| 22166 | sqlite3_fputs("'", out); |
| 22167 | if( nCR ){ |
| 22168 | sqlite3_fprintf(out, ",'%s',char(13))", zCR); |
| 22169 | } |
| 22170 | if( nNL ){ |
| 22171 | sqlite3_fprintf(out, ",'%s',char(10))", zNL); |
| 22172 | } |
| 22173 | } |
| 22174 | setCrlfMode(p); |
| 22175 | } |
| 22176 | |
| 22177 | /* |
| 22178 | ** Find earliest of chars within s specified in zAny. |
| @@ -22317,10 +22334,97 @@ | |
| 22317 | sqlite3_fputs(ace+1, out); |
| 22318 | } |
| 22319 | } |
| 22320 | sqlite3_fputs(zq, out); |
| 22321 | } |
| 22322 | |
| 22323 | /* |
| 22324 | ** Output the given string with characters that are special to |
| 22325 | ** HTML escaped. |
| 22326 | */ |
| @@ -22761,12 +22865,16 @@ | |
| 22761 | int len = strlen30(azCol[i] ? azCol[i] : ""); |
| 22762 | if( len>w ) w = len; |
| 22763 | } |
| 22764 | if( p->cnt++>0 ) sqlite3_fputs(p->rowSeparator, p->out); |
| 22765 | for(i=0; i<nArg; i++){ |
| 22766 | sqlite3_fprintf(p->out, "%*s = %s%s", w, azCol[i], |
| 22767 | azArg[i] ? azArg[i] : p->nullValue, p->rowSeparator); |
| 22768 | } |
| 22769 | break; |
| 22770 | } |
| 22771 | case MODE_ScanExp: |
| 22772 | case MODE_Explain: { |
| @@ -22832,18 +22940,22 @@ | |
| 22832 | int j; |
| 22833 | int nParen = 0; |
| 22834 | char cEnd = 0; |
| 22835 | char c; |
| 22836 | int nLine = 0; |
| 22837 | assert( nArg==1 ); |
| 22838 | if( azArg[0]==0 ) break; |
| 22839 | if( sqlite3_strlike("CREATE VIEW%", azArg[0], 0)==0 |
| 22840 | || sqlite3_strlike("CREATE TRIG%", azArg[0], 0)==0 |
| 22841 | ){ |
| 22842 | sqlite3_fprintf(p->out, "%s;\n", azArg[0]); |
| 22843 | break; |
| 22844 | } |
| 22845 | z = sqlite3_mprintf("%s", azArg[0]); |
| 22846 | shell_check_oom(z); |
| 22847 | j = 0; |
| 22848 | for(i=0; IsSpace(z[i]); i++){} |
| 22849 | for(; (c = z[i])!=0; i++){ |
| @@ -22869,18 +22981,30 @@ | |
| 22869 | cEnd = '\n'; |
| 22870 | }else if( c=='(' ){ |
| 22871 | nParen++; |
| 22872 | }else if( c==')' ){ |
| 22873 | nParen--; |
| 22874 | if( nLine>0 && nParen==0 && j>0 ){ |
| 22875 | printSchemaLineN(p->out, z, j, "\n"); |
| 22876 | j = 0; |
| 22877 | } |
| 22878 | } |
| 22879 | z[j++] = c; |
| 22880 | if( nParen==1 && cEnd==0 |
| 22881 | && (c=='(' || c=='\n' || (c==',' && !wsToEol(z+i+1))) |
| 22882 | ){ |
| 22883 | if( c=='\n' ) j--; |
| 22884 | printSchemaLineN(p->out, z, j, "\n "); |
| 22885 | j = 0; |
| 22886 | nLine++; |
| @@ -22894,19 +23018,27 @@ | |
| 22894 | break; |
| 22895 | } |
| 22896 | case MODE_List: { |
| 22897 | if( p->cnt++==0 && p->showHeader ){ |
| 22898 | for(i=0; i<nArg; i++){ |
| 22899 | sqlite3_fprintf(p->out, "%s%s", azCol[i], |
| 22900 | i==nArg-1 ? p->rowSeparator : p->colSeparator); |
| 22901 | } |
| 22902 | } |
| 22903 | if( azArg==0 ) break; |
| 22904 | for(i=0; i<nArg; i++){ |
| 22905 | char *z = azArg[i]; |
| 22906 | if( z==0 ) z = p->nullValue; |
| 22907 | sqlite3_fputs(z, p->out); |
| 22908 | sqlite3_fputs((i<nArg-1)? p->colSeparator : p->rowSeparator, p->out); |
| 22909 | } |
| 22910 | break; |
| 22911 | } |
| 22912 | case MODE_Www: |
| @@ -24021,10 +24153,11 @@ | |
| 24021 | ** from malloc()) of that first line, which caller should free sometime. |
| 24022 | ** Write anything to display on the next line into *pzTail. If this is |
| 24023 | ** the last line, write a NULL into *pzTail. (*pzTail is not allocated.) |
| 24024 | */ |
| 24025 | static char *translateForDisplayAndDup( |
| 24026 | const unsigned char *z, /* Input text to be transformed */ |
| 24027 | const unsigned char **pzTail, /* OUT: Tail of the input for next line */ |
| 24028 | int mxWidth, /* Max width. 0 means no limit */ |
| 24029 | u8 bWordWrap /* If true, avoid breaking mid-word */ |
| 24030 | ){ |
| @@ -24055,28 +24188,31 @@ | |
| 24055 | n++; |
| 24056 | i++; |
| 24057 | j++; |
| 24058 | continue; |
| 24059 | } |
| 24060 | if( c=='\t' ){ |
| 24061 | do{ |
| 24062 | n++; |
| 24063 | j++; |
| 24064 | }while( (n&7)!=0 && n<mxWidth ); |
| 24065 | i++; |
| 24066 | continue; |
| 24067 | } |
| 24068 | break; |
| 24069 | } |
| 24070 | if( n>=mxWidth && bWordWrap ){ |
| 24071 | /* Perhaps try to back up to a better place to break the line */ |
| 24072 | for(k=i; k>i/2; k--){ |
| 24073 | if( isspace(z[k-1]) ) break; |
| 24074 | } |
| 24075 | if( k<=i/2 ){ |
| 24076 | for(k=i; k>i/2; k--){ |
| 24077 | if( isalnum(z[k-1])!=isalnum(z[k]) && (z[k]&0xc0)!=0x80 ) break; |
| 24078 | } |
| 24079 | } |
| 24080 | if( k<=i/2 ){ |
| 24081 | k = i; |
| 24082 | }else{ |
| @@ -24110,23 +24246,48 @@ | |
| 24110 | if( c>=' ' ){ |
| 24111 | n++; |
| 24112 | zOut[j++] = z[i++]; |
| 24113 | continue; |
| 24114 | } |
| 24115 | if( z[i]=='\t' ){ |
| 24116 | do{ |
| 24117 | n++; |
| 24118 | zOut[j++] = ' '; |
| 24119 | }while( (n&7)!=0 && n<mxWidth ); |
| 24120 | i++; |
| 24121 | continue; |
| 24122 | } |
| 24123 | break; |
| 24124 | } |
| 24125 | zOut[j] = 0; |
| 24126 | return (char*)zOut; |
| 24127 | } |
| 24128 | |
| 24129 | /* Extract the value of the i-th current column for pStmt as an SQL literal |
| 24130 | ** value. Memory is obtained from sqlite3_malloc64() and must be freed by |
| 24131 | ** the caller. |
| 24132 | */ |
| @@ -24138,11 +24299,12 @@ | |
| 24138 | case SQLITE_INTEGER: |
| 24139 | case SQLITE_FLOAT: { |
| 24140 | return sqlite3_mprintf("%s",sqlite3_column_text(pStmt,i)); |
| 24141 | } |
| 24142 | case SQLITE_TEXT: { |
| 24143 | return sqlite3_mprintf("%Q",sqlite3_column_text(pStmt,i)); |
| 24144 | } |
| 24145 | case SQLITE_BLOB: { |
| 24146 | int j; |
| 24147 | sqlite3_str *pStr = sqlite3_str_new(0); |
| 24148 | const unsigned char *a = sqlite3_column_blob(pStmt,i); |
| @@ -24230,11 +24392,11 @@ | |
| 24230 | wx = p->cmOpts.iWrap; |
| 24231 | } |
| 24232 | if( wx<0 ) wx = -wx; |
| 24233 | uz = (const unsigned char*)sqlite3_column_name(pStmt,i); |
| 24234 | if( uz==0 ) uz = (u8*)""; |
| 24235 | azData[i] = translateForDisplayAndDup(uz, &zNotUsed, wx, bw); |
| 24236 | } |
| 24237 | do{ |
| 24238 | int useNextLine = bNextLine; |
| 24239 | bNextLine = 0; |
| 24240 | if( (nRow+2)*nColumn >= nAlloc ){ |
| @@ -24254,19 +24416,20 @@ | |
| 24254 | if( wx<0 ) wx = -wx; |
| 24255 | if( useNextLine ){ |
| 24256 | uz = azNextLine[i]; |
| 24257 | if( uz==0 ) uz = (u8*)zEmpty; |
| 24258 | }else if( p->cmOpts.bQuote ){ |
| 24259 | sqlite3_free(azQuoted[i]); |
| 24260 | azQuoted[i] = quoted_column(pStmt,i); |
| 24261 | uz = (const unsigned char*)azQuoted[i]; |
| 24262 | }else{ |
| 24263 | uz = (const unsigned char*)sqlite3_column_text(pStmt,i); |
| 24264 | if( uz==0 ) uz = (u8*)zShowNull; |
| 24265 | } |
| 24266 | azData[nRow*nColumn + i] |
| 24267 | = translateForDisplayAndDup(uz, &azNextLine[i], wx, bw); |
| 24268 | if( azNextLine[i] ){ |
| 24269 | bNextLine = 1; |
| 24270 | abRowDiv[nRow-1] = 0; |
| 24271 | bMultiLineRowExists = 1; |
| 24272 | } |
| @@ -25185,11 +25348,11 @@ | |
| 25185 | #if !defined(SQLITE_SHELL_FIDDLE) |
| 25186 | ".log FILE|on|off Turn logging on or off. FILE can be stderr/stdout", |
| 25187 | #else |
| 25188 | ".log on|off Turn logging on or off.", |
| 25189 | #endif |
| 25190 | ".mode MODE ?OPTIONS? Set output mode", |
| 25191 | " MODE is one of:", |
| 25192 | " ascii Columns/rows delimited by 0x1F and 0x1E", |
| 25193 | " box Tables using unicode box-drawing characters", |
| 25194 | " csv Comma-separated values", |
| 25195 | " column Output in columns. (See .width)", |
| @@ -25203,10 +25366,11 @@ | |
| 25203 | " quote Escape answers as for SQL", |
| 25204 | " table ASCII-art table", |
| 25205 | " tabs Tab-separated values", |
| 25206 | " tcl TCL list elements", |
| 25207 | " OPTIONS: (for columnar modes or insert mode):", |
| 25208 | " --wrap N Wrap output lines to no longer than N characters", |
| 25209 | " --wordwrap B Wrap or not at word boundaries per B (on/off)", |
| 25210 | " --ww Shorthand for \"--wordwrap 1\"", |
| 25211 | " --quote Quote output text as SQL literals", |
| 25212 | " --noquote Do not quote output text", |
| @@ -25978,11 +26142,11 @@ | |
| 25978 | #if HAVE_LINENOISE==2 |
| 25979 | UNUSED_PARAMETER(pUserData); |
| 25980 | #endif |
| 25981 | if( nLine>(i64)sizeof(zBuf)-30 ) return; |
| 25982 | if( zLine[0]=='.' || zLine[0]=='#') return; |
| 25983 | for(i=nLine-1; i>=0 && (isalnum(zLine[i]) || zLine[i]=='_'); i--){} |
| 25984 | if( i==nLine-1 ) return; |
| 25985 | iStart = i+1; |
| 25986 | memcpy(zBuf, zLine, iStart); |
| 25987 | zSql = sqlite3_mprintf("SELECT DISTINCT candidate COLLATE nocase" |
| 25988 | " FROM completion(%Q,%Q) ORDER BY 1", |
| @@ -28218,10 +28382,11 @@ | |
| 28218 | sqlite3_recover_config(p, 789, (void*)zRecoveryDb); /* Debug use only */ |
| 28219 | sqlite3_recover_config(p, SQLITE_RECOVER_LOST_AND_FOUND, (void*)zLAF); |
| 28220 | sqlite3_recover_config(p, SQLITE_RECOVER_ROWIDS, (void*)&bRowids); |
| 28221 | sqlite3_recover_config(p, SQLITE_RECOVER_FREELIST_CORRUPT,(void*)&bFreelist); |
| 28222 | |
| 28223 | sqlite3_recover_run(p); |
| 28224 | if( sqlite3_recover_errcode(p)!=SQLITE_OK ){ |
| 28225 | const char *zErr = sqlite3_recover_errmsg(p); |
| 28226 | int errCode = sqlite3_recover_errcode(p); |
| 28227 | sqlite3_fprintf(stderr,"sql error: %s (%d)\n", zErr, errCode); |
| @@ -29943,28 +30108,56 @@ | |
| 29943 | |
| 29944 | if( c=='m' && cli_strncmp(azArg[0], "mode", n)==0 ){ |
| 29945 | const char *zMode = 0; |
| 29946 | const char *zTabname = 0; |
| 29947 | int i, n2; |
| 29948 | ColModeOpts cmOpts = ColModeOpts_default; |
| 29949 | for(i=1; i<nArg; i++){ |
| 29950 | const char *z = azArg[i]; |
| 29951 | if( optionMatch(z,"wrap") && i+1<nArg ){ |
| 29952 | cmOpts.iWrap = integerValue(azArg[++i]); |
| 29953 | }else if( optionMatch(z,"ww") ){ |
| 29954 | cmOpts.bWordWrap = 1; |
| 29955 | }else if( optionMatch(z,"wordwrap") && i+1<nArg ){ |
| 29956 | cmOpts.bWordWrap = (u8)booleanValue(azArg[++i]); |
| 29957 | }else if( optionMatch(z,"quote") ){ |
| 29958 | cmOpts.bQuote = 1; |
| 29959 | }else if( optionMatch(z,"noquote") ){ |
| 29960 | cmOpts.bQuote = 0; |
| 29961 | }else if( zMode==0 ){ |
| 29962 | zMode = z; |
| 29963 | /* Apply defaults for qbox pseudo-mode. If that |
| 29964 | * overwrites already-set values, user was informed of this. |
| 29965 | */ |
| 29966 | if( cli_strcmp(z, "qbox")==0 ){ |
| 29967 | ColModeOpts cmo = ColModeOpts_default_qbox; |
| 29968 | zMode = "box"; |
| 29969 | cmOpts = cmo; |
| 29970 | } |
| @@ -29971,10 +30164,11 @@ | |
| 29971 | }else if( zTabname==0 ){ |
| 29972 | zTabname = z; |
| 29973 | }else if( z[0]=='-' ){ |
| 29974 | sqlite3_fprintf(stderr,"unknown option: %s\n", z); |
| 29975 | eputz("options:\n" |
| 29976 | " --noquote\n" |
| 29977 | " --quote\n" |
| 29978 | " --wordwrap on/off\n" |
| 29979 | " --wrap N\n" |
| 29980 | " --ww\n"); |
| @@ -29984,24 +30178,33 @@ | |
| 29984 | sqlite3_fprintf(stderr,"extra argument: \"%s\"\n", z); |
| 29985 | rc = 1; |
| 29986 | goto meta_command_exit; |
| 29987 | } |
| 29988 | } |
| 29989 | if( zMode==0 ){ |
| 29990 | if( p->mode==MODE_Column |
| 29991 | || (p->mode>=MODE_Markdown && p->mode<=MODE_Box) |
| 29992 | ){ |
| 29993 | sqlite3_fprintf(p->out, |
| 29994 | "current output mode: %s --wrap %d --wordwrap %s --%squote\n", |
| 29995 | modeDescr[p->mode], p->cmOpts.iWrap, |
| 29996 | p->cmOpts.bWordWrap ? "on" : "off", |
| 29997 | p->cmOpts.bQuote ? "" : "no"); |
| 29998 | }else{ |
| 29999 | sqlite3_fprintf(p->out, |
| 30000 | "current output mode: %s\n", modeDescr[p->mode]); |
| 30001 | } |
| 30002 | zMode = modeDescr[p->mode]; |
| 30003 | } |
| 30004 | n2 = strlen30(zMode); |
| 30005 | if( cli_strncmp(zMode,"lines",n2)==0 ){ |
| 30006 | p->mode = MODE_Line; |
| 30007 | sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); |
| @@ -30030,10 +30233,15 @@ | |
| 30030 | p->mode = MODE_List; |
| 30031 | sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Tab); |
| 30032 | }else if( cli_strncmp(zMode,"insert",n2)==0 ){ |
| 30033 | p->mode = MODE_Insert; |
| 30034 | set_table_name(p, zTabname ? zTabname : "table"); |
| 30035 | }else if( cli_strncmp(zMode,"quote",n2)==0 ){ |
| 30036 | p->mode = MODE_Quote; |
| 30037 | sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma); |
| 30038 | sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); |
| 30039 | }else if( cli_strncmp(zMode,"ascii",n2)==0 ){ |
| @@ -32266,11 +32474,11 @@ | |
| 32266 | /* |
| 32267 | ** The CLI needs a working sqlite3_complete() to work properly. So error |
| 32268 | ** out of the build if compiling with SQLITE_OMIT_COMPLETE. |
| 32269 | */ |
| 32270 | #ifdef SQLITE_OMIT_COMPLETE |
| 32271 | # error the CLI application is imcompatable with SQLITE_OMIT_COMPLETE. |
| 32272 | #endif |
| 32273 | |
| 32274 | /* |
| 32275 | ** Return true if zSql is a complete SQL statement. Return false if it |
| 32276 | ** ends in the middle of a string literal or C-style comment. |
| @@ -32445,11 +32653,11 @@ | |
| 32445 | UNUSED_PARAMETER(in); |
| 32446 | UNUSED_PARAMETER(isContinuation); |
| 32447 | if(!z || !*z){ |
| 32448 | return 0; |
| 32449 | } |
| 32450 | while(*z && isspace(*z)) ++z; |
| 32451 | zBegin = z; |
| 32452 | for(; *z && '\n'!=*z; ++nZ, ++z){} |
| 32453 | if(nZ>0 && '\r'==zBegin[nZ-1]){ |
| 32454 | --nZ; |
| 32455 | } |
| @@ -32750,10 +32958,11 @@ | |
| 32750 | " -csv set output mode to 'csv'\n" |
| 32751 | #if !defined(SQLITE_OMIT_DESERIALIZE) |
| 32752 | " -deserialize open the database using sqlite3_deserialize()\n" |
| 32753 | #endif |
| 32754 | " -echo print inputs before execution\n" |
| 32755 | " -init FILENAME read/process named file\n" |
| 32756 | " -[no]header turn headers on or off\n" |
| 32757 | #if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5) |
| 32758 | " -heap SIZE Size of heap for memsys3 or memsys5\n" |
| 32759 | #endif |
| @@ -32797,11 +33006,11 @@ | |
| 32797 | #ifdef SQLITE_HAVE_ZLIB |
| 32798 | " -zip open the file as a ZIP Archive\n" |
| 32799 | #endif |
| 32800 | ; |
| 32801 | static void usage(int showDetail){ |
| 32802 | sqlite3_fprintf(stderr,"Usage: %s [OPTIONS] [FILENAME [SQL]]\n" |
| 32803 | "FILENAME is the name of an SQLite database. A new database is created\n" |
| 32804 | "if the file does not previously exist. Defaults to :memory:.\n", Argv0); |
| 32805 | if( showDetail ){ |
| 32806 | sqlite3_fprintf(stderr,"OPTIONS include:\n%s", zOptions); |
| 32807 | }else{ |
| @@ -33183,10 +33392,13 @@ | |
| 33183 | data.zNonce = strdup(cmdline_option_value(argc, argv, ++i)); |
| 33184 | }else if( cli_strcmp(z,"-unsafe-testing")==0 ){ |
| 33185 | ShellSetFlag(&data,SHFLG_TestingMode); |
| 33186 | }else if( cli_strcmp(z,"-safe")==0 ){ |
| 33187 | /* no-op - catch this on the second pass */ |
| 33188 | } |
| 33189 | } |
| 33190 | #ifndef SQLITE_SHELL_FIDDLE |
| 33191 | if( !bEnableVfstrace ) verify_uninitialized(); |
| 33192 | #endif |
| @@ -33282,10 +33494,29 @@ | |
| 33282 | }else if( cli_strcmp(z,"-box")==0 ){ |
| 33283 | data.mode = MODE_Box; |
| 33284 | }else if( cli_strcmp(z,"-csv")==0 ){ |
| 33285 | data.mode = MODE_Csv; |
| 33286 | memcpy(data.colSeparator,",",2); |
| 33287 | #ifdef SQLITE_HAVE_ZLIB |
| 33288 | }else if( cli_strcmp(z,"-zip")==0 ){ |
| 33289 | data.openMode = SHELL_OPEN_ZIPFILE; |
| 33290 | #endif |
| 33291 | }else if( cli_strcmp(z,"-append")==0 ){ |
| @@ -33443,19 +33674,19 @@ | |
| 33443 | /* Run all arguments that do not begin with '-' as if they were separate |
| 33444 | ** command-line inputs, except for the argToSkip argument which contains |
| 33445 | ** the database filename. |
| 33446 | */ |
| 33447 | for(i=0; i<nCmd; i++){ |
| 33448 | if( azCmd[i][0]=='.' ){ |
| 33449 | rc = do_meta_command(azCmd[i], &data); |
| 33450 | if( rc ){ |
| 33451 | if( rc==2 ) rc = 0; |
| 33452 | goto shell_main_exit; |
| 33453 | } |
| 33454 | }else{ |
| 33455 | open_db(&data, 0); |
| 33456 | echo_group_input(&data, azCmd[i]); |
| 33457 | rc = shell_exec(&data, azCmd[i], &zErrMsg); |
| 33458 | if( zErrMsg || rc ){ |
| 33459 | if( zErrMsg!=0 ){ |
| 33460 | shellEmitError(zErrMsg); |
| 33461 | }else{ |
| 33462 |
| --- extsrc/shell.c | |
| +++ extsrc/shell.c | |
| @@ -234,10 +234,12 @@ | |
| 234 | |
| 235 | /* ctype macros that work with signed characters */ |
| 236 | #define IsSpace(X) isspace((unsigned char)X) |
| 237 | #define IsDigit(X) isdigit((unsigned char)X) |
| 238 | #define ToLower(X) (char)tolower((unsigned char)X) |
| 239 | #define IsAlnum(X) isalnum((unsigned char)X) |
| 240 | #define IsAlpha(X) isalpha((unsigned char)X) |
| 241 | |
| 242 | #if defined(_WIN32) || defined(WIN32) |
| 243 | #if SQLITE_OS_WINRT |
| 244 | #include <intrin.h> |
| 245 | #endif |
| @@ -849,11 +851,11 @@ | |
| 851 | |
| 852 | /* |
| 853 | ** Prompt strings. Initialized in main. Settable with |
| 854 | ** .prompt main continue |
| 855 | */ |
| 856 | #define PROMPT_LEN_MAX 128 |
| 857 | /* First line prompt. default: "sqlite> " */ |
| 858 | static char mainPrompt[PROMPT_LEN_MAX]; |
| 859 | /* Continuation prompt. default: " ...> " */ |
| 860 | static char continuePrompt[PROMPT_LEN_MAX]; |
| 861 | |
| @@ -1506,13 +1508,13 @@ | |
| 1508 | ** Return '"' if quoting is required. Return 0 if no quoting is required. |
| 1509 | */ |
| 1510 | static char quoteChar(const char *zName){ |
| 1511 | int i; |
| 1512 | if( zName==0 ) return '"'; |
| 1513 | if( !IsAlpha(zName[0]) && zName[0]!='_' ) return '"'; |
| 1514 | for(i=0; zName[i]; i++){ |
| 1515 | if( !IsAlnum(zName[i]) && zName[i]!='_' ) return '"'; |
| 1516 | } |
| 1517 | return sqlite3_keyword_check(zName, i) ? '"' : 0; |
| 1518 | } |
| 1519 | |
| 1520 | /* |
| @@ -2425,11 +2427,11 @@ | |
| 2427 | ** content in cases where the content starts |
| 2428 | ** with a digit. |
| 2429 | ** |
| 2430 | ** typeof(Y)='blob' The hash is taken over prefix "Bnnn:" followed |
| 2431 | ** by the binary content of the blob. The "nnn" |
| 2432 | ** in the prefix is the minimum-length decimal |
| 2433 | ** representation of the byte-length of the blob. |
| 2434 | ** |
| 2435 | ** According to the rules above, all of the following SELECT statements |
| 2436 | ** should return TRUE: |
| 2437 | ** |
| @@ -3646,11 +3648,11 @@ | |
| 3648 | ** |
| 3649 | ** UINT works like BINARY for text, except that embedded strings |
| 3650 | ** of digits compare in numeric order. |
| 3651 | ** |
| 3652 | ** * Leading zeros are handled properly, in the sense that |
| 3653 | ** they do not mess of the magnitude comparison of embedded |
| 3654 | ** strings of digits. "x00123y" is equal to "x123y". |
| 3655 | ** |
| 3656 | ** * Only unsigned integers are recognized. Plus and minus |
| 3657 | ** signs are ignored. Decimal points and exponential notation |
| 3658 | ** are ignored. |
| @@ -3752,10 +3754,13 @@ | |
| 3754 | ** warnings. */ |
| 3755 | #ifndef UNUSED_PARAMETER |
| 3756 | # define UNUSED_PARAMETER(X) (void)(X) |
| 3757 | #endif |
| 3758 | |
| 3759 | #ifndef IsSpace |
| 3760 | #define IsSpace(X) isspace((unsigned char)X) |
| 3761 | #endif |
| 3762 | |
| 3763 | /* A decimal object */ |
| 3764 | typedef struct Decimal Decimal; |
| 3765 | struct Decimal { |
| 3766 | char sign; /* 0 for positive, 1 for negative */ |
| @@ -3801,11 +3806,11 @@ | |
| 3806 | p->isNull = 0; |
| 3807 | p->nDigit = 0; |
| 3808 | p->nFrac = 0; |
| 3809 | p->a = sqlite3_malloc64( n+1 ); |
| 3810 | if( p->a==0 ) goto new_from_text_failed; |
| 3811 | for(i=0; IsSpace(zIn[i]); i++){} |
| 3812 | if( zIn[i]=='-' ){ |
| 3813 | p->sign = 1; |
| 3814 | i++; |
| 3815 | }else if( zIn[i]=='+' ){ |
| 3816 | i++; |
| @@ -4456,11 +4461,11 @@ | |
| 4461 | } |
| 4462 | decimal_free(pA); |
| 4463 | decimal_free(pB); |
| 4464 | } |
| 4465 | |
| 4466 | /* Aggregate function: decimal_sum(X) |
| 4467 | ** |
| 4468 | ** Works like sum() except that it uses decimal arithmetic for unlimited |
| 4469 | ** precision. |
| 4470 | */ |
| 4471 | static void decimalSumStep( |
| @@ -4817,11 +4822,11 @@ | |
| 4822 | } |
| 4823 | |
| 4824 | /* |
| 4825 | ** Generate an error for a percentile function. |
| 4826 | ** |
| 4827 | ** The error format string must have exactly one occurrence of "%%s()" |
| 4828 | ** (with two '%' characters). That substring will be replaced by the name |
| 4829 | ** of the function. |
| 4830 | */ |
| 4831 | static void percentError(sqlite3_context *pCtx, const char *zFormat, ...){ |
| 4832 | PercentileFunc *pFunc = (PercentileFunc*)sqlite3_user_data(pCtx); |
| @@ -5957,11 +5962,11 @@ | |
| 5962 | ** number format: |
| 5963 | ** |
| 5964 | ** WITH c(name,bin) AS (VALUES |
| 5965 | ** ('minimum positive value', x'0000000000000001'), |
| 5966 | ** ('maximum subnormal value', x'000fffffffffffff'), |
| 5967 | ** ('minimum positive normal value', x'0010000000000000'), |
| 5968 | ** ('maximum value', x'7fefffffffffffff')) |
| 5969 | ** SELECT c.name, decimal_mul(ieee754_mantissa(c.bin),pow2.v) |
| 5970 | ** FROM pow2, c WHERE pow2.x=ieee754_exponent(c.bin); |
| 5971 | ** |
| 5972 | */ |
| @@ -6344,11 +6349,11 @@ | |
| 6349 | * 2 for UBSAN's satisfaction (and hypothetical 1's complement ALUs.) */ |
| 6350 | smBase += (mxI64/2) * smStep; |
| 6351 | smBase += (mxI64 - mxI64/2) * smStep; |
| 6352 | } |
| 6353 | /* Under UBSAN (or on 1's complement machines), must do this last term |
| 6354 | * in steps to avoid the dreaded (and harmless) signed multiply overflow. */ |
| 6355 | if( ix>=2 ){ |
| 6356 | sqlite3_int64 ix2 = (sqlite3_int64)ix/2; |
| 6357 | smBase += ix2*smStep; |
| 6358 | ix -= ix2; |
| 6359 | } |
| @@ -6724,23 +6729,21 @@ | |
| 6729 | if( pCur->ss.iBase<iMin ){ |
| 6730 | sqlite3_uint64 d = iMin - pCur->ss.iBase; |
| 6731 | pCur->ss.iBase += ((d+szStep-1)/szStep)*szStep; |
| 6732 | } |
| 6733 | if( pCur->ss.iTerm>iMax ){ |
| 6734 | pCur->ss.iTerm = iMax; |
| 6735 | } |
| 6736 | }else{ |
| 6737 | sqlite3_int64 szStep = -pCur->ss.iStep; |
| 6738 | assert( szStep>0 ); |
| 6739 | if( pCur->ss.iBase>iMax ){ |
| 6740 | sqlite3_uint64 d = pCur->ss.iBase - iMax; |
| 6741 | pCur->ss.iBase -= ((d+szStep-1)/szStep)*szStep; |
| 6742 | } |
| 6743 | if( pCur->ss.iTerm<iMin ){ |
| 6744 | pCur->ss.iTerm = iMin; |
| 6745 | } |
| 6746 | } |
| 6747 | } |
| 6748 | |
| 6749 | /* Apply LIMIT and OFFSET constraints, if any */ |
| @@ -9024,10 +9027,15 @@ | |
| 9027 | #include <assert.h> |
| 9028 | #include <string.h> |
| 9029 | #include <ctype.h> |
| 9030 | |
| 9031 | #ifndef SQLITE_OMIT_VIRTUALTABLE |
| 9032 | |
| 9033 | #ifndef IsAlnum |
| 9034 | #define IsAlnum(X) isalnum((unsigned char)X) |
| 9035 | #endif |
| 9036 | |
| 9037 | |
| 9038 | /* completion_vtab is a subclass of sqlite3_vtab which will |
| 9039 | ** serve as the underlying representation of a completion virtual table |
| 9040 | */ |
| 9041 | typedef struct completion_vtab completion_vtab; |
| @@ -9361,11 +9369,11 @@ | |
| 9369 | if( pCur->zLine==0 ) return SQLITE_NOMEM; |
| 9370 | } |
| 9371 | } |
| 9372 | if( pCur->zLine!=0 && pCur->zPrefix==0 ){ |
| 9373 | int i = pCur->nLine; |
| 9374 | while( i>0 && (IsAlnum(pCur->zLine[i-1]) || pCur->zLine[i-1]=='_') ){ |
| 9375 | i--; |
| 9376 | } |
| 9377 | pCur->nPrefix = pCur->nLine - i; |
| 9378 | if( pCur->nPrefix>0 ){ |
| 9379 | pCur->zPrefix = sqlite3_mprintf("%.*s", pCur->nPrefix, pCur->zLine + i); |
| @@ -14768,11 +14776,11 @@ | |
| 14776 | /* Register the auth callback with dbv */ |
| 14777 | if( rc==SQLITE_OK ){ |
| 14778 | sqlite3_set_authorizer(pNew->dbv, idxAuthCallback, (void*)pNew); |
| 14779 | } |
| 14780 | |
| 14781 | /* If an error has occurred, free the new object and return NULL. Otherwise, |
| 14782 | ** return the new sqlite3expert handle. */ |
| 14783 | if( rc!=SQLITE_OK ){ |
| 14784 | sqlite3_expert_destroy(pNew); |
| 14785 | pNew = 0; |
| 14786 | } |
| @@ -16290,11 +16298,11 @@ | |
| 16298 | ** |
| 16299 | ** PRAGMA vfstrace('+all'); |
| 16300 | ** |
| 16301 | ** Individual APIs can be enabled or disabled by name, with or without |
| 16302 | ** the initial "x" character. For example, to set up for tracing lock |
| 16303 | ** primitives only: |
| 16304 | ** |
| 16305 | ** PRAGMA vfstrace('-all, +Lock,Unlock,ShmLock'); |
| 16306 | ** |
| 16307 | ** The argument to the vfstrace pragma ignores capitalization and any |
| 16308 | ** characters other than alphabetics, '+', and '-'. |
| @@ -18700,10 +18708,20 @@ | |
| 18708 | |
| 18709 | /* typedef unsigned int u32; */ |
| 18710 | /* typedef unsigned char u8; */ |
| 18711 | /* typedef sqlite3_int64 i64; */ |
| 18712 | |
| 18713 | /* |
| 18714 | ** Work around C99 "flex-array" syntax for pre-C99 compilers, so as |
| 18715 | ** to avoid complaints from -fsanitize=strict-bounds. |
| 18716 | */ |
| 18717 | #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) |
| 18718 | # define FLEXARRAY |
| 18719 | #else |
| 18720 | # define FLEXARRAY 1 |
| 18721 | #endif |
| 18722 | |
| 18723 | typedef struct RecoverTable RecoverTable; |
| 18724 | typedef struct RecoverColumn RecoverColumn; |
| 18725 | |
| 18726 | /* |
| 18727 | ** When recovering rows of data that can be associated with table |
| @@ -18807,12 +18825,15 @@ | |
| 18825 | ** (aElem[iKey/32] & (1 << (iKey%32))) ? 1 : 0 |
| 18826 | */ |
| 18827 | typedef struct RecoverBitmap RecoverBitmap; |
| 18828 | struct RecoverBitmap { |
| 18829 | i64 nPg; /* Size of bitmap */ |
| 18830 | u32 aElem[FLEXARRAY]; /* Array of 32-bit bitmasks */ |
| 18831 | }; |
| 18832 | |
| 18833 | /* Size in bytes of a RecoverBitmap object sufficient to cover 32 pages */ |
| 18834 | #define SZ_RECOVERBITMAP_32 (16) |
| 18835 | |
| 18836 | /* |
| 18837 | ** State variables (part of the sqlite3_recover structure) used while |
| 18838 | ** recovering data for tables identified in the recovered schema (state |
| 18839 | ** RECOVER_STATE_WRITING). |
| @@ -19049,11 +19070,11 @@ | |
| 19070 | ** large enough to store a bit for all page numbers between 1 and nPg, |
| 19071 | ** inclusive. The bitmap is initially zeroed. |
| 19072 | */ |
| 19073 | static RecoverBitmap *recoverBitmapAlloc(sqlite3_recover *p, i64 nPg){ |
| 19074 | int nElem = (nPg+1+31) / 32; |
| 19075 | int nByte = SZ_RECOVERBITMAP_32 + nElem*sizeof(u32); |
| 19076 | RecoverBitmap *pRet = (RecoverBitmap*)recoverMalloc(p, nByte); |
| 19077 | |
| 19078 | if( pRet ){ |
| 19079 | pRet->nPg = nPg; |
| 19080 | } |
| @@ -21241,41 +21262,57 @@ | |
| 21262 | ** function is called. |
| 21263 | */ |
| 21264 | static void recoverStep(sqlite3_recover *p){ |
| 21265 | assert( p && p->errCode==SQLITE_OK ); |
| 21266 | switch( p->eState ){ |
| 21267 | case RECOVER_STATE_INIT: { |
| 21268 | int bUseWrapper = 1; |
| 21269 | /* This is the very first call to sqlite3_recover_step() on this object. |
| 21270 | */ |
| 21271 | recoverSqlCallback(p, "BEGIN"); |
| 21272 | recoverSqlCallback(p, "PRAGMA writable_schema = on"); |
| 21273 | recoverSqlCallback(p, "PRAGMA foreign_keys = off"); |
| 21274 | |
| 21275 | recoverEnterMutex(); |
| 21276 | |
| 21277 | /* Open the output database. And register required virtual tables and |
| 21278 | ** user functions with the new handle. */ |
| 21279 | recoverOpenOutput(p); |
| 21280 | |
| 21281 | /* Attempt to open a transaction and read page 1 of the input database. |
| 21282 | ** Two attempts may be made - one with a wrapper installed to ensure |
| 21283 | ** that the database header is sane, and then if that attempt returns |
| 21284 | ** SQLITE_NOTADB, then again with no wrapper. The second attempt is |
| 21285 | ** required for encrypted databases. */ |
| 21286 | if( p->errCode==SQLITE_OK ){ |
| 21287 | do{ |
| 21288 | p->errCode = SQLITE_OK; |
| 21289 | if( bUseWrapper ) recoverInstallWrapper(p); |
| 21290 | |
| 21291 | /* Open a transaction on the input database. */ |
| 21292 | sqlite3_file_control(p->dbIn, p->zDb, SQLITE_FCNTL_RESET_CACHE, 0); |
| 21293 | recoverExec(p, p->dbIn, "PRAGMA writable_schema = on"); |
| 21294 | recoverExec(p, p->dbIn, "BEGIN"); |
| 21295 | if( p->errCode==SQLITE_OK ) p->bCloseTransaction = 1; |
| 21296 | recoverExec(p, p->dbIn, "SELECT 1 FROM sqlite_schema"); |
| 21297 | recoverTransferSettings(p); |
| 21298 | recoverOpenRecovery(p); |
| 21299 | recoverCacheSchema(p); |
| 21300 | |
| 21301 | if( bUseWrapper ) recoverUninstallWrapper(p); |
| 21302 | }while( p->errCode==SQLITE_NOTADB |
| 21303 | && (bUseWrapper--) |
| 21304 | && SQLITE_OK==sqlite3_exec(p->dbIn, "ROLLBACK", 0, 0, 0) |
| 21305 | ); |
| 21306 | } |
| 21307 | |
| 21308 | recoverLeaveMutex(); |
| 21309 | recoverExec(p, p->dbOut, "BEGIN"); |
| 21310 | recoverWriteSchema1(p); |
| 21311 | p->eState = RECOVER_STATE_WRITING; |
| 21312 | break; |
| 21313 | } |
| 21314 | |
| 21315 | case RECOVER_STATE_WRITING: { |
| 21316 | if( p->w1.pTbls==0 ){ |
| 21317 | recoverWriteDataInit(p); |
| 21318 | } |
| @@ -21610,10 +21647,11 @@ | |
| 21647 | u8 eTraceType; /* SHELL_TRACE_* value for type of trace */ |
| 21648 | u8 bSafeMode; /* True to prohibit unsafe operations */ |
| 21649 | u8 bSafeModePersist; /* The long-term value of bSafeMode */ |
| 21650 | u8 eRestoreState; /* See comments above doAutoDetectRestore() */ |
| 21651 | u8 crlfMode; /* Do NL-to-CRLF translations when enabled (maybe) */ |
| 21652 | u8 eEscMode; /* Escape mode for text output */ |
| 21653 | ColModeOpts cmOpts; /* Option values affecting columnar mode output */ |
| 21654 | unsigned statsOn; /* True to display memory stats before each finalize */ |
| 21655 | unsigned mEqpLines; /* Mask of vertical lines in the EQP output graph */ |
| 21656 | int inputNesting; /* Track nesting level of .read and other redirects */ |
| 21657 | int outCount; /* Revert to stdout when reaching zero */ |
| @@ -21710,10 +21748,19 @@ | |
| 21748 | #define SHELL_PROGRESS_RESET 0x02 /* Reset the count when the progress |
| 21749 | ** callback limit is reached, and for each |
| 21750 | ** top-level SQL statement */ |
| 21751 | #define SHELL_PROGRESS_ONCE 0x04 /* Cancel the --limit after firing once */ |
| 21752 | |
| 21753 | /* Allowed values for ShellState.eEscMode. The default value should |
| 21754 | ** be 0, so to change the default, reorder the names. |
| 21755 | */ |
| 21756 | #define SHELL_ESC_ASCII 0 /* Substitute ^Y for X where Y=X+0x40 */ |
| 21757 | #define SHELL_ESC_SYMBOL 1 /* Substitute U+2400 graphics */ |
| 21758 | #define SHELL_ESC_OFF 2 /* Send characters verbatim */ |
| 21759 | |
| 21760 | static const char *shell_EscModeNames[] = { "ascii", "symbol", "off" }; |
| 21761 | |
| 21762 | /* |
| 21763 | ** These are the allowed shellFlgs values |
| 21764 | */ |
| 21765 | #define SHFLG_Pagecache 0x00000001 /* The --pagecache option is used */ |
| 21766 | #define SHFLG_Lookaside 0x00000002 /* Lookaside memory is used */ |
| @@ -22047,63 +22094,79 @@ | |
| 22094 | sqlite3_fprintf(out, "X'%s'", zStr); |
| 22095 | sqlite3_free(zStr); |
| 22096 | } |
| 22097 | |
| 22098 | /* |
| 22099 | ** Output the given string as a quoted string using SQL quoting conventions: |
| 22100 | ** |
| 22101 | ** (1) Single quotes (') within the string are doubled |
| 22102 | ** (2) The whle string is enclosed in '...' |
| 22103 | ** (3) Control characters other than \n, \t, and \r\n are escaped |
| 22104 | ** using \u00XX notation and if such substitutions occur, |
| 22105 | ** the whole string is enclosed in unistr('...') instead of '...'. |
| 22106 | ** |
| 22107 | ** Step (3) is omitted if the control-character escape mode is OFF. |
| 22108 | ** |
| 22109 | ** See also: output_quoted_escaped_string() which does the same except |
| 22110 | ** that it does not make exceptions for \n, \t, and \r\n in step (3). |
| 22111 | */ |
| 22112 | static void output_quoted_string(ShellState *p, const char *zInX){ |
| 22113 | int i; |
| 22114 | int needUnistr = 0; |
| 22115 | int needDblQuote = 0; |
| 22116 | const unsigned char *z = (const unsigned char*)zInX; |
| 22117 | unsigned char c; |
| 22118 | FILE *out = p->out; |
| 22119 | sqlite3_fsetmode(out, _O_BINARY); |
| 22120 | if( z==0 ) return; |
| 22121 | for(i=0; (c = z[i])!=0; i++){ |
| 22122 | if( c=='\'' ){ needDblQuote = 1; } |
| 22123 | if( c>0x1f ) continue; |
| 22124 | if( c=='\t' || c=='\n' ) continue; |
| 22125 | if( c=='\r' && z[i+1]=='\n' ) continue; |
| 22126 | needUnistr = 1; |
| 22127 | break; |
| 22128 | } |
| 22129 | if( (needDblQuote==0 && needUnistr==0) |
| 22130 | || (needDblQuote==0 && p->eEscMode==SHELL_ESC_OFF) |
| 22131 | ){ |
| 22132 | sqlite3_fprintf(out, "'%s'",z); |
| 22133 | }else if( p->eEscMode==SHELL_ESC_OFF ){ |
| 22134 | char *zEncoded = sqlite3_mprintf("%Q", z); |
| 22135 | sqlite3_fputs(zEncoded, out); |
| 22136 | sqlite3_free(zEncoded); |
| 22137 | }else{ |
| 22138 | if( needUnistr ){ |
| 22139 | sqlite3_fputs("unistr('", out); |
| 22140 | }else{ |
| 22141 | sqlite3_fputs("'", out); |
| 22142 | } |
| 22143 | while( *z ){ |
| 22144 | for(i=0; (c = z[i])!=0; i++){ |
| 22145 | if( c=='\'' ) break; |
| 22146 | if( c>0x1f ) continue; |
| 22147 | if( c=='\t' || c=='\n' ) continue; |
| 22148 | if( c=='\r' && z[i+1]=='\n' ) continue; |
| 22149 | break; |
| 22150 | } |
| 22151 | if( i ){ |
| 22152 | sqlite3_fprintf(out, "%.*s", i, z); |
| 22153 | z += i; |
| 22154 | } |
| 22155 | if( c==0 ) break; |
| 22156 | if( c=='\'' ){ |
| 22157 | sqlite3_fputs("''", out); |
| 22158 | }else{ |
| 22159 | sqlite3_fprintf(out, "\\u%04x", c); |
| 22160 | } |
| 22161 | z++; |
| 22162 | } |
| 22163 | if( needUnistr ){ |
| 22164 | sqlite3_fputs("')", out); |
| 22165 | }else{ |
| 22166 | sqlite3_fputs("'", out); |
| 22167 | } |
| 22168 | } |
| 22169 | setCrlfMode(p); |
| 22170 | } |
| 22171 | |
| 22172 | /* |
| @@ -22114,65 +22177,19 @@ | |
| 22177 | ** |
| 22178 | ** This is like output_quoted_string() but with the addition of the \r\n |
| 22179 | ** escape mechanism. |
| 22180 | */ |
| 22181 | static void output_quoted_escaped_string(ShellState *p, const char *z){ |
| 22182 | char *zEscaped; |
| 22183 | sqlite3_fsetmode(p->out, _O_BINARY); |
| 22184 | if( p->eEscMode==SHELL_ESC_OFF ){ |
| 22185 | zEscaped = sqlite3_mprintf("%Q", z); |
| 22186 | }else{ |
| 22187 | zEscaped = sqlite3_mprintf("%#Q", z); |
| 22188 | } |
| 22189 | sqlite3_fputs(zEscaped, p->out); |
| 22190 | sqlite3_free(zEscaped); |
| 22191 | setCrlfMode(p); |
| 22192 | } |
| 22193 | |
| 22194 | /* |
| 22195 | ** Find earliest of chars within s specified in zAny. |
| @@ -22317,10 +22334,97 @@ | |
| 22334 | sqlite3_fputs(ace+1, out); |
| 22335 | } |
| 22336 | } |
| 22337 | sqlite3_fputs(zq, out); |
| 22338 | } |
| 22339 | |
| 22340 | /* |
| 22341 | ** Escape the input string if it is needed and in accordance with |
| 22342 | ** eEscMode. |
| 22343 | ** |
| 22344 | ** Escaping is needed if the string contains any control characters |
| 22345 | ** other than \t, \n, and \r\n |
| 22346 | ** |
| 22347 | ** If no escaping is needed (the common case) then set *ppFree to NULL |
| 22348 | ** and return the original string. If escapingn is needed, write the |
| 22349 | ** escaped string into memory obtained from sqlite3_malloc64() or the |
| 22350 | ** equivalent, and return the new string and set *ppFree to the new string |
| 22351 | ** as well. |
| 22352 | ** |
| 22353 | ** The caller is responsible for freeing *ppFree if it is non-NULL in order |
| 22354 | ** to reclaim memory. |
| 22355 | */ |
| 22356 | static const char *escapeOutput( |
| 22357 | ShellState *p, |
| 22358 | const char *zInX, |
| 22359 | char **ppFree |
| 22360 | ){ |
| 22361 | i64 i, j; |
| 22362 | i64 nCtrl = 0; |
| 22363 | unsigned char *zIn; |
| 22364 | unsigned char c; |
| 22365 | unsigned char *zOut; |
| 22366 | |
| 22367 | |
| 22368 | /* No escaping if disabled */ |
| 22369 | if( p->eEscMode==SHELL_ESC_OFF ){ |
| 22370 | *ppFree = 0; |
| 22371 | return zInX; |
| 22372 | } |
| 22373 | |
| 22374 | /* Count the number of control characters in the string. */ |
| 22375 | zIn = (unsigned char*)zInX; |
| 22376 | for(i=0; (c = zIn[i])!=0; i++){ |
| 22377 | if( c<=0x1f |
| 22378 | && c!='\t' |
| 22379 | && c!='\n' |
| 22380 | && (c!='\r' || zIn[i+1]!='\n') |
| 22381 | ){ |
| 22382 | nCtrl++; |
| 22383 | } |
| 22384 | } |
| 22385 | if( nCtrl==0 ){ |
| 22386 | *ppFree = 0; |
| 22387 | return zInX; |
| 22388 | } |
| 22389 | if( p->eEscMode==SHELL_ESC_SYMBOL ) nCtrl *= 2; |
| 22390 | zOut = sqlite3_malloc64( i + nCtrl + 1 ); |
| 22391 | shell_check_oom(zOut); |
| 22392 | for(i=j=0; (c = zIn[i])!=0; i++){ |
| 22393 | if( c>0x1f |
| 22394 | || c=='\t' |
| 22395 | || c=='\n' |
| 22396 | || (c=='\r' && zIn[i+1]=='\n') |
| 22397 | ){ |
| 22398 | continue; |
| 22399 | } |
| 22400 | if( i>0 ){ |
| 22401 | memcpy(&zOut[j], zIn, i); |
| 22402 | j += i; |
| 22403 | } |
| 22404 | zIn += i+1; |
| 22405 | i = -1; |
| 22406 | switch( p->eEscMode ){ |
| 22407 | case SHELL_ESC_SYMBOL: |
| 22408 | zOut[j++] = 0xe2; |
| 22409 | zOut[j++] = 0x90; |
| 22410 | zOut[j++] = 0x80+c; |
| 22411 | break; |
| 22412 | case SHELL_ESC_ASCII: |
| 22413 | zOut[j++] = '^'; |
| 22414 | zOut[j++] = 0x40+c; |
| 22415 | break; |
| 22416 | } |
| 22417 | } |
| 22418 | if( i>0 ){ |
| 22419 | memcpy(&zOut[j], zIn, i); |
| 22420 | j += i; |
| 22421 | } |
| 22422 | zOut[j] = 0; |
| 22423 | *ppFree = (char*)zOut; |
| 22424 | return (char*)zOut; |
| 22425 | } |
| 22426 | |
| 22427 | /* |
| 22428 | ** Output the given string with characters that are special to |
| 22429 | ** HTML escaped. |
| 22430 | */ |
| @@ -22761,12 +22865,16 @@ | |
| 22865 | int len = strlen30(azCol[i] ? azCol[i] : ""); |
| 22866 | if( len>w ) w = len; |
| 22867 | } |
| 22868 | if( p->cnt++>0 ) sqlite3_fputs(p->rowSeparator, p->out); |
| 22869 | for(i=0; i<nArg; i++){ |
| 22870 | char *pFree = 0; |
| 22871 | const char *pDisplay; |
| 22872 | pDisplay = escapeOutput(p, azArg[i] ? azArg[i] : p->nullValue, &pFree); |
| 22873 | sqlite3_fprintf(p->out, "%*s = %s%s", w, azCol[i], |
| 22874 | pDisplay, p->rowSeparator); |
| 22875 | if( pFree ) sqlite3_free(pFree); |
| 22876 | } |
| 22877 | break; |
| 22878 | } |
| 22879 | case MODE_ScanExp: |
| 22880 | case MODE_Explain: { |
| @@ -22832,18 +22940,22 @@ | |
| 22940 | int j; |
| 22941 | int nParen = 0; |
| 22942 | char cEnd = 0; |
| 22943 | char c; |
| 22944 | int nLine = 0; |
| 22945 | int isIndex; |
| 22946 | int isWhere = 0; |
| 22947 | assert( nArg==1 ); |
| 22948 | if( azArg[0]==0 ) break; |
| 22949 | if( sqlite3_strlike("CREATE VIEW%", azArg[0], 0)==0 |
| 22950 | || sqlite3_strlike("CREATE TRIG%", azArg[0], 0)==0 |
| 22951 | ){ |
| 22952 | sqlite3_fprintf(p->out, "%s;\n", azArg[0]); |
| 22953 | break; |
| 22954 | } |
| 22955 | isIndex = sqlite3_strlike("CREATE INDEX%", azArg[0], 0)==0 |
| 22956 | || sqlite3_strlike("CREATE UNIQUE INDEX%", azArg[0], 0)==0; |
| 22957 | z = sqlite3_mprintf("%s", azArg[0]); |
| 22958 | shell_check_oom(z); |
| 22959 | j = 0; |
| 22960 | for(i=0; IsSpace(z[i]); i++){} |
| 22961 | for(; (c = z[i])!=0; i++){ |
| @@ -22869,18 +22981,30 @@ | |
| 22981 | cEnd = '\n'; |
| 22982 | }else if( c=='(' ){ |
| 22983 | nParen++; |
| 22984 | }else if( c==')' ){ |
| 22985 | nParen--; |
| 22986 | if( nLine>0 && nParen==0 && j>0 && !isWhere ){ |
| 22987 | printSchemaLineN(p->out, z, j, "\n"); |
| 22988 | j = 0; |
| 22989 | } |
| 22990 | }else if( (c=='w' || c=='W') |
| 22991 | && nParen==0 && isIndex |
| 22992 | && sqlite3_strnicmp("WHERE",&z[i],5)==0 |
| 22993 | && !IsAlnum(z[i+5]) && z[i+5]!='_' ){ |
| 22994 | isWhere = 1; |
| 22995 | }else if( isWhere && (c=='A' || c=='a') |
| 22996 | && nParen==0 |
| 22997 | && sqlite3_strnicmp("AND",&z[i],3)==0 |
| 22998 | && !IsAlnum(z[i+3]) && z[i+3]!='_' ){ |
| 22999 | printSchemaLineN(p->out, z, j, "\n "); |
| 23000 | j = 0; |
| 23001 | } |
| 23002 | z[j++] = c; |
| 23003 | if( nParen==1 && cEnd==0 |
| 23004 | && (c=='(' || c=='\n' || (c==',' && !wsToEol(z+i+1))) |
| 23005 | && !isWhere |
| 23006 | ){ |
| 23007 | if( c=='\n' ) j--; |
| 23008 | printSchemaLineN(p->out, z, j, "\n "); |
| 23009 | j = 0; |
| 23010 | nLine++; |
| @@ -22894,19 +23018,27 @@ | |
| 23018 | break; |
| 23019 | } |
| 23020 | case MODE_List: { |
| 23021 | if( p->cnt++==0 && p->showHeader ){ |
| 23022 | for(i=0; i<nArg; i++){ |
| 23023 | char *z = azCol[i]; |
| 23024 | char *pFree; |
| 23025 | const char *zOut = escapeOutput(p, z, &pFree); |
| 23026 | sqlite3_fprintf(p->out, "%s%s", zOut, |
| 23027 | i==nArg-1 ? p->rowSeparator : p->colSeparator); |
| 23028 | if( pFree ) sqlite3_free(pFree); |
| 23029 | } |
| 23030 | } |
| 23031 | if( azArg==0 ) break; |
| 23032 | for(i=0; i<nArg; i++){ |
| 23033 | char *z = azArg[i]; |
| 23034 | char *pFree; |
| 23035 | const char *zOut; |
| 23036 | if( z==0 ) z = p->nullValue; |
| 23037 | zOut = escapeOutput(p, z, &pFree); |
| 23038 | sqlite3_fputs(zOut, p->out); |
| 23039 | if( pFree ) sqlite3_free(pFree); |
| 23040 | sqlite3_fputs((i<nArg-1)? p->colSeparator : p->rowSeparator, p->out); |
| 23041 | } |
| 23042 | break; |
| 23043 | } |
| 23044 | case MODE_Www: |
| @@ -24021,10 +24153,11 @@ | |
| 24153 | ** from malloc()) of that first line, which caller should free sometime. |
| 24154 | ** Write anything to display on the next line into *pzTail. If this is |
| 24155 | ** the last line, write a NULL into *pzTail. (*pzTail is not allocated.) |
| 24156 | */ |
| 24157 | static char *translateForDisplayAndDup( |
| 24158 | ShellState *p, /* To access current settings */ |
| 24159 | const unsigned char *z, /* Input text to be transformed */ |
| 24160 | const unsigned char **pzTail, /* OUT: Tail of the input for next line */ |
| 24161 | int mxWidth, /* Max width. 0 means no limit */ |
| 24162 | u8 bWordWrap /* If true, avoid breaking mid-word */ |
| 24163 | ){ |
| @@ -24055,28 +24188,31 @@ | |
| 24188 | n++; |
| 24189 | i++; |
| 24190 | j++; |
| 24191 | continue; |
| 24192 | } |
| 24193 | if( c==0 || c=='\n' || (c=='\r' && z[i+1]=='\n') ) break; |
| 24194 | if( c=='\t' ){ |
| 24195 | do{ |
| 24196 | n++; |
| 24197 | j++; |
| 24198 | }while( (n&7)!=0 && n<mxWidth ); |
| 24199 | i++; |
| 24200 | continue; |
| 24201 | } |
| 24202 | n++; |
| 24203 | j += 3; |
| 24204 | i++; |
| 24205 | } |
| 24206 | if( n>=mxWidth && bWordWrap ){ |
| 24207 | /* Perhaps try to back up to a better place to break the line */ |
| 24208 | for(k=i; k>i/2; k--){ |
| 24209 | if( IsSpace(z[k-1]) ) break; |
| 24210 | } |
| 24211 | if( k<=i/2 ){ |
| 24212 | for(k=i; k>i/2; k--){ |
| 24213 | if( IsAlnum(z[k-1])!=IsAlnum(z[k]) && (z[k]&0xc0)!=0x80 ) break; |
| 24214 | } |
| 24215 | } |
| 24216 | if( k<=i/2 ){ |
| 24217 | k = i; |
| 24218 | }else{ |
| @@ -24110,23 +24246,48 @@ | |
| 24246 | if( c>=' ' ){ |
| 24247 | n++; |
| 24248 | zOut[j++] = z[i++]; |
| 24249 | continue; |
| 24250 | } |
| 24251 | if( c==0 ) break; |
| 24252 | if( z[i]=='\t' ){ |
| 24253 | do{ |
| 24254 | n++; |
| 24255 | zOut[j++] = ' '; |
| 24256 | }while( (n&7)!=0 && n<mxWidth ); |
| 24257 | i++; |
| 24258 | continue; |
| 24259 | } |
| 24260 | switch( p->eEscMode ){ |
| 24261 | case SHELL_ESC_SYMBOL: |
| 24262 | zOut[j++] = 0xe2; |
| 24263 | zOut[j++] = 0x90; |
| 24264 | zOut[j++] = 0x80 + c; |
| 24265 | break; |
| 24266 | case SHELL_ESC_ASCII: |
| 24267 | zOut[j++] = '^'; |
| 24268 | zOut[j++] = 0x40 + c; |
| 24269 | break; |
| 24270 | case SHELL_ESC_OFF: |
| 24271 | zOut[j++] = c; |
| 24272 | break; |
| 24273 | } |
| 24274 | i++; |
| 24275 | } |
| 24276 | zOut[j] = 0; |
| 24277 | return (char*)zOut; |
| 24278 | } |
| 24279 | |
| 24280 | /* Return true if the text string z[] contains characters that need |
| 24281 | ** unistr() escaping. |
| 24282 | */ |
| 24283 | static int needUnistr(const unsigned char *z){ |
| 24284 | unsigned char c; |
| 24285 | if( z==0 ) return 0; |
| 24286 | while( (c = *z)>0x1f || c=='\t' || c=='\n' || (c=='\r' && z[1]=='\n') ){ z++; } |
| 24287 | return c!=0; |
| 24288 | } |
| 24289 | |
| 24290 | /* Extract the value of the i-th current column for pStmt as an SQL literal |
| 24291 | ** value. Memory is obtained from sqlite3_malloc64() and must be freed by |
| 24292 | ** the caller. |
| 24293 | */ |
| @@ -24138,11 +24299,12 @@ | |
| 24299 | case SQLITE_INTEGER: |
| 24300 | case SQLITE_FLOAT: { |
| 24301 | return sqlite3_mprintf("%s",sqlite3_column_text(pStmt,i)); |
| 24302 | } |
| 24303 | case SQLITE_TEXT: { |
| 24304 | const unsigned char *zText = sqlite3_column_text(pStmt,i); |
| 24305 | return sqlite3_mprintf(needUnistr(zText)?"%#Q":"%Q",zText); |
| 24306 | } |
| 24307 | case SQLITE_BLOB: { |
| 24308 | int j; |
| 24309 | sqlite3_str *pStr = sqlite3_str_new(0); |
| 24310 | const unsigned char *a = sqlite3_column_blob(pStmt,i); |
| @@ -24230,11 +24392,11 @@ | |
| 24392 | wx = p->cmOpts.iWrap; |
| 24393 | } |
| 24394 | if( wx<0 ) wx = -wx; |
| 24395 | uz = (const unsigned char*)sqlite3_column_name(pStmt,i); |
| 24396 | if( uz==0 ) uz = (u8*)""; |
| 24397 | azData[i] = translateForDisplayAndDup(p, uz, &zNotUsed, wx, bw); |
| 24398 | } |
| 24399 | do{ |
| 24400 | int useNextLine = bNextLine; |
| 24401 | bNextLine = 0; |
| 24402 | if( (nRow+2)*nColumn >= nAlloc ){ |
| @@ -24254,19 +24416,20 @@ | |
| 24416 | if( wx<0 ) wx = -wx; |
| 24417 | if( useNextLine ){ |
| 24418 | uz = azNextLine[i]; |
| 24419 | if( uz==0 ) uz = (u8*)zEmpty; |
| 24420 | }else if( p->cmOpts.bQuote ){ |
| 24421 | assert( azQuoted!=0 ); |
| 24422 | sqlite3_free(azQuoted[i]); |
| 24423 | azQuoted[i] = quoted_column(pStmt,i); |
| 24424 | uz = (const unsigned char*)azQuoted[i]; |
| 24425 | }else{ |
| 24426 | uz = (const unsigned char*)sqlite3_column_text(pStmt,i); |
| 24427 | if( uz==0 ) uz = (u8*)zShowNull; |
| 24428 | } |
| 24429 | azData[nRow*nColumn + i] |
| 24430 | = translateForDisplayAndDup(p, uz, &azNextLine[i], wx, bw); |
| 24431 | if( azNextLine[i] ){ |
| 24432 | bNextLine = 1; |
| 24433 | abRowDiv[nRow-1] = 0; |
| 24434 | bMultiLineRowExists = 1; |
| 24435 | } |
| @@ -25185,11 +25348,11 @@ | |
| 25348 | #if !defined(SQLITE_SHELL_FIDDLE) |
| 25349 | ".log FILE|on|off Turn logging on or off. FILE can be stderr/stdout", |
| 25350 | #else |
| 25351 | ".log on|off Turn logging on or off.", |
| 25352 | #endif |
| 25353 | ".mode ?MODE? ?OPTIONS? Set output mode", |
| 25354 | " MODE is one of:", |
| 25355 | " ascii Columns/rows delimited by 0x1F and 0x1E", |
| 25356 | " box Tables using unicode box-drawing characters", |
| 25357 | " csv Comma-separated values", |
| 25358 | " column Output in columns. (See .width)", |
| @@ -25203,10 +25366,11 @@ | |
| 25366 | " quote Escape answers as for SQL", |
| 25367 | " table ASCII-art table", |
| 25368 | " tabs Tab-separated values", |
| 25369 | " tcl TCL list elements", |
| 25370 | " OPTIONS: (for columnar modes or insert mode):", |
| 25371 | " --escape T ctrl-char escape; T is one of: symbol, ascii, off", |
| 25372 | " --wrap N Wrap output lines to no longer than N characters", |
| 25373 | " --wordwrap B Wrap or not at word boundaries per B (on/off)", |
| 25374 | " --ww Shorthand for \"--wordwrap 1\"", |
| 25375 | " --quote Quote output text as SQL literals", |
| 25376 | " --noquote Do not quote output text", |
| @@ -25978,11 +26142,11 @@ | |
| 26142 | #if HAVE_LINENOISE==2 |
| 26143 | UNUSED_PARAMETER(pUserData); |
| 26144 | #endif |
| 26145 | if( nLine>(i64)sizeof(zBuf)-30 ) return; |
| 26146 | if( zLine[0]=='.' || zLine[0]=='#') return; |
| 26147 | for(i=nLine-1; i>=0 && (IsAlnum(zLine[i]) || zLine[i]=='_'); i--){} |
| 26148 | if( i==nLine-1 ) return; |
| 26149 | iStart = i+1; |
| 26150 | memcpy(zBuf, zLine, iStart); |
| 26151 | zSql = sqlite3_mprintf("SELECT DISTINCT candidate COLLATE nocase" |
| 26152 | " FROM completion(%Q,%Q) ORDER BY 1", |
| @@ -28218,10 +28382,11 @@ | |
| 28382 | sqlite3_recover_config(p, 789, (void*)zRecoveryDb); /* Debug use only */ |
| 28383 | sqlite3_recover_config(p, SQLITE_RECOVER_LOST_AND_FOUND, (void*)zLAF); |
| 28384 | sqlite3_recover_config(p, SQLITE_RECOVER_ROWIDS, (void*)&bRowids); |
| 28385 | sqlite3_recover_config(p, SQLITE_RECOVER_FREELIST_CORRUPT,(void*)&bFreelist); |
| 28386 | |
| 28387 | sqlite3_fprintf(pState->out, ".dbconfig defensive off\n"); |
| 28388 | sqlite3_recover_run(p); |
| 28389 | if( sqlite3_recover_errcode(p)!=SQLITE_OK ){ |
| 28390 | const char *zErr = sqlite3_recover_errmsg(p); |
| 28391 | int errCode = sqlite3_recover_errcode(p); |
| 28392 | sqlite3_fprintf(stderr,"sql error: %s (%d)\n", zErr, errCode); |
| @@ -29943,28 +30108,56 @@ | |
| 30108 | |
| 30109 | if( c=='m' && cli_strncmp(azArg[0], "mode", n)==0 ){ |
| 30110 | const char *zMode = 0; |
| 30111 | const char *zTabname = 0; |
| 30112 | int i, n2; |
| 30113 | int chng = 0; /* 0x01: change to cmopts. 0x02: Any other change */ |
| 30114 | ColModeOpts cmOpts = ColModeOpts_default; |
| 30115 | for(i=1; i<nArg; i++){ |
| 30116 | const char *z = azArg[i]; |
| 30117 | if( optionMatch(z,"wrap") && i+1<nArg ){ |
| 30118 | cmOpts.iWrap = integerValue(azArg[++i]); |
| 30119 | chng |= 1; |
| 30120 | }else if( optionMatch(z,"ww") ){ |
| 30121 | cmOpts.bWordWrap = 1; |
| 30122 | chng |= 1; |
| 30123 | }else if( optionMatch(z,"wordwrap") && i+1<nArg ){ |
| 30124 | cmOpts.bWordWrap = (u8)booleanValue(azArg[++i]); |
| 30125 | chng |= 1; |
| 30126 | }else if( optionMatch(z,"quote") ){ |
| 30127 | cmOpts.bQuote = 1; |
| 30128 | chng |= 1; |
| 30129 | }else if( optionMatch(z,"noquote") ){ |
| 30130 | cmOpts.bQuote = 0; |
| 30131 | chng |= 1; |
| 30132 | }else if( optionMatch(z,"escape") && i+1<nArg ){ |
| 30133 | /* See similar code at tag-20250224-1 */ |
| 30134 | const char *zEsc = azArg[++i]; |
| 30135 | int k; |
| 30136 | for(k=0; k<ArraySize(shell_EscModeNames); k++){ |
| 30137 | if( sqlite3_stricmp(zEsc,shell_EscModeNames[k])==0 ){ |
| 30138 | p->eEscMode = k; |
| 30139 | chng |= 2; |
| 30140 | break; |
| 30141 | } |
| 30142 | } |
| 30143 | if( k>=ArraySize(shell_EscModeNames) ){ |
| 30144 | sqlite3_fprintf(stderr, "unknown control character escape mode \"%s\"" |
| 30145 | " - choices:", zEsc); |
| 30146 | for(k=0; k<ArraySize(shell_EscModeNames); k++){ |
| 30147 | sqlite3_fprintf(stderr, " %s", shell_EscModeNames[k]); |
| 30148 | } |
| 30149 | sqlite3_fprintf(stderr, "\n"); |
| 30150 | rc = 1; |
| 30151 | goto meta_command_exit; |
| 30152 | } |
| 30153 | }else if( zMode==0 ){ |
| 30154 | zMode = z; |
| 30155 | /* Apply defaults for qbox pseudo-mode. If that |
| 30156 | * overwrites already-set values, user was informed of this. |
| 30157 | */ |
| 30158 | chng |= 1; |
| 30159 | if( cli_strcmp(z, "qbox")==0 ){ |
| 30160 | ColModeOpts cmo = ColModeOpts_default_qbox; |
| 30161 | zMode = "box"; |
| 30162 | cmOpts = cmo; |
| 30163 | } |
| @@ -29971,10 +30164,11 @@ | |
| 30164 | }else if( zTabname==0 ){ |
| 30165 | zTabname = z; |
| 30166 | }else if( z[0]=='-' ){ |
| 30167 | sqlite3_fprintf(stderr,"unknown option: %s\n", z); |
| 30168 | eputz("options:\n" |
| 30169 | " --escape MODE\n" |
| 30170 | " --noquote\n" |
| 30171 | " --quote\n" |
| 30172 | " --wordwrap on/off\n" |
| 30173 | " --wrap N\n" |
| 30174 | " --ww\n"); |
| @@ -29984,24 +30178,33 @@ | |
| 30178 | sqlite3_fprintf(stderr,"extra argument: \"%s\"\n", z); |
| 30179 | rc = 1; |
| 30180 | goto meta_command_exit; |
| 30181 | } |
| 30182 | } |
| 30183 | if( !chng ){ |
| 30184 | if( p->mode==MODE_Column |
| 30185 | || (p->mode>=MODE_Markdown && p->mode<=MODE_Box) |
| 30186 | ){ |
| 30187 | sqlite3_fprintf(p->out, |
| 30188 | "current output mode: %s --wrap %d --wordwrap %s " |
| 30189 | "--%squote --escape %s\n", |
| 30190 | modeDescr[p->mode], p->cmOpts.iWrap, |
| 30191 | p->cmOpts.bWordWrap ? "on" : "off", |
| 30192 | p->cmOpts.bQuote ? "" : "no", |
| 30193 | shell_EscModeNames[p->eEscMode] |
| 30194 | ); |
| 30195 | }else{ |
| 30196 | sqlite3_fprintf(p->out, |
| 30197 | "current output mode: %s --escape %s\n", |
| 30198 | modeDescr[p->mode], |
| 30199 | shell_EscModeNames[p->eEscMode] |
| 30200 | ); |
| 30201 | } |
| 30202 | } |
| 30203 | if( zMode==0 ){ |
| 30204 | zMode = modeDescr[p->mode]; |
| 30205 | if( (chng&1)==0 ) cmOpts = p->cmOpts; |
| 30206 | } |
| 30207 | n2 = strlen30(zMode); |
| 30208 | if( cli_strncmp(zMode,"lines",n2)==0 ){ |
| 30209 | p->mode = MODE_Line; |
| 30210 | sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); |
| @@ -30030,10 +30233,15 @@ | |
| 30233 | p->mode = MODE_List; |
| 30234 | sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Tab); |
| 30235 | }else if( cli_strncmp(zMode,"insert",n2)==0 ){ |
| 30236 | p->mode = MODE_Insert; |
| 30237 | set_table_name(p, zTabname ? zTabname : "table"); |
| 30238 | if( p->eEscMode==SHELL_ESC_OFF ){ |
| 30239 | ShellSetFlag(p, SHFLG_Newlines); |
| 30240 | }else{ |
| 30241 | ShellClearFlag(p, SHFLG_Newlines); |
| 30242 | } |
| 30243 | }else if( cli_strncmp(zMode,"quote",n2)==0 ){ |
| 30244 | p->mode = MODE_Quote; |
| 30245 | sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma); |
| 30246 | sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row); |
| 30247 | }else if( cli_strncmp(zMode,"ascii",n2)==0 ){ |
| @@ -32266,11 +32474,11 @@ | |
| 32474 | /* |
| 32475 | ** The CLI needs a working sqlite3_complete() to work properly. So error |
| 32476 | ** out of the build if compiling with SQLITE_OMIT_COMPLETE. |
| 32477 | */ |
| 32478 | #ifdef SQLITE_OMIT_COMPLETE |
| 32479 | # error the CLI application is incompatible with SQLITE_OMIT_COMPLETE. |
| 32480 | #endif |
| 32481 | |
| 32482 | /* |
| 32483 | ** Return true if zSql is a complete SQL statement. Return false if it |
| 32484 | ** ends in the middle of a string literal or C-style comment. |
| @@ -32445,11 +32653,11 @@ | |
| 32653 | UNUSED_PARAMETER(in); |
| 32654 | UNUSED_PARAMETER(isContinuation); |
| 32655 | if(!z || !*z){ |
| 32656 | return 0; |
| 32657 | } |
| 32658 | while(*z && IsSpace(*z)) ++z; |
| 32659 | zBegin = z; |
| 32660 | for(; *z && '\n'!=*z; ++nZ, ++z){} |
| 32661 | if(nZ>0 && '\r'==zBegin[nZ-1]){ |
| 32662 | --nZ; |
| 32663 | } |
| @@ -32750,10 +32958,11 @@ | |
| 32958 | " -csv set output mode to 'csv'\n" |
| 32959 | #if !defined(SQLITE_OMIT_DESERIALIZE) |
| 32960 | " -deserialize open the database using sqlite3_deserialize()\n" |
| 32961 | #endif |
| 32962 | " -echo print inputs before execution\n" |
| 32963 | " -escape T ctrl-char escape; T is one of: symbol, ascii, off\n" |
| 32964 | " -init FILENAME read/process named file\n" |
| 32965 | " -[no]header turn headers on or off\n" |
| 32966 | #if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5) |
| 32967 | " -heap SIZE Size of heap for memsys3 or memsys5\n" |
| 32968 | #endif |
| @@ -32797,11 +33006,11 @@ | |
| 33006 | #ifdef SQLITE_HAVE_ZLIB |
| 33007 | " -zip open the file as a ZIP Archive\n" |
| 33008 | #endif |
| 33009 | ; |
| 33010 | static void usage(int showDetail){ |
| 33011 | sqlite3_fprintf(stderr,"Usage: %s [OPTIONS] [FILENAME [SQL...]]\n" |
| 33012 | "FILENAME is the name of an SQLite database. A new database is created\n" |
| 33013 | "if the file does not previously exist. Defaults to :memory:.\n", Argv0); |
| 33014 | if( showDetail ){ |
| 33015 | sqlite3_fprintf(stderr,"OPTIONS include:\n%s", zOptions); |
| 33016 | }else{ |
| @@ -33183,10 +33392,13 @@ | |
| 33392 | data.zNonce = strdup(cmdline_option_value(argc, argv, ++i)); |
| 33393 | }else if( cli_strcmp(z,"-unsafe-testing")==0 ){ |
| 33394 | ShellSetFlag(&data,SHFLG_TestingMode); |
| 33395 | }else if( cli_strcmp(z,"-safe")==0 ){ |
| 33396 | /* no-op - catch this on the second pass */ |
| 33397 | }else if( cli_strcmp(z,"-escape")==0 && i+1<argc ){ |
| 33398 | /* skip over the argument */ |
| 33399 | i++; |
| 33400 | } |
| 33401 | } |
| 33402 | #ifndef SQLITE_SHELL_FIDDLE |
| 33403 | if( !bEnableVfstrace ) verify_uninitialized(); |
| 33404 | #endif |
| @@ -33282,10 +33494,29 @@ | |
| 33494 | }else if( cli_strcmp(z,"-box")==0 ){ |
| 33495 | data.mode = MODE_Box; |
| 33496 | }else if( cli_strcmp(z,"-csv")==0 ){ |
| 33497 | data.mode = MODE_Csv; |
| 33498 | memcpy(data.colSeparator,",",2); |
| 33499 | }else if( cli_strcmp(z,"-escape")==0 && i+1<argc ){ |
| 33500 | /* See similar code at tag-20250224-1 */ |
| 33501 | const char *zEsc = argv[++i]; |
| 33502 | int k; |
| 33503 | for(k=0; k<ArraySize(shell_EscModeNames); k++){ |
| 33504 | if( sqlite3_stricmp(zEsc,shell_EscModeNames[k])==0 ){ |
| 33505 | data.eEscMode = k; |
| 33506 | break; |
| 33507 | } |
| 33508 | } |
| 33509 | if( k>=ArraySize(shell_EscModeNames) ){ |
| 33510 | sqlite3_fprintf(stderr, "unknown control character escape mode \"%s\"" |
| 33511 | " - choices:", zEsc); |
| 33512 | for(k=0; k<ArraySize(shell_EscModeNames); k++){ |
| 33513 | sqlite3_fprintf(stderr, " %s", shell_EscModeNames[k]); |
| 33514 | } |
| 33515 | sqlite3_fprintf(stderr, "\n"); |
| 33516 | exit(1); |
| 33517 | } |
| 33518 | #ifdef SQLITE_HAVE_ZLIB |
| 33519 | }else if( cli_strcmp(z,"-zip")==0 ){ |
| 33520 | data.openMode = SHELL_OPEN_ZIPFILE; |
| 33521 | #endif |
| 33522 | }else if( cli_strcmp(z,"-append")==0 ){ |
| @@ -33443,19 +33674,19 @@ | |
| 33674 | /* Run all arguments that do not begin with '-' as if they were separate |
| 33675 | ** command-line inputs, except for the argToSkip argument which contains |
| 33676 | ** the database filename. |
| 33677 | */ |
| 33678 | for(i=0; i<nCmd; i++){ |
| 33679 | echo_group_input(&data, azCmd[i]); |
| 33680 | if( azCmd[i][0]=='.' ){ |
| 33681 | rc = do_meta_command(azCmd[i], &data); |
| 33682 | if( rc ){ |
| 33683 | if( rc==2 ) rc = 0; |
| 33684 | goto shell_main_exit; |
| 33685 | } |
| 33686 | }else{ |
| 33687 | open_db(&data, 0); |
| 33688 | rc = shell_exec(&data, azCmd[i], &zErrMsg); |
| 33689 | if( zErrMsg || rc ){ |
| 33690 | if( zErrMsg!=0 ){ |
| 33691 | shellEmitError(zErrMsg); |
| 33692 | }else{ |
| 33693 |
+1614
-799
| --- extsrc/sqlite3.c | ||
| +++ extsrc/sqlite3.c | ||
| @@ -16,11 +16,11 @@ | ||
| 16 | 16 | ** if you want a wrapper to interface SQLite with your choice of programming |
| 17 | 17 | ** language. The code for the "sqlite3" command-line shell is also in a |
| 18 | 18 | ** separate file. This file contains only code for the core SQLite library. |
| 19 | 19 | ** |
| 20 | 20 | ** The content in this amalgamation comes from Fossil check-in |
| 21 | -** 57caa3136d1bfca06e4f2285734a4977b8d3 with changes in files: | |
| 21 | +** 18bda13e197e4b4ec7464b3e70012f71edc0 with changes in files: | |
| 22 | 22 | ** |
| 23 | 23 | ** |
| 24 | 24 | */ |
| 25 | 25 | #ifndef SQLITE_AMALGAMATION |
| 26 | 26 | #define SQLITE_CORE 1 |
| @@ -465,11 +465,11 @@ | ||
| 465 | 465 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 466 | 466 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 467 | 467 | */ |
| 468 | 468 | #define SQLITE_VERSION "3.50.0" |
| 469 | 469 | #define SQLITE_VERSION_NUMBER 3050000 |
| 470 | -#define SQLITE_SOURCE_ID "2025-02-18 01:16:26 57caa3136d1bfca06e4f2285734a4977b8d3fa1f75bf87453b975867e9de38fc" | |
| 470 | +#define SQLITE_SOURCE_ID "2025-03-16 00:13:29 18bda13e197e4b4ec7464b3e70012f71edc05f73d8b14bb48bad452f81c7e185" | |
| 471 | 471 | |
| 472 | 472 | /* |
| 473 | 473 | ** CAPI3REF: Run-Time Library Version Numbers |
| 474 | 474 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 475 | 475 | ** |
| @@ -1479,10 +1479,16 @@ | ||
| 1479 | 1479 | ** to block for up to M milliseconds before failing when attempting to |
| 1480 | 1480 | ** obtain a file lock using the xLock or xShmLock methods of the VFS. |
| 1481 | 1481 | ** The parameter is a pointer to a 32-bit signed integer that contains |
| 1482 | 1482 | ** the value that M is to be set to. Before returning, the 32-bit signed |
| 1483 | 1483 | ** integer is overwritten with the previous value of M. |
| 1484 | +** | |
| 1485 | +** <li>[[SQLITE_FCNTL_BLOCK_ON_CONNECT]] | |
| 1486 | +** The [SQLITE_FCNTL_BLOCK_ON_CONNECT] opcode is used to configure the | |
| 1487 | +** VFS to block when taking a SHARED lock to connect to a wal mode database. | |
| 1488 | +** This is used to implement the functionality associated with | |
| 1489 | +** SQLITE_SETLK_BLOCK_ON_CONNECT. | |
| 1484 | 1490 | ** |
| 1485 | 1491 | ** <li>[[SQLITE_FCNTL_DATA_VERSION]] |
| 1486 | 1492 | ** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to |
| 1487 | 1493 | ** a database file. The argument is a pointer to a 32-bit unsigned integer. |
| 1488 | 1494 | ** The "data version" for the pager is written into the pointer. The |
| @@ -1576,10 +1582,11 @@ | ||
| 1576 | 1582 | #define SQLITE_FCNTL_CKPT_START 39 |
| 1577 | 1583 | #define SQLITE_FCNTL_EXTERNAL_READER 40 |
| 1578 | 1584 | #define SQLITE_FCNTL_CKSM_FILE 41 |
| 1579 | 1585 | #define SQLITE_FCNTL_RESET_CACHE 42 |
| 1580 | 1586 | #define SQLITE_FCNTL_NULL_IO 43 |
| 1587 | +#define SQLITE_FCNTL_BLOCK_ON_CONNECT 44 | |
| 1581 | 1588 | |
| 1582 | 1589 | /* deprecated names */ |
| 1583 | 1590 | #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE |
| 1584 | 1591 | #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE |
| 1585 | 1592 | #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO |
| @@ -3332,10 +3339,48 @@ | ||
| 3332 | 3339 | ** |
| 3333 | 3340 | ** See also: [PRAGMA busy_timeout] |
| 3334 | 3341 | */ |
| 3335 | 3342 | SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); |
| 3336 | 3343 | |
| 3344 | +/* | |
| 3345 | +** CAPI3REF: Set the Setlk Timeout | |
| 3346 | +** METHOD: sqlite3 | |
| 3347 | +** | |
| 3348 | +** This routine is only useful in SQLITE_ENABLE_SETLK_TIMEOUT builds. If | |
| 3349 | +** the VFS supports blocking locks, it sets the timeout in ms used by | |
| 3350 | +** eligible locks taken on wal mode databases by the specified database | |
| 3351 | +** handle. In non-SQLITE_ENABLE_SETLK_TIMEOUT builds, or if the VFS does | |
| 3352 | +** not support blocking locks, this function is a no-op. | |
| 3353 | +** | |
| 3354 | +** Passing 0 to this function disables blocking locks altogether. Passing | |
| 3355 | +** -1 to this function requests that the VFS blocks for a long time - | |
| 3356 | +** indefinitely if possible. The results of passing any other negative value | |
| 3357 | +** are undefined. | |
| 3358 | +** | |
| 3359 | +** Internally, each SQLite database handle store two timeout values - the | |
| 3360 | +** busy-timeout (used for rollback mode databases, or if the VFS does not | |
| 3361 | +** support blocking locks) and the setlk-timeout (used for blocking locks | |
| 3362 | +** on wal-mode databases). The sqlite3_busy_timeout() method sets both | |
| 3363 | +** values, this function sets only the setlk-timeout value. Therefore, | |
| 3364 | +** to configure separate busy-timeout and setlk-timeout values for a single | |
| 3365 | +** database handle, call sqlite3_busy_timeout() followed by this function. | |
| 3366 | +** | |
| 3367 | +** Whenever the number of connections to a wal mode database falls from | |
| 3368 | +** 1 to 0, the last connection takes an exclusive lock on the database, | |
| 3369 | +** then checkpoints and deletes the wal file. While it is doing this, any | |
| 3370 | +** new connection that tries to read from the database fails with an | |
| 3371 | +** SQLITE_BUSY error. Or, if the SQLITE_SETLK_BLOCK_ON_CONNECT flag is | |
| 3372 | +** passed to this API, the new connection blocks until the exclusive lock | |
| 3373 | +** has been released. | |
| 3374 | +*/ | |
| 3375 | +SQLITE_API int sqlite3_setlk_timeout(sqlite3*, int ms, int flags); | |
| 3376 | + | |
| 3377 | +/* | |
| 3378 | +** CAPI3REF: Flags for sqlite3_setlk_timeout() | |
| 3379 | +*/ | |
| 3380 | +#define SQLITE_SETLK_BLOCK_ON_CONNECT 0x01 | |
| 3381 | + | |
| 3337 | 3382 | /* |
| 3338 | 3383 | ** CAPI3REF: Convenience Routines For Running Queries |
| 3339 | 3384 | ** METHOD: sqlite3 |
| 3340 | 3385 | ** |
| 3341 | 3386 | ** This is a legacy interface that is preserved for backwards compatibility. |
| @@ -5447,11 +5492,11 @@ | ||
| 5447 | 5492 | ** For all versions of SQLite up to and including 3.6.23.1, a call to |
| 5448 | 5493 | ** [sqlite3_reset()] was required after sqlite3_step() returned anything |
| 5449 | 5494 | ** other than [SQLITE_ROW] before any subsequent invocation of |
| 5450 | 5495 | ** sqlite3_step(). Failure to reset the prepared statement using |
| 5451 | 5496 | ** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from |
| 5452 | -** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1], | |
| 5497 | +** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1]), | |
| 5453 | 5498 | ** sqlite3_step() began |
| 5454 | 5499 | ** calling [sqlite3_reset()] automatically in this circumstance rather |
| 5455 | 5500 | ** than returning [SQLITE_MISUSE]. This is not considered a compatibility |
| 5456 | 5501 | ** break because any application that ever receives an SQLITE_MISUSE error |
| 5457 | 5502 | ** is broken by definition. The [SQLITE_OMIT_AUTORESET] compile-time option |
| @@ -7343,10 +7388,12 @@ | ||
| 7343 | 7388 | ** ^Any callback set by a previous call to this function |
| 7344 | 7389 | ** for the same database connection is overridden. |
| 7345 | 7390 | ** |
| 7346 | 7391 | ** ^The second argument is a pointer to the function to invoke when a |
| 7347 | 7392 | ** row is updated, inserted or deleted in a rowid table. |
| 7393 | +** ^The update hook is disabled by invoking sqlite3_update_hook() | |
| 7394 | +** with a NULL pointer as the second parameter. | |
| 7348 | 7395 | ** ^The first argument to the callback is a copy of the third argument |
| 7349 | 7396 | ** to sqlite3_update_hook(). |
| 7350 | 7397 | ** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE], |
| 7351 | 7398 | ** or [SQLITE_UPDATE], depending on the operation that caused the callback |
| 7352 | 7399 | ** to be invoked. |
| @@ -14097,18 +14144,26 @@ | ||
| 14097 | 14144 | ** * Terms in the SET clause of an UPDATE statement |
| 14098 | 14145 | ** * Terms in the result set of a SELECT statement |
| 14099 | 14146 | ** * Terms in the GROUP BY or ORDER BY clauses of a SELECT statement. |
| 14100 | 14147 | ** * Terms in the VALUES clause of an INSERT statement |
| 14101 | 14148 | ** |
| 14102 | -** The hard upper limit here is 32676. Most database people will | |
| 14149 | +** The hard upper limit here is 32767. Most database people will | |
| 14103 | 14150 | ** tell you that in a well-normalized database, you usually should |
| 14104 | 14151 | ** not have more than a dozen or so columns in any table. And if |
| 14105 | 14152 | ** that is the case, there is no point in having more than a few |
| 14106 | 14153 | ** dozen values in any of the other situations described above. |
| 14154 | +** | |
| 14155 | +** An index can only have SQLITE_MAX_COLUMN columns from the user | |
| 14156 | +** point of view, but the underlying b-tree that implements the index | |
| 14157 | +** might have up to twice as many columns in a WITHOUT ROWID table, | |
| 14158 | +** since must also store the primary key at the end. Hence the | |
| 14159 | +** column count for Index is u16 instead of i16. | |
| 14107 | 14160 | */ |
| 14108 | -#ifndef SQLITE_MAX_COLUMN | |
| 14161 | +#if !defined(SQLITE_MAX_COLUMN) | |
| 14109 | 14162 | # define SQLITE_MAX_COLUMN 2000 |
| 14163 | +#elif SQLITE_MAX_COLUMN>32767 | |
| 14164 | +# error SQLITE_MAX_COLUMN may not exceed 32767 | |
| 14110 | 14165 | #endif |
| 14111 | 14166 | |
| 14112 | 14167 | /* |
| 14113 | 14168 | ** The maximum length of a single SQL statement in bytes. |
| 14114 | 14169 | ** |
| @@ -15117,11 +15172,21 @@ | ||
| 15117 | 15172 | /* |
| 15118 | 15173 | ** GCC does not define the offsetof() macro so we'll have to do it |
| 15119 | 15174 | ** ourselves. |
| 15120 | 15175 | */ |
| 15121 | 15176 | #ifndef offsetof |
| 15122 | -#define offsetof(STRUCTURE,FIELD) ((int)((char*)&((STRUCTURE*)0)->FIELD)) | |
| 15177 | +#define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD)) | |
| 15178 | +#endif | |
| 15179 | + | |
| 15180 | +/* | |
| 15181 | +** Work around C99 "flex-array" syntax for pre-C99 compilers, so as | |
| 15182 | +** to avoid complaints from -fsanitize=strict-bounds. | |
| 15183 | +*/ | |
| 15184 | +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) | |
| 15185 | +# define FLEXARRAY | |
| 15186 | +#else | |
| 15187 | +# define FLEXARRAY 1 | |
| 15123 | 15188 | #endif |
| 15124 | 15189 | |
| 15125 | 15190 | /* |
| 15126 | 15191 | ** Macros to compute minimum and maximum of two numbers. |
| 15127 | 15192 | */ |
| @@ -17352,12 +17417,12 @@ | ||
| 17352 | 17417 | SQLITE_PRIVATE int sqlite3NotPureFunc(sqlite3_context*); |
| 17353 | 17418 | #ifdef SQLITE_ENABLE_BYTECODE_VTAB |
| 17354 | 17419 | SQLITE_PRIVATE int sqlite3VdbeBytecodeVtabInit(sqlite3*); |
| 17355 | 17420 | #endif |
| 17356 | 17421 | |
| 17357 | -/* Use SQLITE_ENABLE_COMMENTS to enable generation of extra comments on | |
| 17358 | -** each VDBE opcode. | |
| 17422 | +/* Use SQLITE_ENABLE_EXPLAIN_COMMENTS to enable generation of extra | |
| 17423 | +** comments on each VDBE opcode. | |
| 17359 | 17424 | ** |
| 17360 | 17425 | ** Use the SQLITE_ENABLE_MODULE_COMMENTS macro to see some extra no-op |
| 17361 | 17426 | ** comments in VDBE programs that show key decision points in the code |
| 17362 | 17427 | ** generator. |
| 17363 | 17428 | */ |
| @@ -18076,10 +18141,14 @@ | ||
| 18076 | 18141 | BusyHandler busyHandler; /* Busy callback */ |
| 18077 | 18142 | Db aDbStatic[2]; /* Static space for the 2 default backends */ |
| 18078 | 18143 | Savepoint *pSavepoint; /* List of active savepoints */ |
| 18079 | 18144 | int nAnalysisLimit; /* Number of index rows to ANALYZE */ |
| 18080 | 18145 | int busyTimeout; /* Busy handler timeout, in msec */ |
| 18146 | +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT | |
| 18147 | + int setlkTimeout; /* Blocking lock timeout, in msec. -1 -> inf. */ | |
| 18148 | + int setlkFlags; /* Flags passed to setlk_timeout() */ | |
| 18149 | +#endif | |
| 18081 | 18150 | int nSavepoint; /* Number of non-transaction savepoints */ |
| 18082 | 18151 | int nStatement; /* Number of nested statement-transactions */ |
| 18083 | 18152 | i64 nDeferredCons; /* Net deferred constraints this transaction. */ |
| 18084 | 18153 | i64 nDeferredImmCons; /* Net deferred immediate constraints */ |
| 18085 | 18154 | int *pnBytesFreed; /* If not NULL, increment this in DbFree() */ |
| @@ -18888,12 +18957,16 @@ | ||
| 18888 | 18957 | u8 aAction[2]; /* ON DELETE and ON UPDATE actions, respectively */ |
| 18889 | 18958 | Trigger *apTrigger[2];/* Triggers for aAction[] actions */ |
| 18890 | 18959 | struct sColMap { /* Mapping of columns in pFrom to columns in zTo */ |
| 18891 | 18960 | int iFrom; /* Index of column in pFrom */ |
| 18892 | 18961 | char *zCol; /* Name of column in zTo. If NULL use PRIMARY KEY */ |
| 18893 | - } aCol[1]; /* One entry for each of nCol columns */ | |
| 18962 | + } aCol[FLEXARRAY]; /* One entry for each of nCol columns */ | |
| 18894 | 18963 | }; |
| 18964 | + | |
| 18965 | +/* The size (in bytes) of an FKey object holding N columns. The answer | |
| 18966 | +** does NOT include space to hold the zTo name. */ | |
| 18967 | +#define SZ_FKEY(N) (offsetof(FKey,aCol)+(N)*sizeof(struct sColMap)) | |
| 18895 | 18968 | |
| 18896 | 18969 | /* |
| 18897 | 18970 | ** SQLite supports many different ways to resolve a constraint |
| 18898 | 18971 | ** error. ROLLBACK processing means that a constraint violation |
| 18899 | 18972 | ** causes the operation in process to fail and for the current transaction |
| @@ -18952,13 +19025,16 @@ | ||
| 18952 | 19025 | u8 enc; /* Text encoding - one of the SQLITE_UTF* values */ |
| 18953 | 19026 | u16 nKeyField; /* Number of key columns in the index */ |
| 18954 | 19027 | u16 nAllField; /* Total columns, including key plus others */ |
| 18955 | 19028 | sqlite3 *db; /* The database connection */ |
| 18956 | 19029 | u8 *aSortFlags; /* Sort order for each column. */ |
| 18957 | - CollSeq *aColl[1]; /* Collating sequence for each term of the key */ | |
| 19030 | + CollSeq *aColl[FLEXARRAY]; /* Collating sequence for each term of the key */ | |
| 18958 | 19031 | }; |
| 18959 | 19032 | |
| 19033 | +/* The size (in bytes) of a KeyInfo object with up to N fields */ | |
| 19034 | +#define SZ_KEYINFO(N) (offsetof(KeyInfo,aColl) + (N)*sizeof(CollSeq*)) | |
| 19035 | + | |
| 18960 | 19036 | /* |
| 18961 | 19037 | ** Allowed bit values for entries in the KeyInfo.aSortFlags[] array. |
| 18962 | 19038 | */ |
| 18963 | 19039 | #define KEYINFO_ORDER_DESC 0x01 /* DESC sort order */ |
| 18964 | 19040 | #define KEYINFO_ORDER_BIGNULL 0x02 /* NULL is larger than any other value */ |
| @@ -19074,11 +19150,11 @@ | ||
| 19074 | 19150 | Expr *pPartIdxWhere; /* WHERE clause for partial indices */ |
| 19075 | 19151 | ExprList *aColExpr; /* Column expressions */ |
| 19076 | 19152 | Pgno tnum; /* DB Page containing root of this index */ |
| 19077 | 19153 | LogEst szIdxRow; /* Estimated average row size in bytes */ |
| 19078 | 19154 | u16 nKeyCol; /* Number of columns forming the key */ |
| 19079 | - u16 nColumn; /* Number of columns stored in the index */ | |
| 19155 | + u16 nColumn; /* Nr columns in btree. Can be 2*Table.nCol */ | |
| 19080 | 19156 | u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ |
| 19081 | 19157 | unsigned idxType:2; /* 0:Normal 1:UNIQUE, 2:PRIMARY KEY, 3:IPK */ |
| 19082 | 19158 | unsigned bUnordered:1; /* Use this index for == or IN queries only */ |
| 19083 | 19159 | unsigned uniqNotNull:1; /* True if UNIQUE and NOT NULL for all columns */ |
| 19084 | 19160 | unsigned isResized:1; /* True if resizeIndexObject() has been called */ |
| @@ -19412,14 +19488,14 @@ | ||
| 19412 | 19488 | #define EP_Propagate (EP_Collate|EP_Subquery|EP_HasFunc) |
| 19413 | 19489 | |
| 19414 | 19490 | /* Macros can be used to test, set, or clear bits in the |
| 19415 | 19491 | ** Expr.flags field. |
| 19416 | 19492 | */ |
| 19417 | -#define ExprHasProperty(E,P) (((E)->flags&(P))!=0) | |
| 19418 | -#define ExprHasAllProperty(E,P) (((E)->flags&(P))==(P)) | |
| 19419 | -#define ExprSetProperty(E,P) (E)->flags|=(P) | |
| 19420 | -#define ExprClearProperty(E,P) (E)->flags&=~(P) | |
| 19493 | +#define ExprHasProperty(E,P) (((E)->flags&(u32)(P))!=0) | |
| 19494 | +#define ExprHasAllProperty(E,P) (((E)->flags&(u32)(P))==(u32)(P)) | |
| 19495 | +#define ExprSetProperty(E,P) (E)->flags|=(u32)(P) | |
| 19496 | +#define ExprClearProperty(E,P) (E)->flags&=~(u32)(P) | |
| 19421 | 19497 | #define ExprAlwaysTrue(E) (((E)->flags&(EP_OuterON|EP_IsTrue))==EP_IsTrue) |
| 19422 | 19498 | #define ExprAlwaysFalse(E) (((E)->flags&(EP_OuterON|EP_IsFalse))==EP_IsFalse) |
| 19423 | 19499 | #define ExprIsFullSize(E) (((E)->flags&(EP_Reduced|EP_TokenOnly))==0) |
| 19424 | 19500 | |
| 19425 | 19501 | /* Macros used to ensure that the correct members of unions are accessed |
| @@ -19527,12 +19603,17 @@ | ||
| 19527 | 19603 | u16 iAlias; /* Index into Parse.aAlias[] for zName */ |
| 19528 | 19604 | } x; |
| 19529 | 19605 | int iConstExprReg; /* Register in which Expr value is cached. Used only |
| 19530 | 19606 | ** by Parse.pConstExpr */ |
| 19531 | 19607 | } u; |
| 19532 | - } a[1]; /* One slot for each expression in the list */ | |
| 19608 | + } a[FLEXARRAY]; /* One slot for each expression in the list */ | |
| 19533 | 19609 | }; |
| 19610 | + | |
| 19611 | +/* The size (in bytes) of an ExprList object that is big enough to hold | |
| 19612 | +** as many as N expressions. */ | |
| 19613 | +#define SZ_EXPRLIST(N) \ | |
| 19614 | + (offsetof(ExprList,a) + (N)*sizeof(struct ExprList_item)) | |
| 19534 | 19615 | |
| 19535 | 19616 | /* |
| 19536 | 19617 | ** Allowed values for Expr.a.eEName |
| 19537 | 19618 | */ |
| 19538 | 19619 | #define ENAME_NAME 0 /* The AS clause of a result set */ |
| @@ -19557,13 +19638,16 @@ | ||
| 19557 | 19638 | */ |
| 19558 | 19639 | struct IdList { |
| 19559 | 19640 | int nId; /* Number of identifiers on the list */ |
| 19560 | 19641 | struct IdList_item { |
| 19561 | 19642 | char *zName; /* Name of the identifier */ |
| 19562 | - } a[1]; | |
| 19643 | + } a[FLEXARRAY]; | |
| 19563 | 19644 | }; |
| 19564 | 19645 | |
| 19646 | +/* The size (in bytes) of an IdList object that can hold up to N IDs. */ | |
| 19647 | +#define SZ_IDLIST(N) (offsetof(IdList,a)+(N)*sizeof(struct IdList_item)) | |
| 19648 | + | |
| 19565 | 19649 | /* |
| 19566 | 19650 | ** Allowed values for IdList.eType, which determines which value of the a.u4 |
| 19567 | 19651 | ** is valid. |
| 19568 | 19652 | */ |
| 19569 | 19653 | #define EU4_NONE 0 /* Does not use IdList.a.u4 */ |
| @@ -19679,14 +19763,22 @@ | ||
| 19679 | 19763 | ** is used to hold the FROM clause of a SELECT statement. SrcList also |
| 19680 | 19764 | ** represents the target tables for DELETE, INSERT, and UPDATE statements. |
| 19681 | 19765 | ** |
| 19682 | 19766 | */ |
| 19683 | 19767 | struct SrcList { |
| 19684 | - int nSrc; /* Number of tables or subqueries in the FROM clause */ | |
| 19685 | - u32 nAlloc; /* Number of entries allocated in a[] below */ | |
| 19686 | - SrcItem a[1]; /* One entry for each identifier on the list */ | |
| 19768 | + int nSrc; /* Number of tables or subqueries in the FROM clause */ | |
| 19769 | + u32 nAlloc; /* Number of entries allocated in a[] below */ | |
| 19770 | + SrcItem a[FLEXARRAY]; /* One entry for each identifier on the list */ | |
| 19687 | 19771 | }; |
| 19772 | + | |
| 19773 | +/* Size (in bytes) of a SrcList object that can hold as many as N | |
| 19774 | +** SrcItem objects. */ | |
| 19775 | +#define SZ_SRCLIST(N) (offsetof(SrcList,a)+(N)*sizeof(SrcItem)) | |
| 19776 | + | |
| 19777 | +/* Size (in bytes( of a SrcList object that holds 1 SrcItem. This is a | |
| 19778 | +** special case of SZ_SRCITEM(1) that comes up often. */ | |
| 19779 | +#define SZ_SRCLIST_1 (offsetof(SrcList,a)+sizeof(SrcItem)) | |
| 19688 | 19780 | |
| 19689 | 19781 | /* |
| 19690 | 19782 | ** Permitted values of the SrcList.a.jointype field |
| 19691 | 19783 | */ |
| 19692 | 19784 | #define JT_INNER 0x01 /* Any kind of inner or cross join */ |
| @@ -20747,12 +20839,16 @@ | ||
| 20747 | 20839 | */ |
| 20748 | 20840 | struct With { |
| 20749 | 20841 | int nCte; /* Number of CTEs in the WITH clause */ |
| 20750 | 20842 | int bView; /* Belongs to the outermost Select of a view */ |
| 20751 | 20843 | With *pOuter; /* Containing WITH clause, or NULL */ |
| 20752 | - Cte a[1]; /* For each CTE in the WITH clause.... */ | |
| 20844 | + Cte a[FLEXARRAY]; /* For each CTE in the WITH clause.... */ | |
| 20753 | 20845 | }; |
| 20846 | + | |
| 20847 | +/* The size (in bytes) of a With object that can hold as many | |
| 20848 | +** as N different CTEs. */ | |
| 20849 | +#define SZ_WITH(N) (offsetof(With,a) + (N)*sizeof(Cte)) | |
| 20754 | 20850 | |
| 20755 | 20851 | /* |
| 20756 | 20852 | ** The Cte object is not guaranteed to persist for the entire duration |
| 20757 | 20853 | ** of code generation. (The query flattener or other parser tree |
| 20758 | 20854 | ** edits might delete it.) The following object records information |
| @@ -20778,12 +20874,16 @@ | ||
| 20778 | 20874 | */ |
| 20779 | 20875 | struct DbClientData { |
| 20780 | 20876 | DbClientData *pNext; /* Next in a linked list */ |
| 20781 | 20877 | void *pData; /* The data */ |
| 20782 | 20878 | void (*xDestructor)(void*); /* Destructor. Might be NULL */ |
| 20783 | - char zName[1]; /* Name of this client data. MUST BE LAST */ | |
| 20879 | + char zName[FLEXARRAY]; /* Name of this client data. MUST BE LAST */ | |
| 20784 | 20880 | }; |
| 20881 | + | |
| 20882 | +/* The size (in bytes) of a DbClientData object that can has a name | |
| 20883 | +** that is N bytes long, including the zero-terminator. */ | |
| 20884 | +#define SZ_DBCLIENTDATA(N) (offsetof(DbClientData,zName)+(N)) | |
| 20785 | 20885 | |
| 20786 | 20886 | #ifdef SQLITE_DEBUG |
| 20787 | 20887 | /* |
| 20788 | 20888 | ** An instance of the TreeView object is used for printing the content of |
| 20789 | 20889 | ** data structures on sqlite3DebugPrintf() using a tree-like view. |
| @@ -21223,11 +21323,11 @@ | ||
| 21223 | 21323 | SQLITE_PRIVATE int sqlite3ColumnsFromExprList(Parse*,ExprList*,i16*,Column**); |
| 21224 | 21324 | SQLITE_PRIVATE void sqlite3SubqueryColumnTypes(Parse*,Table*,Select*,char); |
| 21225 | 21325 | SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse*,Select*,char); |
| 21226 | 21326 | SQLITE_PRIVATE void sqlite3OpenSchemaTable(Parse *, int); |
| 21227 | 21327 | SQLITE_PRIVATE Index *sqlite3PrimaryKeyIndex(Table*); |
| 21228 | -SQLITE_PRIVATE i16 sqlite3TableColumnToIndex(Index*, i16); | |
| 21328 | +SQLITE_PRIVATE int sqlite3TableColumnToIndex(Index*, int); | |
| 21229 | 21329 | #ifdef SQLITE_OMIT_GENERATED_COLUMNS |
| 21230 | 21330 | # define sqlite3TableColumnToStorage(T,X) (X) /* No-op pass-through */ |
| 21231 | 21331 | # define sqlite3StorageColumnToTable(T,X) (X) /* No-op pass-through */ |
| 21232 | 21332 | #else |
| 21233 | 21333 | SQLITE_PRIVATE i16 sqlite3TableColumnToStorage(Table*, i16); |
| @@ -21321,11 +21421,11 @@ | ||
| 21321 | 21421 | SQLITE_PRIVATE void sqlite3SrcListShiftJoinType(Parse*,SrcList*); |
| 21322 | 21422 | SQLITE_PRIVATE void sqlite3SrcListAssignCursors(Parse*, SrcList*); |
| 21323 | 21423 | SQLITE_PRIVATE void sqlite3IdListDelete(sqlite3*, IdList*); |
| 21324 | 21424 | SQLITE_PRIVATE void sqlite3ClearOnOrUsing(sqlite3*, OnOrUsing*); |
| 21325 | 21425 | SQLITE_PRIVATE void sqlite3SrcListDelete(sqlite3*, SrcList*); |
| 21326 | -SQLITE_PRIVATE Index *sqlite3AllocateIndexObject(sqlite3*,i16,int,char**); | |
| 21426 | +SQLITE_PRIVATE Index *sqlite3AllocateIndexObject(sqlite3*,int,int,char**); | |
| 21327 | 21427 | SQLITE_PRIVATE void sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*, |
| 21328 | 21428 | Expr*, int, int, u8); |
| 21329 | 21429 | SQLITE_PRIVATE void sqlite3DropIndex(Parse*, SrcList*, int); |
| 21330 | 21430 | SQLITE_PRIVATE int sqlite3Select(Parse*, Select*, SelectDest*); |
| 21331 | 21431 | SQLITE_PRIVATE Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*, |
| @@ -21457,11 +21557,12 @@ | ||
| 21457 | 21557 | SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3*,const IdList*); |
| 21458 | 21558 | SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3*,const Select*,int); |
| 21459 | 21559 | SQLITE_PRIVATE FuncDef *sqlite3FunctionSearch(int,const char*); |
| 21460 | 21560 | SQLITE_PRIVATE void sqlite3InsertBuiltinFuncs(FuncDef*,int); |
| 21461 | 21561 | SQLITE_PRIVATE FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,u8,u8); |
| 21462 | -SQLITE_PRIVATE void sqlite3QuoteValue(StrAccum*,sqlite3_value*); | |
| 21562 | +SQLITE_PRIVATE void sqlite3QuoteValue(StrAccum*,sqlite3_value*,int); | |
| 21563 | +SQLITE_PRIVATE int sqlite3AppendOneUtf8Character(char*, u32); | |
| 21463 | 21564 | SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void); |
| 21464 | 21565 | SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void); |
| 21465 | 21566 | SQLITE_PRIVATE void sqlite3RegisterJsonFunctions(void); |
| 21466 | 21567 | SQLITE_PRIVATE void sqlite3RegisterPerConnectionBuiltinFunctions(sqlite3*); |
| 21467 | 21568 | #if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON) |
| @@ -22557,10 +22658,13 @@ | ||
| 22557 | 22658 | #ifdef SQLITE_ENABLE_RTREE |
| 22558 | 22659 | "ENABLE_RTREE", |
| 22559 | 22660 | #endif |
| 22560 | 22661 | #ifdef SQLITE_ENABLE_SESSION |
| 22561 | 22662 | "ENABLE_SESSION", |
| 22663 | +#endif | |
| 22664 | +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT | |
| 22665 | + "ENABLE_SETLK_TIMEOUT", | |
| 22562 | 22666 | #endif |
| 22563 | 22667 | #ifdef SQLITE_ENABLE_SNAPSHOT |
| 22564 | 22668 | "ENABLE_SNAPSHOT", |
| 22565 | 22669 | #endif |
| 22566 | 22670 | #ifdef SQLITE_ENABLE_SORTER_REFERENCES |
| @@ -22612,10 +22716,13 @@ | ||
| 22612 | 22716 | "EXTRA_IFNULLROW", |
| 22613 | 22717 | #endif |
| 22614 | 22718 | #ifdef SQLITE_EXTRA_INIT |
| 22615 | 22719 | "EXTRA_INIT=" CTIMEOPT_VAL(SQLITE_EXTRA_INIT), |
| 22616 | 22720 | #endif |
| 22721 | +#ifdef SQLITE_EXTRA_INIT_MUTEXED | |
| 22722 | + "EXTRA_INIT_MUTEXED=" CTIMEOPT_VAL(SQLITE_EXTRA_INIT_MUTEXED), | |
| 22723 | +#endif | |
| 22617 | 22724 | #ifdef SQLITE_EXTRA_SHUTDOWN |
| 22618 | 22725 | "EXTRA_SHUTDOWN=" CTIMEOPT_VAL(SQLITE_EXTRA_SHUTDOWN), |
| 22619 | 22726 | #endif |
| 22620 | 22727 | #ifdef SQLITE_FTS3_MAX_EXPR_DEPTH |
| 22621 | 22728 | "FTS3_MAX_EXPR_DEPTH=" CTIMEOPT_VAL(SQLITE_FTS3_MAX_EXPR_DEPTH), |
| @@ -23596,16 +23703,23 @@ | ||
| 23596 | 23703 | #ifdef SQLITE_ENABLE_COLUMN_USED_MASK |
| 23597 | 23704 | u64 maskUsed; /* Mask of columns used by this cursor */ |
| 23598 | 23705 | #endif |
| 23599 | 23706 | VdbeTxtBlbCache *pCache; /* Cache of large TEXT or BLOB values */ |
| 23600 | 23707 | |
| 23601 | - /* 2*nField extra array elements allocated for aType[], beyond the one | |
| 23602 | - ** static element declared in the structure. nField total array slots for | |
| 23603 | - ** aType[] and nField+1 array slots for aOffset[] */ | |
| 23604 | - u32 aType[1]; /* Type values record decode. MUST BE LAST */ | |
| 23708 | + /* Space is allocated for aType to hold at least 2*nField+1 entries: | |
| 23709 | + ** nField slots for aType[] and nField+1 array slots for aOffset[] */ | |
| 23710 | + u32 aType[FLEXARRAY]; /* Type values record decode. MUST BE LAST */ | |
| 23605 | 23711 | }; |
| 23606 | 23712 | |
| 23713 | +/* | |
| 23714 | +** The size (in bytes) of a VdbeCursor object that has an nField value of N | |
| 23715 | +** or less. The value of SZ_VDBECURSOR(n) is guaranteed to be a multiple | |
| 23716 | +** of 8. | |
| 23717 | +*/ | |
| 23718 | +#define SZ_VDBECURSOR(N) \ | |
| 23719 | + (ROUND8(offsetof(VdbeCursor,aType)) + ((N)+1)*sizeof(u64)) | |
| 23720 | + | |
| 23607 | 23721 | /* Return true if P is a null-only cursor |
| 23608 | 23722 | */ |
| 23609 | 23723 | #define IsNullCursor(P) \ |
| 23610 | 23724 | ((P)->eCurType==CURTYPE_PSEUDO && (P)->nullRow && (P)->seekResult==0) |
| 23611 | 23725 | |
| @@ -23858,13 +23972,20 @@ | ||
| 23858 | 23972 | int iOp; /* Instruction number of OP_Function */ |
| 23859 | 23973 | int isError; /* Error code returned by the function. */ |
| 23860 | 23974 | u8 enc; /* Encoding to use for results */ |
| 23861 | 23975 | u8 skipFlag; /* Skip accumulator loading if true */ |
| 23862 | 23976 | u16 argc; /* Number of arguments */ |
| 23863 | - sqlite3_value *argv[1]; /* Argument set */ | |
| 23977 | + sqlite3_value *argv[FLEXARRAY]; /* Argument set */ | |
| 23864 | 23978 | }; |
| 23865 | 23979 | |
| 23980 | +/* | |
| 23981 | +** The size (in bytes) of an sqlite3_context object that holds N | |
| 23982 | +** argv[] arguments. | |
| 23983 | +*/ | |
| 23984 | +#define SZ_CONTEXT(N) \ | |
| 23985 | + (offsetof(sqlite3_context,argv)+(N)*sizeof(sqlite3_value*)) | |
| 23986 | + | |
| 23866 | 23987 | |
| 23867 | 23988 | /* The ScanStatus object holds a single value for the |
| 23868 | 23989 | ** sqlite3_stmt_scanstatus() interface. |
| 23869 | 23990 | ** |
| 23870 | 23991 | ** aAddrRange[]: |
| @@ -23994,11 +24115,11 @@ | ||
| 23994 | 24115 | struct PreUpdate { |
| 23995 | 24116 | Vdbe *v; |
| 23996 | 24117 | VdbeCursor *pCsr; /* Cursor to read old values from */ |
| 23997 | 24118 | int op; /* One of SQLITE_INSERT, UPDATE, DELETE */ |
| 23998 | 24119 | u8 *aRecord; /* old.* database record */ |
| 23999 | - KeyInfo keyinfo; | |
| 24120 | + KeyInfo *pKeyinfo; /* Key information */ | |
| 24000 | 24121 | UnpackedRecord *pUnpacked; /* Unpacked version of aRecord[] */ |
| 24001 | 24122 | UnpackedRecord *pNewUnpacked; /* Unpacked version of new.* record */ |
| 24002 | 24123 | int iNewReg; /* Register for new.* values */ |
| 24003 | 24124 | int iBlobWrite; /* Value returned by preupdate_blobwrite() */ |
| 24004 | 24125 | i64 iKey1; /* First key value passed to hook */ |
| @@ -24006,10 +24127,11 @@ | ||
| 24006 | 24127 | Mem oldipk; /* Memory cell holding "old" IPK value */ |
| 24007 | 24128 | Mem *aNew; /* Array of new.* values */ |
| 24008 | 24129 | Table *pTab; /* Schema object being updated */ |
| 24009 | 24130 | Index *pPk; /* PK index if pTab is WITHOUT ROWID */ |
| 24010 | 24131 | sqlite3_value **apDflt; /* Array of default values, if required */ |
| 24132 | + u8 keyinfoSpace[SZ_KEYINFO(0)]; /* Space to hold pKeyinfo[0] content */ | |
| 24011 | 24133 | }; |
| 24012 | 24134 | |
| 24013 | 24135 | /* |
| 24014 | 24136 | ** An instance of this object is used to pass an vector of values into |
| 24015 | 24137 | ** OP_VFilter, the xFilter method of a virtual table. The vector is the |
| @@ -24372,12 +24494,13 @@ | ||
| 24372 | 24494 | u32 nFree = countLookasideSlots(db->lookaside.pFree); |
| 24373 | 24495 | #ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE |
| 24374 | 24496 | nInit += countLookasideSlots(db->lookaside.pSmallInit); |
| 24375 | 24497 | nFree += countLookasideSlots(db->lookaside.pSmallFree); |
| 24376 | 24498 | #endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */ |
| 24377 | - if( pHighwater ) *pHighwater = db->lookaside.nSlot - nInit; | |
| 24378 | - return db->lookaside.nSlot - (nInit+nFree); | |
| 24499 | + assert( db->lookaside.nSlot >= nInit+nFree ); | |
| 24500 | + if( pHighwater ) *pHighwater = (int)(db->lookaside.nSlot - nInit); | |
| 24501 | + return (int)(db->lookaside.nSlot - (nInit+nFree)); | |
| 24379 | 24502 | } |
| 24380 | 24503 | |
| 24381 | 24504 | /* |
| 24382 | 24505 | ** Query status information for a single database connection |
| 24383 | 24506 | */ |
| @@ -24426,11 +24549,11 @@ | ||
| 24426 | 24549 | testcase( op==SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE ); |
| 24427 | 24550 | testcase( op==SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL ); |
| 24428 | 24551 | assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)>=0 ); |
| 24429 | 24552 | assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)<3 ); |
| 24430 | 24553 | *pCurrent = 0; |
| 24431 | - *pHighwater = db->lookaside.anStat[op - SQLITE_DBSTATUS_LOOKASIDE_HIT]; | |
| 24554 | + *pHighwater = (int)db->lookaside.anStat[op-SQLITE_DBSTATUS_LOOKASIDE_HIT]; | |
| 24432 | 24555 | if( resetFlag ){ |
| 24433 | 24556 | db->lookaside.anStat[op - SQLITE_DBSTATUS_LOOKASIDE_HIT] = 0; |
| 24434 | 24557 | } |
| 24435 | 24558 | break; |
| 24436 | 24559 | } |
| @@ -25938,11 +26061,11 @@ | ||
| 25938 | 26061 | ** Return the number of days after the most recent Sunday. |
| 25939 | 26062 | ** |
| 25940 | 26063 | ** In other words, return the day of the week according |
| 25941 | 26064 | ** to this code: |
| 25942 | 26065 | ** |
| 25943 | -** 0=Sunday, 1=Monday, 2=Tues, ..., 6=Saturday | |
| 26066 | +** 0=Sunday, 1=Monday, 2=Tuesday, ..., 6=Saturday | |
| 25944 | 26067 | */ |
| 25945 | 26068 | static int daysAfterSunday(DateTime *pDate){ |
| 25946 | 26069 | assert( pDate->validJD ); |
| 25947 | 26070 | return (int)((pDate->iJD+129600000)/86400000) % 7; |
| 25948 | 26071 | } |
| @@ -31541,21 +31664,21 @@ | ||
| 31541 | 31664 | #define etSTRING 5 /* Strings. %s */ |
| 31542 | 31665 | #define etDYNSTRING 6 /* Dynamically allocated strings. %z */ |
| 31543 | 31666 | #define etPERCENT 7 /* Percent symbol. %% */ |
| 31544 | 31667 | #define etCHARX 8 /* Characters. %c */ |
| 31545 | 31668 | /* The rest are extensions, not normally found in printf() */ |
| 31546 | -#define etSQLESCAPE 9 /* Strings with '\'' doubled. %q */ | |
| 31547 | -#define etSQLESCAPE2 10 /* Strings with '\'' doubled and enclosed in '', | |
| 31548 | - NULL pointers replaced by SQL NULL. %Q */ | |
| 31549 | -#define etTOKEN 11 /* a pointer to a Token structure */ | |
| 31550 | -#define etSRCITEM 12 /* a pointer to a SrcItem */ | |
| 31551 | -#define etPOINTER 13 /* The %p conversion */ | |
| 31552 | -#define etSQLESCAPE3 14 /* %w -> Strings with '\"' doubled */ | |
| 31553 | -#define etORDINAL 15 /* %r -> 1st, 2nd, 3rd, 4th, etc. English only */ | |
| 31554 | -#define etDECIMAL 16 /* %d or %u, but not %x, %o */ | |
| 31555 | - | |
| 31556 | -#define etINVALID 17 /* Any unrecognized conversion type */ | |
| 31669 | +#define etESCAPE_q 9 /* Strings with '\'' doubled. %q */ | |
| 31670 | +#define etESCAPE_Q 10 /* Strings with '\'' doubled and enclosed in '', | |
| 31671 | + NULL pointers replaced by SQL NULL. %Q */ | |
| 31672 | +#define etTOKEN 11 /* a pointer to a Token structure */ | |
| 31673 | +#define etSRCITEM 12 /* a pointer to a SrcItem */ | |
| 31674 | +#define etPOINTER 13 /* The %p conversion */ | |
| 31675 | +#define etESCAPE_w 14 /* %w -> Strings with '\"' doubled */ | |
| 31676 | +#define etORDINAL 15 /* %r -> 1st, 2nd, 3rd, 4th, etc. English only */ | |
| 31677 | +#define etDECIMAL 16 /* %d or %u, but not %x, %o */ | |
| 31678 | + | |
| 31679 | +#define etINVALID 17 /* Any unrecognized conversion type */ | |
| 31557 | 31680 | |
| 31558 | 31681 | |
| 31559 | 31682 | /* |
| 31560 | 31683 | ** An "etByte" is an 8-bit unsigned value. |
| 31561 | 31684 | */ |
| @@ -31590,13 +31713,13 @@ | ||
| 31590 | 31713 | static const et_info fmtinfo[] = { |
| 31591 | 31714 | { 'd', 10, 1, etDECIMAL, 0, 0 }, |
| 31592 | 31715 | { 's', 0, 4, etSTRING, 0, 0 }, |
| 31593 | 31716 | { 'g', 0, 1, etGENERIC, 30, 0 }, |
| 31594 | 31717 | { 'z', 0, 4, etDYNSTRING, 0, 0 }, |
| 31595 | - { 'q', 0, 4, etSQLESCAPE, 0, 0 }, | |
| 31596 | - { 'Q', 0, 4, etSQLESCAPE2, 0, 0 }, | |
| 31597 | - { 'w', 0, 4, etSQLESCAPE3, 0, 0 }, | |
| 31718 | + { 'q', 0, 4, etESCAPE_q, 0, 0 }, | |
| 31719 | + { 'Q', 0, 4, etESCAPE_Q, 0, 0 }, | |
| 31720 | + { 'w', 0, 4, etESCAPE_w, 0, 0 }, | |
| 31598 | 31721 | { 'c', 0, 0, etCHARX, 0, 0 }, |
| 31599 | 31722 | { 'o', 8, 0, etRADIX, 0, 2 }, |
| 31600 | 31723 | { 'u', 10, 0, etDECIMAL, 0, 0 }, |
| 31601 | 31724 | { 'x', 16, 0, etRADIX, 16, 1 }, |
| 31602 | 31725 | { 'X', 16, 0, etRADIX, 0, 4 }, |
| @@ -32189,29 +32312,11 @@ | ||
| 32189 | 32312 | }else{ |
| 32190 | 32313 | buf[0] = 0; |
| 32191 | 32314 | } |
| 32192 | 32315 | }else{ |
| 32193 | 32316 | unsigned int ch = va_arg(ap,unsigned int); |
| 32194 | - if( ch<0x00080 ){ | |
| 32195 | - buf[0] = ch & 0xff; | |
| 32196 | - length = 1; | |
| 32197 | - }else if( ch<0x00800 ){ | |
| 32198 | - buf[0] = 0xc0 + (u8)((ch>>6)&0x1f); | |
| 32199 | - buf[1] = 0x80 + (u8)(ch & 0x3f); | |
| 32200 | - length = 2; | |
| 32201 | - }else if( ch<0x10000 ){ | |
| 32202 | - buf[0] = 0xe0 + (u8)((ch>>12)&0x0f); | |
| 32203 | - buf[1] = 0x80 + (u8)((ch>>6) & 0x3f); | |
| 32204 | - buf[2] = 0x80 + (u8)(ch & 0x3f); | |
| 32205 | - length = 3; | |
| 32206 | - }else{ | |
| 32207 | - buf[0] = 0xf0 + (u8)((ch>>18) & 0x07); | |
| 32208 | - buf[1] = 0x80 + (u8)((ch>>12) & 0x3f); | |
| 32209 | - buf[2] = 0x80 + (u8)((ch>>6) & 0x3f); | |
| 32210 | - buf[3] = 0x80 + (u8)(ch & 0x3f); | |
| 32211 | - length = 4; | |
| 32212 | - } | |
| 32317 | + length = sqlite3AppendOneUtf8Character(buf, ch); | |
| 32213 | 32318 | } |
| 32214 | 32319 | if( precision>1 ){ |
| 32215 | 32320 | i64 nPrior = 1; |
| 32216 | 32321 | width -= precision-1; |
| 32217 | 32322 | if( width>1 && !flag_leftjustify ){ |
| @@ -32287,26 +32392,35 @@ | ||
| 32287 | 32392 | /* Adjust width to account for extra bytes in UTF-8 characters */ |
| 32288 | 32393 | int ii = length - 1; |
| 32289 | 32394 | while( ii>=0 ) if( (bufpt[ii--] & 0xc0)==0x80 ) width++; |
| 32290 | 32395 | } |
| 32291 | 32396 | break; |
| 32292 | - case etSQLESCAPE: /* %q: Escape ' characters */ | |
| 32293 | - case etSQLESCAPE2: /* %Q: Escape ' and enclose in '...' */ | |
| 32294 | - case etSQLESCAPE3: { /* %w: Escape " characters */ | |
| 32397 | + case etESCAPE_q: /* %q: Escape ' characters */ | |
| 32398 | + case etESCAPE_Q: /* %Q: Escape ' and enclose in '...' */ | |
| 32399 | + case etESCAPE_w: { /* %w: Escape " characters */ | |
| 32295 | 32400 | i64 i, j, k, n; |
| 32296 | - int needQuote, isnull; | |
| 32401 | + int needQuote = 0; | |
| 32297 | 32402 | char ch; |
| 32298 | - char q = ((xtype==etSQLESCAPE3)?'"':'\''); /* Quote character */ | |
| 32299 | 32403 | char *escarg; |
| 32404 | + char q; | |
| 32300 | 32405 | |
| 32301 | 32406 | if( bArgList ){ |
| 32302 | 32407 | escarg = getTextArg(pArgList); |
| 32303 | 32408 | }else{ |
| 32304 | 32409 | escarg = va_arg(ap,char*); |
| 32305 | 32410 | } |
| 32306 | - isnull = escarg==0; | |
| 32307 | - if( isnull ) escarg = (xtype==etSQLESCAPE2 ? "NULL" : "(NULL)"); | |
| 32411 | + if( escarg==0 ){ | |
| 32412 | + escarg = (xtype==etESCAPE_Q ? "NULL" : "(NULL)"); | |
| 32413 | + }else if( xtype==etESCAPE_Q ){ | |
| 32414 | + needQuote = 1; | |
| 32415 | + } | |
| 32416 | + if( xtype==etESCAPE_w ){ | |
| 32417 | + q = '"'; | |
| 32418 | + flag_alternateform = 0; | |
| 32419 | + }else{ | |
| 32420 | + q = '\''; | |
| 32421 | + } | |
| 32308 | 32422 | /* For %q, %Q, and %w, the precision is the number of bytes (or |
| 32309 | 32423 | ** characters if the ! flags is present) to use from the input. |
| 32310 | 32424 | ** Because of the extra quoting characters inserted, the number |
| 32311 | 32425 | ** of output characters may be larger than the precision. |
| 32312 | 32426 | */ |
| @@ -32315,26 +32429,77 @@ | ||
| 32315 | 32429 | if( ch==q ) n++; |
| 32316 | 32430 | if( flag_altform2 && (ch&0xc0)==0xc0 ){ |
| 32317 | 32431 | while( (escarg[i+1]&0xc0)==0x80 ){ i++; } |
| 32318 | 32432 | } |
| 32319 | 32433 | } |
| 32320 | - needQuote = !isnull && xtype==etSQLESCAPE2; | |
| 32434 | + if( flag_alternateform ){ | |
| 32435 | + /* For %#q, do unistr()-style backslash escapes for | |
| 32436 | + ** all control characters, and for backslash itself. | |
| 32437 | + ** For %#Q, do the same but only if there is at least | |
| 32438 | + ** one control character. */ | |
| 32439 | + u32 nBack = 0; | |
| 32440 | + u32 nCtrl = 0; | |
| 32441 | + for(k=0; k<i; k++){ | |
| 32442 | + if( escarg[k]=='\\' ){ | |
| 32443 | + nBack++; | |
| 32444 | + }else if( ((u8*)escarg)[k]<=0x1f ){ | |
| 32445 | + nCtrl++; | |
| 32446 | + } | |
| 32447 | + } | |
| 32448 | + if( nCtrl || xtype==etESCAPE_q ){ | |
| 32449 | + n += nBack + 5*nCtrl; | |
| 32450 | + if( xtype==etESCAPE_Q ){ | |
| 32451 | + n += 10; | |
| 32452 | + needQuote = 2; | |
| 32453 | + } | |
| 32454 | + }else{ | |
| 32455 | + flag_alternateform = 0; | |
| 32456 | + } | |
| 32457 | + } | |
| 32321 | 32458 | n += i + 3; |
| 32322 | 32459 | if( n>etBUFSIZE ){ |
| 32323 | 32460 | bufpt = zExtra = printfTempBuf(pAccum, n); |
| 32324 | 32461 | if( bufpt==0 ) return; |
| 32325 | 32462 | }else{ |
| 32326 | 32463 | bufpt = buf; |
| 32327 | 32464 | } |
| 32328 | 32465 | j = 0; |
| 32329 | - if( needQuote ) bufpt[j++] = q; | |
| 32466 | + if( needQuote ){ | |
| 32467 | + if( needQuote==2 ){ | |
| 32468 | + memcpy(&bufpt[j], "unistr('", 8); | |
| 32469 | + j += 8; | |
| 32470 | + }else{ | |
| 32471 | + bufpt[j++] = '\''; | |
| 32472 | + } | |
| 32473 | + } | |
| 32330 | 32474 | k = i; |
| 32331 | - for(i=0; i<k; i++){ | |
| 32332 | - bufpt[j++] = ch = escarg[i]; | |
| 32333 | - if( ch==q ) bufpt[j++] = ch; | |
| 32475 | + if( flag_alternateform ){ | |
| 32476 | + for(i=0; i<k; i++){ | |
| 32477 | + bufpt[j++] = ch = escarg[i]; | |
| 32478 | + if( ch==q ){ | |
| 32479 | + bufpt[j++] = ch; | |
| 32480 | + }else if( ch=='\\' ){ | |
| 32481 | + bufpt[j++] = '\\'; | |
| 32482 | + }else if( ((unsigned char)ch)<=0x1f ){ | |
| 32483 | + bufpt[j-1] = '\\'; | |
| 32484 | + bufpt[j++] = 'u'; | |
| 32485 | + bufpt[j++] = '0'; | |
| 32486 | + bufpt[j++] = '0'; | |
| 32487 | + bufpt[j++] = ch>=0x10 ? '1' : '0'; | |
| 32488 | + bufpt[j++] = "0123456789abcdef"[ch&0xf]; | |
| 32489 | + } | |
| 32490 | + } | |
| 32491 | + }else{ | |
| 32492 | + for(i=0; i<k; i++){ | |
| 32493 | + bufpt[j++] = ch = escarg[i]; | |
| 32494 | + if( ch==q ) bufpt[j++] = ch; | |
| 32495 | + } | |
| 32334 | 32496 | } |
| 32335 | - if( needQuote ) bufpt[j++] = q; | |
| 32497 | + if( needQuote ){ | |
| 32498 | + bufpt[j++] = '\''; | |
| 32499 | + if( needQuote==2 ) bufpt[j++] = ')'; | |
| 32500 | + } | |
| 32336 | 32501 | bufpt[j] = 0; |
| 32337 | 32502 | length = j; |
| 32338 | 32503 | goto adjust_width_for_utf8; |
| 32339 | 32504 | } |
| 32340 | 32505 | case etTOKEN: { |
| @@ -34828,10 +34993,39 @@ | ||
| 34828 | 34993 | *zOut++ = (u8)(((c>>10)&0x003F) + (((c-0x10000)>>10)&0x00C0)); \ |
| 34829 | 34994 | *zOut++ = (u8)(0x00DC + ((c>>8)&0x03)); \ |
| 34830 | 34995 | *zOut++ = (u8)(c&0x00FF); \ |
| 34831 | 34996 | } \ |
| 34832 | 34997 | } |
| 34998 | + | |
| 34999 | +/* | |
| 35000 | +** Write a single UTF8 character whose value is v into the | |
| 35001 | +** buffer starting at zOut. zOut must be sized to hold at | |
| 35002 | +** least for bytes. Return the number of bytes needed | |
| 35003 | +** to encode the new character. | |
| 35004 | +*/ | |
| 35005 | +SQLITE_PRIVATE int sqlite3AppendOneUtf8Character(char *zOut, u32 v){ | |
| 35006 | + if( v<0x00080 ){ | |
| 35007 | + zOut[0] = (u8)(v & 0xff); | |
| 35008 | + return 1; | |
| 35009 | + } | |
| 35010 | + if( v<0x00800 ){ | |
| 35011 | + zOut[0] = 0xc0 + (u8)((v>>6) & 0x1f); | |
| 35012 | + zOut[1] = 0x80 + (u8)(v & 0x3f); | |
| 35013 | + return 2; | |
| 35014 | + } | |
| 35015 | + if( v<0x10000 ){ | |
| 35016 | + zOut[0] = 0xe0 + (u8)((v>>12) & 0x0f); | |
| 35017 | + zOut[1] = 0x80 + (u8)((v>>6) & 0x3f); | |
| 35018 | + zOut[2] = 0x80 + (u8)(v & 0x3f); | |
| 35019 | + return 3; | |
| 35020 | + } | |
| 35021 | + zOut[0] = 0xf0 + (u8)((v>>18) & 0x07); | |
| 35022 | + zOut[1] = 0x80 + (u8)((v>>12) & 0x3f); | |
| 35023 | + zOut[2] = 0x80 + (u8)((v>>6) & 0x3f); | |
| 35024 | + zOut[3] = 0x80 + (u8)(v & 0x3f); | |
| 35025 | + return 4; | |
| 35026 | +} | |
| 34833 | 35027 | |
| 34834 | 35028 | /* |
| 34835 | 35029 | ** Translate a single UTF-8 character. Return the unicode value. |
| 34836 | 35030 | ** |
| 34837 | 35031 | ** During translation, assume that the byte that zTerm points |
| @@ -36934,11 +37128,11 @@ | ||
| 36934 | 37128 | return 0; |
| 36935 | 37129 | #endif |
| 36936 | 37130 | } |
| 36937 | 37131 | |
| 36938 | 37132 | /* |
| 36939 | -** Compute the absolute value of a 32-bit signed integer, of possible. Or | |
| 37133 | +** Compute the absolute value of a 32-bit signed integer, if possible. Or | |
| 36940 | 37134 | ** if the integer has a value of -2147483648, return +2147483647 |
| 36941 | 37135 | */ |
| 36942 | 37136 | SQLITE_PRIVATE int sqlite3AbsInt32(int x){ |
| 36943 | 37137 | if( x>=0 ) return x; |
| 36944 | 37138 | if( x==(int)0x80000000 ) return 0x7fffffff; |
| @@ -38911,10 +39105,11 @@ | ||
| 38911 | 39105 | #if SQLITE_ENABLE_LOCKING_STYLE || defined(__APPLE__) |
| 38912 | 39106 | unsigned fsFlags; /* cached details from statfs() */ |
| 38913 | 39107 | #endif |
| 38914 | 39108 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 38915 | 39109 | unsigned iBusyTimeout; /* Wait this many millisec on locks */ |
| 39110 | + int bBlockOnConnect; /* True to block for SHARED locks */ | |
| 38916 | 39111 | #endif |
| 38917 | 39112 | #if OS_VXWORKS |
| 38918 | 39113 | struct vxworksFileId *pId; /* Unique file ID */ |
| 38919 | 39114 | #endif |
| 38920 | 39115 | #ifdef SQLITE_DEBUG |
| @@ -40304,10 +40499,17 @@ | ||
| 40304 | 40499 | pInode->nLock++; |
| 40305 | 40500 | }else{ |
| 40306 | 40501 | rc = 0; |
| 40307 | 40502 | } |
| 40308 | 40503 | }else{ |
| 40504 | +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT | |
| 40505 | + if( pFile->bBlockOnConnect && pLock->l_type==F_RDLCK | |
| 40506 | + && pLock->l_start==SHARED_FIRST && pLock->l_len==SHARED_SIZE | |
| 40507 | + ){ | |
| 40508 | + rc = osFcntl(pFile->h, F_SETLKW, pLock); | |
| 40509 | + }else | |
| 40510 | +#endif | |
| 40309 | 40511 | rc = osSetPosixAdvisoryLock(pFile->h, pLock, pFile); |
| 40310 | 40512 | } |
| 40311 | 40513 | return rc; |
| 40312 | 40514 | } |
| 40313 | 40515 | |
| @@ -42665,21 +42867,27 @@ | ||
| 42665 | 42867 | return SQLITE_OK; |
| 42666 | 42868 | } |
| 42667 | 42869 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 42668 | 42870 | case SQLITE_FCNTL_LOCK_TIMEOUT: { |
| 42669 | 42871 | int iOld = pFile->iBusyTimeout; |
| 42872 | + int iNew = *(int*)pArg; | |
| 42670 | 42873 | #if SQLITE_ENABLE_SETLK_TIMEOUT==1 |
| 42671 | - pFile->iBusyTimeout = *(int*)pArg; | |
| 42874 | + pFile->iBusyTimeout = iNew<0 ? 0x7FFFFFFF : (unsigned)iNew; | |
| 42672 | 42875 | #elif SQLITE_ENABLE_SETLK_TIMEOUT==2 |
| 42673 | 42876 | pFile->iBusyTimeout = !!(*(int*)pArg); |
| 42674 | 42877 | #else |
| 42675 | 42878 | # error "SQLITE_ENABLE_SETLK_TIMEOUT must be set to 1 or 2" |
| 42676 | 42879 | #endif |
| 42677 | 42880 | *(int*)pArg = iOld; |
| 42678 | 42881 | return SQLITE_OK; |
| 42679 | 42882 | } |
| 42680 | -#endif | |
| 42883 | + case SQLITE_FCNTL_BLOCK_ON_CONNECT: { | |
| 42884 | + int iNew = *(int*)pArg; | |
| 42885 | + pFile->bBlockOnConnect = iNew; | |
| 42886 | + return SQLITE_OK; | |
| 42887 | + } | |
| 42888 | +#endif /* SQLITE_ENABLE_SETLK_TIMEOUT */ | |
| 42681 | 42889 | #if SQLITE_MAX_MMAP_SIZE>0 |
| 42682 | 42890 | case SQLITE_FCNTL_MMAP_SIZE: { |
| 42683 | 42891 | i64 newLimit = *(i64*)pArg; |
| 42684 | 42892 | int rc = SQLITE_OK; |
| 42685 | 42893 | if( newLimit>sqlite3GlobalConfig.mxMmap ){ |
| @@ -43658,11 +43866,11 @@ | ||
| 43658 | 43866 | ** occur later in the above list than the lock being obtained may be |
| 43659 | 43867 | ** held. |
| 43660 | 43868 | ** |
| 43661 | 43869 | ** It is not permitted to block on the RECOVER lock. |
| 43662 | 43870 | */ |
| 43663 | -#ifdef SQLITE_ENABLE_SETLK_TIMEOUT | |
| 43871 | +#if defined(SQLITE_ENABLE_SETLK_TIMEOUT) && defined(SQLITE_DEBUG) | |
| 43664 | 43872 | { |
| 43665 | 43873 | u16 lockMask = (p->exclMask|p->sharedMask); |
| 43666 | 43874 | assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || ( |
| 43667 | 43875 | (ofst!=2) /* not RECOVER */ |
| 43668 | 43876 | && (ofst!=1 || lockMask==0 || lockMask==2) |
| @@ -45467,11 +45675,11 @@ | ||
| 45467 | 45675 | sp.tv_sec = microseconds / 1000000; |
| 45468 | 45676 | sp.tv_nsec = (microseconds % 1000000) * 1000; |
| 45469 | 45677 | |
| 45470 | 45678 | /* Almost all modern unix systems support nanosleep(). But if you are |
| 45471 | 45679 | ** compiling for one of the rare exceptions, you can use |
| 45472 | - ** -DHAVE_NANOSLEEP=0 (perhaps in conjuction with -DHAVE_USLEEP if | |
| 45680 | + ** -DHAVE_NANOSLEEP=0 (perhaps in conjunction with -DHAVE_USLEEP if | |
| 45473 | 45681 | ** usleep() is available) in order to bypass the use of nanosleep() */ |
| 45474 | 45682 | nanosleep(&sp, NULL); |
| 45475 | 45683 | |
| 45476 | 45684 | UNUSED_PARAMETER(NotUsed); |
| 45477 | 45685 | return microseconds; |
| @@ -47188,11 +47396,21 @@ | ||
| 47188 | 47396 | HANDLE hMap; /* Handle for accessing memory mapping */ |
| 47189 | 47397 | void *pMapRegion; /* Area memory mapped */ |
| 47190 | 47398 | sqlite3_int64 mmapSize; /* Size of mapped region */ |
| 47191 | 47399 | sqlite3_int64 mmapSizeMax; /* Configured FCNTL_MMAP_SIZE value */ |
| 47192 | 47400 | #endif |
| 47401 | +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT | |
| 47402 | + DWORD iBusyTimeout; /* Wait this many millisec on locks */ | |
| 47403 | + int bBlockOnConnect; | |
| 47404 | +#endif | |
| 47193 | 47405 | }; |
| 47406 | + | |
| 47407 | +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT | |
| 47408 | +# define winFileBusyTimeout(pDbFd) pDbFd->iBusyTimeout | |
| 47409 | +#else | |
| 47410 | +# define winFileBusyTimeout(pDbFd) 0 | |
| 47411 | +#endif | |
| 47194 | 47412 | |
| 47195 | 47413 | /* |
| 47196 | 47414 | ** The winVfsAppData structure is used for the pAppData member for all of the |
| 47197 | 47415 | ** Win32 VFS variants. |
| 47198 | 47416 | */ |
| @@ -47623,10 +47841,16 @@ | ||
| 47623 | 47841 | #endif |
| 47624 | 47842 | |
| 47625 | 47843 | #define osGetFullPathNameW ((DWORD(WINAPI*)(LPCWSTR,DWORD,LPWSTR, \ |
| 47626 | 47844 | LPWSTR*))aSyscall[25].pCurrent) |
| 47627 | 47845 | |
| 47846 | +/* | |
| 47847 | +** For GetLastError(), MSDN says: | |
| 47848 | +** | |
| 47849 | +** Minimum supported client: Windows XP [desktop apps | UWP apps] | |
| 47850 | +** Minimum supported server: Windows Server 2003 [desktop apps | UWP apps] | |
| 47851 | +*/ | |
| 47628 | 47852 | { "GetLastError", (SYSCALL)GetLastError, 0 }, |
| 47629 | 47853 | |
| 47630 | 47854 | #define osGetLastError ((DWORD(WINAPI*)(VOID))aSyscall[26].pCurrent) |
| 47631 | 47855 | |
| 47632 | 47856 | #if !defined(SQLITE_OMIT_LOAD_EXTENSION) |
| @@ -47905,15 +48129,17 @@ | ||
| 47905 | 48129 | #endif |
| 47906 | 48130 | |
| 47907 | 48131 | #define osCreateEventExW ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,LPCWSTR, \ |
| 47908 | 48132 | DWORD,DWORD))aSyscall[62].pCurrent) |
| 47909 | 48133 | |
| 47910 | -#if !SQLITE_OS_WINRT | |
| 48134 | +/* | |
| 48135 | +** For WaitForSingleObject(), MSDN says: | |
| 48136 | +** | |
| 48137 | +** Minimum supported client: Windows XP [desktop apps | UWP apps] | |
| 48138 | +** Minimum supported server: Windows Server 2003 [desktop apps | UWP apps] | |
| 48139 | +*/ | |
| 47911 | 48140 | { "WaitForSingleObject", (SYSCALL)WaitForSingleObject, 0 }, |
| 47912 | -#else | |
| 47913 | - { "WaitForSingleObject", (SYSCALL)0, 0 }, | |
| 47914 | -#endif | |
| 47915 | 48141 | |
| 47916 | 48142 | #define osWaitForSingleObject ((DWORD(WINAPI*)(HANDLE, \ |
| 47917 | 48143 | DWORD))aSyscall[63].pCurrent) |
| 47918 | 48144 | |
| 47919 | 48145 | #if !SQLITE_OS_WINCE |
| @@ -48056,10 +48282,44 @@ | ||
| 48056 | 48282 | #endif |
| 48057 | 48283 | |
| 48058 | 48284 | #define osFlushViewOfFile \ |
| 48059 | 48285 | ((BOOL(WINAPI*)(LPCVOID,SIZE_T))aSyscall[79].pCurrent) |
| 48060 | 48286 | |
| 48287 | +/* | |
| 48288 | +** If SQLITE_ENABLE_SETLK_TIMEOUT is defined, we require CreateEvent() | |
| 48289 | +** to implement blocking locks with timeouts. MSDN says: | |
| 48290 | +** | |
| 48291 | +** Minimum supported client: Windows XP [desktop apps | UWP apps] | |
| 48292 | +** Minimum supported server: Windows Server 2003 [desktop apps | UWP apps] | |
| 48293 | +*/ | |
| 48294 | +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT | |
| 48295 | + { "CreateEvent", (SYSCALL)CreateEvent, 0 }, | |
| 48296 | +#else | |
| 48297 | + { "CreateEvent", (SYSCALL)0, 0 }, | |
| 48298 | +#endif | |
| 48299 | + | |
| 48300 | +#define osCreateEvent ( \ | |
| 48301 | + (HANDLE(WINAPI*) (LPSECURITY_ATTRIBUTES,BOOL,BOOL,LPCSTR)) \ | |
| 48302 | + aSyscall[80].pCurrent \ | |
| 48303 | +) | |
| 48304 | + | |
| 48305 | +/* | |
| 48306 | +** If SQLITE_ENABLE_SETLK_TIMEOUT is defined, we require CancelIo() | |
| 48307 | +** for the case where a timeout expires and a lock request must be | |
| 48308 | +** cancelled. | |
| 48309 | +** | |
| 48310 | +** Minimum supported client: Windows XP [desktop apps | UWP apps] | |
| 48311 | +** Minimum supported server: Windows Server 2003 [desktop apps | UWP apps] | |
| 48312 | +*/ | |
| 48313 | +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT | |
| 48314 | + { "CancelIo", (SYSCALL)CancelIo, 0 }, | |
| 48315 | +#else | |
| 48316 | + { "CancelIo", (SYSCALL)0, 0 }, | |
| 48317 | +#endif | |
| 48318 | + | |
| 48319 | +#define osCancelIo ((BOOL(WINAPI*)(HANDLE))aSyscall[81].pCurrent) | |
| 48320 | + | |
| 48061 | 48321 | }; /* End of the overrideable system calls */ |
| 48062 | 48322 | |
| 48063 | 48323 | /* |
| 48064 | 48324 | ** This is the xSetSystemCall() method of sqlite3_vfs for all of the |
| 48065 | 48325 | ** "win32" VFSes. Return SQLITE_OK upon successfully updating the |
| @@ -48354,11 +48614,13 @@ | ||
| 48354 | 48614 | (sInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) ? 2 : 1, 0); |
| 48355 | 48615 | #endif |
| 48356 | 48616 | } |
| 48357 | 48617 | return osInterlockedCompareExchange(&sqlite3_os_type, 2, 2)==2; |
| 48358 | 48618 | #elif SQLITE_TEST |
| 48359 | - return osInterlockedCompareExchange(&sqlite3_os_type, 2, 2)==2; | |
| 48619 | + return osInterlockedCompareExchange(&sqlite3_os_type, 2, 2)==2 | |
| 48620 | + || osInterlockedCompareExchange(&sqlite3_os_type, 0, 0)==0 | |
| 48621 | + ; | |
| 48360 | 48622 | #else |
| 48361 | 48623 | /* |
| 48362 | 48624 | ** NOTE: All sub-platforms where the GetVersionEx[AW] functions are |
| 48363 | 48625 | ** deprecated are always assumed to be based on the NT kernel. |
| 48364 | 48626 | */ |
| @@ -49440,10 +49702,89 @@ | ||
| 49440 | 49702 | return osLockFile(*phFile, offsetLow, offsetHigh, numBytesLow, |
| 49441 | 49703 | numBytesHigh); |
| 49442 | 49704 | } |
| 49443 | 49705 | #endif |
| 49444 | 49706 | } |
| 49707 | + | |
| 49708 | +/* | |
| 49709 | +** Lock a region of nByte bytes starting at offset offset of file hFile. | |
| 49710 | +** Take an EXCLUSIVE lock if parameter bExclusive is true, or a SHARED lock | |
| 49711 | +** otherwise. If nMs is greater than zero and the lock cannot be obtained | |
| 49712 | +** immediately, block for that many ms before giving up. | |
| 49713 | +** | |
| 49714 | +** This function returns SQLITE_OK if the lock is obtained successfully. If | |
| 49715 | +** some other process holds the lock, SQLITE_BUSY is returned if nMs==0, or | |
| 49716 | +** SQLITE_BUSY_TIMEOUT otherwise. Or, if an error occurs, SQLITE_IOERR. | |
| 49717 | +*/ | |
| 49718 | +static int winHandleLockTimeout( | |
| 49719 | + HANDLE hFile, | |
| 49720 | + DWORD offset, | |
| 49721 | + DWORD nByte, | |
| 49722 | + int bExcl, | |
| 49723 | + DWORD nMs | |
| 49724 | +){ | |
| 49725 | + DWORD flags = LOCKFILE_FAIL_IMMEDIATELY | (bExcl?LOCKFILE_EXCLUSIVE_LOCK:0); | |
| 49726 | + int rc = SQLITE_OK; | |
| 49727 | + BOOL ret; | |
| 49728 | + | |
| 49729 | + if( !osIsNT() ){ | |
| 49730 | + ret = winLockFile(&hFile, flags, offset, 0, nByte, 0); | |
| 49731 | + }else{ | |
| 49732 | + OVERLAPPED ovlp; | |
| 49733 | + memset(&ovlp, 0, sizeof(OVERLAPPED)); | |
| 49734 | + ovlp.Offset = offset; | |
| 49735 | + | |
| 49736 | +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT | |
| 49737 | + if( nMs!=0 ){ | |
| 49738 | + flags &= ~LOCKFILE_FAIL_IMMEDIATELY; | |
| 49739 | + } | |
| 49740 | + ovlp.hEvent = osCreateEvent(NULL, TRUE, FALSE, NULL); | |
| 49741 | + if( ovlp.hEvent==NULL ){ | |
| 49742 | + return SQLITE_IOERR_LOCK; | |
| 49743 | + } | |
| 49744 | +#endif | |
| 49745 | + | |
| 49746 | + ret = osLockFileEx(hFile, flags, 0, nByte, 0, &ovlp); | |
| 49747 | + | |
| 49748 | +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT | |
| 49749 | + /* If SQLITE_ENABLE_SETLK_TIMEOUT is defined, then the file-handle was | |
| 49750 | + ** opened with FILE_FLAG_OVERHEAD specified. In this case, the call to | |
| 49751 | + ** LockFileEx() may fail because the request is still pending. This can | |
| 49752 | + ** happen even if LOCKFILE_FAIL_IMMEDIATELY was specified. | |
| 49753 | + ** | |
| 49754 | + ** If nMs is 0, then LOCKFILE_FAIL_IMMEDIATELY was set in the flags | |
| 49755 | + ** passed to LockFileEx(). In this case, if the operation is pending, | |
| 49756 | + ** block indefinitely until it is finished. | |
| 49757 | + ** | |
| 49758 | + ** Otherwise, wait for up to nMs ms for the operation to finish. nMs | |
| 49759 | + ** may be set to INFINITE. | |
| 49760 | + */ | |
| 49761 | + if( !ret && GetLastError()==ERROR_IO_PENDING ){ | |
| 49762 | + DWORD nDelay = (nMs==0 ? INFINITE : nMs); | |
| 49763 | + DWORD res = osWaitForSingleObject(ovlp.hEvent, nDelay); | |
| 49764 | + if( res==WAIT_OBJECT_0 ){ | |
| 49765 | + ret = TRUE; | |
| 49766 | + }else if( res==WAIT_TIMEOUT ){ | |
| 49767 | + rc = SQLITE_BUSY_TIMEOUT; | |
| 49768 | + }else{ | |
| 49769 | + /* Some other error has occurred */ | |
| 49770 | + rc = SQLITE_IOERR_LOCK; | |
| 49771 | + } | |
| 49772 | + | |
| 49773 | + /* If it is still pending, cancel the LockFileEx() call. */ | |
| 49774 | + osCancelIo(hFile); | |
| 49775 | + } | |
| 49776 | + | |
| 49777 | + osCloseHandle(ovlp.hEvent); | |
| 49778 | +#endif | |
| 49779 | + } | |
| 49780 | + | |
| 49781 | + if( rc==SQLITE_OK && !ret ){ | |
| 49782 | + rc = SQLITE_BUSY; | |
| 49783 | + } | |
| 49784 | + return rc; | |
| 49785 | +} | |
| 49445 | 49786 | |
| 49446 | 49787 | /* |
| 49447 | 49788 | ** Unlock a file region. |
| 49448 | 49789 | */ |
| 49449 | 49790 | static BOOL winUnlockFile( |
| @@ -49471,10 +49812,18 @@ | ||
| 49471 | 49812 | return osUnlockFile(*phFile, offsetLow, offsetHigh, numBytesLow, |
| 49472 | 49813 | numBytesHigh); |
| 49473 | 49814 | } |
| 49474 | 49815 | #endif |
| 49475 | 49816 | } |
| 49817 | + | |
| 49818 | +/* | |
| 49819 | +** Remove an nByte lock starting at offset iOff from HANDLE h. | |
| 49820 | +*/ | |
| 49821 | +static int winHandleUnlock(HANDLE h, int iOff, int nByte){ | |
| 49822 | + BOOL ret = winUnlockFile(&h, iOff, 0, nByte, 0); | |
| 49823 | + return (ret ? SQLITE_OK : SQLITE_IOERR_UNLOCK); | |
| 49824 | +} | |
| 49476 | 49825 | |
| 49477 | 49826 | /***************************************************************************** |
| 49478 | 49827 | ** The next group of routines implement the I/O methods specified |
| 49479 | 49828 | ** by the sqlite3_io_methods object. |
| 49480 | 49829 | ******************************************************************************/ |
| @@ -49485,69 +49834,73 @@ | ||
| 49485 | 49834 | #ifndef INVALID_SET_FILE_POINTER |
| 49486 | 49835 | # define INVALID_SET_FILE_POINTER ((DWORD)-1) |
| 49487 | 49836 | #endif |
| 49488 | 49837 | |
| 49489 | 49838 | /* |
| 49490 | -** Move the current position of the file handle passed as the first | |
| 49491 | -** argument to offset iOffset within the file. If successful, return 0. | |
| 49492 | -** Otherwise, set pFile->lastErrno and return non-zero. | |
| 49839 | +** Seek the file handle h to offset nByte of the file. | |
| 49840 | +** | |
| 49841 | +** If successful, return SQLITE_OK. Or, if an error occurs, return an SQLite | |
| 49842 | +** error code. | |
| 49493 | 49843 | */ |
| 49494 | -static int winSeekFile(winFile *pFile, sqlite3_int64 iOffset){ | |
| 49844 | +static int winHandleSeek(HANDLE h, sqlite3_int64 iOffset){ | |
| 49845 | + int rc = SQLITE_OK; /* Return value */ | |
| 49846 | + | |
| 49495 | 49847 | #if !SQLITE_OS_WINRT |
| 49496 | 49848 | LONG upperBits; /* Most sig. 32 bits of new offset */ |
| 49497 | 49849 | LONG lowerBits; /* Least sig. 32 bits of new offset */ |
| 49498 | 49850 | DWORD dwRet; /* Value returned by SetFilePointer() */ |
| 49499 | - DWORD lastErrno; /* Value returned by GetLastError() */ | |
| 49500 | - | |
| 49501 | - OSTRACE(("SEEK file=%p, offset=%lld\n", pFile->h, iOffset)); | |
| 49502 | 49851 | |
| 49503 | 49852 | upperBits = (LONG)((iOffset>>32) & 0x7fffffff); |
| 49504 | 49853 | lowerBits = (LONG)(iOffset & 0xffffffff); |
| 49854 | + | |
| 49855 | + dwRet = osSetFilePointer(h, lowerBits, &upperBits, FILE_BEGIN); | |
| 49505 | 49856 | |
| 49506 | 49857 | /* API oddity: If successful, SetFilePointer() returns a dword |
| 49507 | 49858 | ** containing the lower 32-bits of the new file-offset. Or, if it fails, |
| 49508 | 49859 | ** it returns INVALID_SET_FILE_POINTER. However according to MSDN, |
| 49509 | 49860 | ** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine |
| 49510 | 49861 | ** whether an error has actually occurred, it is also necessary to call |
| 49511 | - ** GetLastError(). | |
| 49512 | - */ | |
| 49513 | - dwRet = osSetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN); | |
| 49514 | - | |
| 49515 | - if( (dwRet==INVALID_SET_FILE_POINTER | |
| 49516 | - && ((lastErrno = osGetLastError())!=NO_ERROR)) ){ | |
| 49517 | - pFile->lastErrno = lastErrno; | |
| 49518 | - winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno, | |
| 49519 | - "winSeekFile", pFile->zPath); | |
| 49520 | - OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h)); | |
| 49521 | - return 1; | |
| 49522 | - } | |
| 49523 | - | |
| 49524 | - OSTRACE(("SEEK file=%p, rc=SQLITE_OK\n", pFile->h)); | |
| 49525 | - return 0; | |
| 49526 | -#else | |
| 49527 | - /* | |
| 49528 | - ** Same as above, except that this implementation works for WinRT. | |
| 49529 | - */ | |
| 49530 | - | |
| 49862 | + ** GetLastError(). */ | |
| 49863 | + if( dwRet==INVALID_SET_FILE_POINTER ){ | |
| 49864 | + DWORD lastErrno = osGetLastError(); | |
| 49865 | + if( lastErrno!=NO_ERROR ){ | |
| 49866 | + rc = SQLITE_IOERR_SEEK; | |
| 49867 | + } | |
| 49868 | + } | |
| 49869 | +#else | |
| 49870 | + /* This implementation works for WinRT. */ | |
| 49531 | 49871 | LARGE_INTEGER x; /* The new offset */ |
| 49532 | 49872 | BOOL bRet; /* Value returned by SetFilePointerEx() */ |
| 49533 | 49873 | |
| 49534 | 49874 | x.QuadPart = iOffset; |
| 49535 | - bRet = osSetFilePointerEx(pFile->h, x, 0, FILE_BEGIN); | |
| 49875 | + bRet = osSetFilePointerEx(h, x, 0, FILE_BEGIN); | |
| 49536 | 49876 | |
| 49537 | 49877 | if(!bRet){ |
| 49878 | + rc = SQLITE_IOERR_SEEK; | |
| 49879 | + } | |
| 49880 | +#endif | |
| 49881 | + | |
| 49882 | + OSTRACE(("SEEK file=%p, offset=%lld rc=%s\n", h, iOffset, sqlite3ErrName(rc))); | |
| 49883 | + return rc; | |
| 49884 | +} | |
| 49885 | + | |
| 49886 | +/* | |
| 49887 | +** Move the current position of the file handle passed as the first | |
| 49888 | +** argument to offset iOffset within the file. If successful, return 0. | |
| 49889 | +** Otherwise, set pFile->lastErrno and return non-zero. | |
| 49890 | +*/ | |
| 49891 | +static int winSeekFile(winFile *pFile, sqlite3_int64 iOffset){ | |
| 49892 | + int rc; | |
| 49893 | + | |
| 49894 | + rc = winHandleSeek(pFile->h, iOffset); | |
| 49895 | + if( rc!=SQLITE_OK ){ | |
| 49538 | 49896 | pFile->lastErrno = osGetLastError(); |
| 49539 | - winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno, | |
| 49540 | - "winSeekFile", pFile->zPath); | |
| 49541 | - OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h)); | |
| 49542 | - return 1; | |
| 49543 | - } | |
| 49544 | - | |
| 49545 | - OSTRACE(("SEEK file=%p, rc=SQLITE_OK\n", pFile->h)); | |
| 49546 | - return 0; | |
| 49547 | -#endif | |
| 49548 | -} | |
| 49897 | + winLogError(rc, pFile->lastErrno, "winSeekFile", pFile->zPath); | |
| 49898 | + } | |
| 49899 | + return rc; | |
| 49900 | +} | |
| 49901 | + | |
| 49549 | 49902 | |
| 49550 | 49903 | #if SQLITE_MAX_MMAP_SIZE>0 |
| 49551 | 49904 | /* Forward references to VFS helper methods used for memory mapped files */ |
| 49552 | 49905 | static int winMapfile(winFile*, sqlite3_int64); |
| 49553 | 49906 | static int winUnmapfile(winFile*); |
| @@ -49803,10 +50156,64 @@ | ||
| 49803 | 50156 | } |
| 49804 | 50157 | OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n", |
| 49805 | 50158 | osGetCurrentProcessId(), pFile, pFile->h)); |
| 49806 | 50159 | return SQLITE_OK; |
| 49807 | 50160 | } |
| 50161 | + | |
| 50162 | +/* | |
| 50163 | +** Truncate the file opened by handle h to nByte bytes in size. | |
| 50164 | +*/ | |
| 50165 | +static int winHandleTruncate(HANDLE h, sqlite3_int64 nByte){ | |
| 50166 | + int rc = SQLITE_OK; /* Return code */ | |
| 50167 | + rc = winHandleSeek(h, nByte); | |
| 50168 | + if( rc==SQLITE_OK ){ | |
| 50169 | + if( 0==osSetEndOfFile(h) ){ | |
| 50170 | + rc = SQLITE_IOERR_TRUNCATE; | |
| 50171 | + } | |
| 50172 | + } | |
| 50173 | + return rc; | |
| 50174 | +} | |
| 50175 | + | |
| 50176 | +/* | |
| 50177 | +** Determine the size in bytes of the file opened by the handle passed as | |
| 50178 | +** the first argument. | |
| 50179 | +*/ | |
| 50180 | +static int winHandleSize(HANDLE h, sqlite3_int64 *pnByte){ | |
| 50181 | + int rc = SQLITE_OK; | |
| 50182 | + | |
| 50183 | +#if SQLITE_OS_WINRT | |
| 50184 | + FILE_STANDARD_INFO info; | |
| 50185 | + BOOL b; | |
| 50186 | + b = osGetFileInformationByHandleEx(h, FileStandardInfo, &info, sizeof(info)); | |
| 50187 | + if( b ){ | |
| 50188 | + *pnByte = info.EndOfFile.QuadPart; | |
| 50189 | + }else{ | |
| 50190 | + rc = SQLITE_IOERR_FSTAT; | |
| 50191 | + } | |
| 50192 | +#else | |
| 50193 | + DWORD upperBits = 0; | |
| 50194 | + DWORD lowerBits = 0; | |
| 50195 | + | |
| 50196 | + assert( pnByte ); | |
| 50197 | + lowerBits = osGetFileSize(h, &upperBits); | |
| 50198 | + *pnByte = (((sqlite3_int64)upperBits)<<32) + lowerBits; | |
| 50199 | + if( lowerBits==INVALID_FILE_SIZE && osGetLastError()!=NO_ERROR ){ | |
| 50200 | + rc = SQLITE_IOERR_FSTAT; | |
| 50201 | + } | |
| 50202 | +#endif | |
| 50203 | + | |
| 50204 | + return rc; | |
| 50205 | +} | |
| 50206 | + | |
| 50207 | +/* | |
| 50208 | +** Close the handle passed as the only argument. | |
| 50209 | +*/ | |
| 50210 | +static void winHandleClose(HANDLE h){ | |
| 50211 | + if( h!=INVALID_HANDLE_VALUE ){ | |
| 50212 | + osCloseHandle(h); | |
| 50213 | + } | |
| 50214 | +} | |
| 49808 | 50215 | |
| 49809 | 50216 | /* |
| 49810 | 50217 | ** Truncate an open file to a specified size |
| 49811 | 50218 | */ |
| 49812 | 50219 | static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){ |
| @@ -50059,31 +50466,32 @@ | ||
| 50059 | 50466 | /* |
| 50060 | 50467 | ** Acquire a reader lock. |
| 50061 | 50468 | ** Different API routines are called depending on whether or not this |
| 50062 | 50469 | ** is Win9x or WinNT. |
| 50063 | 50470 | */ |
| 50064 | -static int winGetReadLock(winFile *pFile){ | |
| 50471 | +static int winGetReadLock(winFile *pFile, int bBlock){ | |
| 50065 | 50472 | int res; |
| 50473 | + DWORD mask = ~(bBlock ? LOCKFILE_FAIL_IMMEDIATELY : 0); | |
| 50066 | 50474 | OSTRACE(("READ-LOCK file=%p, lock=%d\n", pFile->h, pFile->locktype)); |
| 50067 | 50475 | if( osIsNT() ){ |
| 50068 | 50476 | #if SQLITE_OS_WINCE |
| 50069 | 50477 | /* |
| 50070 | 50478 | ** NOTE: Windows CE is handled differently here due its lack of the Win32 |
| 50071 | 50479 | ** API LockFileEx. |
| 50072 | 50480 | */ |
| 50073 | 50481 | res = winceLockFile(&pFile->h, SHARED_FIRST, 0, 1, 0); |
| 50074 | 50482 | #else |
| 50075 | - res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS, SHARED_FIRST, 0, | |
| 50483 | + res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS&mask, SHARED_FIRST, 0, | |
| 50076 | 50484 | SHARED_SIZE, 0); |
| 50077 | 50485 | #endif |
| 50078 | 50486 | } |
| 50079 | 50487 | #ifdef SQLITE_WIN32_HAS_ANSI |
| 50080 | 50488 | else{ |
| 50081 | 50489 | int lk; |
| 50082 | 50490 | sqlite3_randomness(sizeof(lk), &lk); |
| 50083 | 50491 | pFile->sharedLockByte = (short)((lk & 0x7fffffff)%(SHARED_SIZE - 1)); |
| 50084 | - res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, | |
| 50492 | + res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS&mask, | |
| 50085 | 50493 | SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0); |
| 50086 | 50494 | } |
| 50087 | 50495 | #endif |
| 50088 | 50496 | if( res == 0 ){ |
| 50089 | 50497 | pFile->lastErrno = osGetLastError(); |
| @@ -50174,50 +50582,66 @@ | ||
| 50174 | 50582 | */ |
| 50175 | 50583 | assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK ); |
| 50176 | 50584 | assert( locktype!=PENDING_LOCK ); |
| 50177 | 50585 | assert( locktype!=RESERVED_LOCK || pFile->locktype==SHARED_LOCK ); |
| 50178 | 50586 | |
| 50179 | - /* Lock the PENDING_LOCK byte if we need to acquire a PENDING lock or | |
| 50587 | + /* Lock the PENDING_LOCK byte if we need to acquire an EXCLUSIVE lock or | |
| 50180 | 50588 | ** a SHARED lock. If we are acquiring a SHARED lock, the acquisition of |
| 50181 | 50589 | ** the PENDING_LOCK byte is temporary. |
| 50182 | 50590 | */ |
| 50183 | 50591 | newLocktype = pFile->locktype; |
| 50184 | - if( pFile->locktype==NO_LOCK | |
| 50185 | - || (locktype==EXCLUSIVE_LOCK && pFile->locktype<=RESERVED_LOCK) | |
| 50592 | + if( locktype==SHARED_LOCK | |
| 50593 | + || (locktype==EXCLUSIVE_LOCK && pFile->locktype==RESERVED_LOCK) | |
| 50186 | 50594 | ){ |
| 50187 | 50595 | int cnt = 3; |
| 50188 | - while( cnt-->0 && (res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, | |
| 50189 | - PENDING_BYTE, 0, 1, 0))==0 ){ | |
| 50596 | + | |
| 50597 | + /* Flags for the LockFileEx() call. This should be an exclusive lock if | |
| 50598 | + ** this call is to obtain EXCLUSIVE, or a shared lock if this call is to | |
| 50599 | + ** obtain SHARED. */ | |
| 50600 | + int flags = LOCKFILE_FAIL_IMMEDIATELY; | |
| 50601 | + if( locktype==EXCLUSIVE_LOCK ){ | |
| 50602 | + flags |= LOCKFILE_EXCLUSIVE_LOCK; | |
| 50603 | + } | |
| 50604 | + while( cnt>0 ){ | |
| 50190 | 50605 | /* Try 3 times to get the pending lock. This is needed to work |
| 50191 | 50606 | ** around problems caused by indexing and/or anti-virus software on |
| 50192 | 50607 | ** Windows systems. |
| 50608 | + ** | |
| 50193 | 50609 | ** If you are using this code as a model for alternative VFSes, do not |
| 50194 | - ** copy this retry logic. It is a hack intended for Windows only. | |
| 50195 | - */ | |
| 50610 | + ** copy this retry logic. It is a hack intended for Windows only. */ | |
| 50611 | + res = winLockFile(&pFile->h, flags, PENDING_BYTE, 0, 1, 0); | |
| 50612 | + if( res ) break; | |
| 50613 | + | |
| 50196 | 50614 | lastErrno = osGetLastError(); |
| 50197 | 50615 | OSTRACE(("LOCK-PENDING-FAIL file=%p, count=%d, result=%d\n", |
| 50198 | - pFile->h, cnt, res)); | |
| 50616 | + pFile->h, cnt, res | |
| 50617 | + )); | |
| 50618 | + | |
| 50199 | 50619 | if( lastErrno==ERROR_INVALID_HANDLE ){ |
| 50200 | 50620 | pFile->lastErrno = lastErrno; |
| 50201 | 50621 | rc = SQLITE_IOERR_LOCK; |
| 50202 | 50622 | OSTRACE(("LOCK-FAIL file=%p, count=%d, rc=%s\n", |
| 50203 | - pFile->h, cnt, sqlite3ErrName(rc))); | |
| 50623 | + pFile->h, cnt, sqlite3ErrName(rc) | |
| 50624 | + )); | |
| 50204 | 50625 | return rc; |
| 50205 | 50626 | } |
| 50206 | - if( cnt ) sqlite3_win32_sleep(1); | |
| 50627 | + | |
| 50628 | + cnt--; | |
| 50629 | + if( cnt>0 ) sqlite3_win32_sleep(1); | |
| 50207 | 50630 | } |
| 50208 | 50631 | gotPendingLock = res; |
| 50209 | - if( !res ){ | |
| 50210 | - lastErrno = osGetLastError(); | |
| 50211 | - } | |
| 50212 | 50632 | } |
| 50213 | 50633 | |
| 50214 | 50634 | /* Acquire a shared lock |
| 50215 | 50635 | */ |
| 50216 | 50636 | if( locktype==SHARED_LOCK && res ){ |
| 50217 | 50637 | assert( pFile->locktype==NO_LOCK ); |
| 50218 | - res = winGetReadLock(pFile); | |
| 50638 | +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT | |
| 50639 | + res = winGetReadLock(pFile, pFile->bBlockOnConnect); | |
| 50640 | +#else | |
| 50641 | + res = winGetReadLock(pFile, 0); | |
| 50642 | +#endif | |
| 50219 | 50643 | if( res ){ |
| 50220 | 50644 | newLocktype = SHARED_LOCK; |
| 50221 | 50645 | }else{ |
| 50222 | 50646 | lastErrno = osGetLastError(); |
| 50223 | 50647 | } |
| @@ -50251,11 +50675,11 @@ | ||
| 50251 | 50675 | SHARED_SIZE, 0); |
| 50252 | 50676 | if( res ){ |
| 50253 | 50677 | newLocktype = EXCLUSIVE_LOCK; |
| 50254 | 50678 | }else{ |
| 50255 | 50679 | lastErrno = osGetLastError(); |
| 50256 | - winGetReadLock(pFile); | |
| 50680 | + winGetReadLock(pFile, 0); | |
| 50257 | 50681 | } |
| 50258 | 50682 | } |
| 50259 | 50683 | |
| 50260 | 50684 | /* If we are holding a PENDING lock that ought to be released, then |
| 50261 | 50685 | ** release it now. |
| @@ -50331,11 +50755,11 @@ | ||
| 50331 | 50755 | OSTRACE(("UNLOCK file=%p, oldLock=%d(%d), newLock=%d\n", |
| 50332 | 50756 | pFile->h, pFile->locktype, pFile->sharedLockByte, locktype)); |
| 50333 | 50757 | type = pFile->locktype; |
| 50334 | 50758 | if( type>=EXCLUSIVE_LOCK ){ |
| 50335 | 50759 | winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); |
| 50336 | - if( locktype==SHARED_LOCK && !winGetReadLock(pFile) ){ | |
| 50760 | + if( locktype==SHARED_LOCK && !winGetReadLock(pFile, 0) ){ | |
| 50337 | 50761 | /* This should never happen. We should always be able to |
| 50338 | 50762 | ** reacquire the read lock */ |
| 50339 | 50763 | rc = winLogError(SQLITE_IOERR_UNLOCK, osGetLastError(), |
| 50340 | 50764 | "winUnlock", pFile->zPath); |
| 50341 | 50765 | } |
| @@ -50541,10 +50965,32 @@ | ||
| 50541 | 50965 | } |
| 50542 | 50966 | OSTRACE(("FCNTL file=%p, rc=%s\n", pFile->h, sqlite3ErrName(rc))); |
| 50543 | 50967 | return rc; |
| 50544 | 50968 | } |
| 50545 | 50969 | #endif |
| 50970 | + | |
| 50971 | +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT | |
| 50972 | + case SQLITE_FCNTL_LOCK_TIMEOUT: { | |
| 50973 | + int iOld = pFile->iBusyTimeout; | |
| 50974 | + int iNew = *(int*)pArg; | |
| 50975 | +#if SQLITE_ENABLE_SETLK_TIMEOUT==1 | |
| 50976 | + pFile->iBusyTimeout = (iNew < 0) ? INFINITE : (DWORD)iNew; | |
| 50977 | +#elif SQLITE_ENABLE_SETLK_TIMEOUT==2 | |
| 50978 | + pFile->iBusyTimeout = (DWORD)(!!iNew); | |
| 50979 | +#else | |
| 50980 | +# error "SQLITE_ENABLE_SETLK_TIMEOUT must be set to 1 or 2" | |
| 50981 | +#endif | |
| 50982 | + *(int*)pArg = iOld; | |
| 50983 | + return SQLITE_OK; | |
| 50984 | + } | |
| 50985 | + case SQLITE_FCNTL_BLOCK_ON_CONNECT: { | |
| 50986 | + int iNew = *(int*)pArg; | |
| 50987 | + pFile->bBlockOnConnect = iNew; | |
| 50988 | + return SQLITE_OK; | |
| 50989 | + } | |
| 50990 | +#endif /* SQLITE_ENABLE_SETLK_TIMEOUT */ | |
| 50991 | + | |
| 50546 | 50992 | } |
| 50547 | 50993 | OSTRACE(("FCNTL file=%p, rc=SQLITE_NOTFOUND\n", pFile->h)); |
| 50548 | 50994 | return SQLITE_NOTFOUND; |
| 50549 | 50995 | } |
| 50550 | 50996 | |
| @@ -50621,36 +51067,39 @@ | ||
| 50621 | 51067 | ** nRef |
| 50622 | 51068 | ** pNext |
| 50623 | 51069 | ** |
| 50624 | 51070 | ** The following fields are read-only after the object is created: |
| 50625 | 51071 | ** |
| 50626 | -** fid | |
| 50627 | 51072 | ** zFilename |
| 50628 | 51073 | ** |
| 50629 | 51074 | ** Either winShmNode.mutex must be held or winShmNode.nRef==0 and |
| 50630 | 51075 | ** winShmMutexHeld() is true when reading or writing any other field |
| 50631 | 51076 | ** in this structure. |
| 50632 | 51077 | ** |
| 51078 | +** File-handle hSharedShm is used to (a) take the DMS lock, (b) truncate | |
| 51079 | +** the *-shm file if the DMS-locking protocol demands it, and (c) map | |
| 51080 | +** regions of the *-shm file into memory using MapViewOfFile() or | |
| 51081 | +** similar. Other locks are taken by individual clients using the | |
| 51082 | +** winShm.hShm handles. | |
| 50633 | 51083 | */ |
| 50634 | 51084 | struct winShmNode { |
| 50635 | 51085 | sqlite3_mutex *mutex; /* Mutex to access this object */ |
| 50636 | 51086 | char *zFilename; /* Name of the file */ |
| 50637 | - winFile hFile; /* File handle from winOpen */ | |
| 51087 | + HANDLE hSharedShm; /* File handle open on zFilename */ | |
| 50638 | 51088 | |
| 51089 | + int isUnlocked; /* DMS lock has not yet been obtained */ | |
| 51090 | + int isReadonly; /* True if read-only */ | |
| 50639 | 51091 | int szRegion; /* Size of shared-memory regions */ |
| 50640 | 51092 | int nRegion; /* Size of array apRegion */ |
| 50641 | - u8 isReadonly; /* True if read-only */ | |
| 50642 | - u8 isUnlocked; /* True if no DMS lock held */ | |
| 50643 | 51093 | |
| 50644 | 51094 | struct ShmRegion { |
| 50645 | 51095 | HANDLE hMap; /* File handle from CreateFileMapping */ |
| 50646 | 51096 | void *pMap; |
| 50647 | 51097 | } *aRegion; |
| 50648 | 51098 | DWORD lastErrno; /* The Windows errno from the last I/O error */ |
| 50649 | 51099 | |
| 50650 | 51100 | int nRef; /* Number of winShm objects pointing to this */ |
| 50651 | - winShm *pFirst; /* All winShm objects pointing to this */ | |
| 50652 | 51101 | winShmNode *pNext; /* Next in list of all winShmNode objects */ |
| 50653 | 51102 | #if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) |
| 50654 | 51103 | u8 nextShmId; /* Next available winShm.id value */ |
| 50655 | 51104 | #endif |
| 50656 | 51105 | }; |
| @@ -50662,27 +51111,19 @@ | ||
| 50662 | 51111 | */ |
| 50663 | 51112 | static winShmNode *winShmNodeList = 0; |
| 50664 | 51113 | |
| 50665 | 51114 | /* |
| 50666 | 51115 | ** Structure used internally by this VFS to record the state of an |
| 50667 | -** open shared memory connection. | |
| 50668 | -** | |
| 50669 | -** The following fields are initialized when this object is created and | |
| 50670 | -** are read-only thereafter: | |
| 50671 | -** | |
| 50672 | -** winShm.pShmNode | |
| 50673 | -** winShm.id | |
| 50674 | -** | |
| 50675 | -** All other fields are read/write. The winShm.pShmNode->mutex must be held | |
| 50676 | -** while accessing any read/write fields. | |
| 51116 | +** open shared memory connection. There is one such structure for each | |
| 51117 | +** winFile open on a wal mode database. | |
| 50677 | 51118 | */ |
| 50678 | 51119 | struct winShm { |
| 50679 | 51120 | winShmNode *pShmNode; /* The underlying winShmNode object */ |
| 50680 | - winShm *pNext; /* Next winShm with the same winShmNode */ | |
| 50681 | - u8 hasMutex; /* True if holding the winShmNode mutex */ | |
| 50682 | 51121 | u16 sharedMask; /* Mask of shared locks held */ |
| 50683 | 51122 | u16 exclMask; /* Mask of exclusive locks held */ |
| 51123 | + HANDLE hShm; /* File-handle on *-shm file. For locking. */ | |
| 51124 | + int bReadonly; /* True if hShm is opened read-only */ | |
| 50684 | 51125 | #if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) |
| 50685 | 51126 | u8 id; /* Id of this connection with its winShmNode */ |
| 50686 | 51127 | #endif |
| 50687 | 51128 | }; |
| 50688 | 51129 | |
| @@ -50690,54 +51131,10 @@ | ||
| 50690 | 51131 | ** Constants used for locking |
| 50691 | 51132 | */ |
| 50692 | 51133 | #define WIN_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */ |
| 50693 | 51134 | #define WIN_SHM_DMS (WIN_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */ |
| 50694 | 51135 | |
| 50695 | -/* | |
| 50696 | -** Apply advisory locks for all n bytes beginning at ofst. | |
| 50697 | -*/ | |
| 50698 | -#define WINSHM_UNLCK 1 | |
| 50699 | -#define WINSHM_RDLCK 2 | |
| 50700 | -#define WINSHM_WRLCK 3 | |
| 50701 | -static int winShmSystemLock( | |
| 50702 | - winShmNode *pFile, /* Apply locks to this open shared-memory segment */ | |
| 50703 | - int lockType, /* WINSHM_UNLCK, WINSHM_RDLCK, or WINSHM_WRLCK */ | |
| 50704 | - int ofst, /* Offset to first byte to be locked/unlocked */ | |
| 50705 | - int nByte /* Number of bytes to lock or unlock */ | |
| 50706 | -){ | |
| 50707 | - int rc = 0; /* Result code form Lock/UnlockFileEx() */ | |
| 50708 | - | |
| 50709 | - /* Access to the winShmNode object is serialized by the caller */ | |
| 50710 | - assert( pFile->nRef==0 || sqlite3_mutex_held(pFile->mutex) ); | |
| 50711 | - | |
| 50712 | - OSTRACE(("SHM-LOCK file=%p, lock=%d, offset=%d, size=%d\n", | |
| 50713 | - pFile->hFile.h, lockType, ofst, nByte)); | |
| 50714 | - | |
| 50715 | - /* Release/Acquire the system-level lock */ | |
| 50716 | - if( lockType==WINSHM_UNLCK ){ | |
| 50717 | - rc = winUnlockFile(&pFile->hFile.h, ofst, 0, nByte, 0); | |
| 50718 | - }else{ | |
| 50719 | - /* Initialize the locking parameters */ | |
| 50720 | - DWORD dwFlags = LOCKFILE_FAIL_IMMEDIATELY; | |
| 50721 | - if( lockType == WINSHM_WRLCK ) dwFlags |= LOCKFILE_EXCLUSIVE_LOCK; | |
| 50722 | - rc = winLockFile(&pFile->hFile.h, dwFlags, ofst, 0, nByte, 0); | |
| 50723 | - } | |
| 50724 | - | |
| 50725 | - if( rc!= 0 ){ | |
| 50726 | - rc = SQLITE_OK; | |
| 50727 | - }else{ | |
| 50728 | - pFile->lastErrno = osGetLastError(); | |
| 50729 | - rc = SQLITE_BUSY; | |
| 50730 | - } | |
| 50731 | - | |
| 50732 | - OSTRACE(("SHM-LOCK file=%p, func=%s, errno=%lu, rc=%s\n", | |
| 50733 | - pFile->hFile.h, (lockType == WINSHM_UNLCK) ? "winUnlockFile" : | |
| 50734 | - "winLockFile", pFile->lastErrno, sqlite3ErrName(rc))); | |
| 50735 | - | |
| 50736 | - return rc; | |
| 50737 | -} | |
| 50738 | - | |
| 50739 | 51136 | /* Forward references to VFS methods */ |
| 50740 | 51137 | static int winOpen(sqlite3_vfs*,const char*,sqlite3_file*,int,int*); |
| 50741 | 51138 | static int winDelete(sqlite3_vfs *,const char*,int); |
| 50742 | 51139 | |
| 50743 | 51140 | /* |
| @@ -50765,15 +51162,11 @@ | ||
| 50765 | 51162 | bRc = osCloseHandle(p->aRegion[i].hMap); |
| 50766 | 51163 | OSTRACE(("SHM-PURGE-CLOSE pid=%lu, region=%d, rc=%s\n", |
| 50767 | 51164 | osGetCurrentProcessId(), i, bRc ? "ok" : "failed")); |
| 50768 | 51165 | UNUSED_VARIABLE_VALUE(bRc); |
| 50769 | 51166 | } |
| 50770 | - if( p->hFile.h!=NULL && p->hFile.h!=INVALID_HANDLE_VALUE ){ | |
| 50771 | - SimulateIOErrorBenign(1); | |
| 50772 | - winClose((sqlite3_file *)&p->hFile); | |
| 50773 | - SimulateIOErrorBenign(0); | |
| 50774 | - } | |
| 51167 | + winHandleClose(p->hSharedShm); | |
| 50775 | 51168 | if( deleteFlag ){ |
| 50776 | 51169 | SimulateIOErrorBenign(1); |
| 50777 | 51170 | sqlite3BeginBenignMalloc(); |
| 50778 | 51171 | winDelete(pVfs, p->zFilename, 0); |
| 50779 | 51172 | sqlite3EndBenignMalloc(); |
| @@ -50787,46 +51180,166 @@ | ||
| 50787 | 51180 | } |
| 50788 | 51181 | } |
| 50789 | 51182 | } |
| 50790 | 51183 | |
| 50791 | 51184 | /* |
| 50792 | -** The DMS lock has not yet been taken on shm file pShmNode. Attempt to | |
| 50793 | -** take it now. Return SQLITE_OK if successful, or an SQLite error | |
| 50794 | -** code otherwise. | |
| 50795 | -** | |
| 50796 | -** If the DMS cannot be locked because this is a readonly_shm=1 | |
| 50797 | -** connection and no other process already holds a lock, return | |
| 50798 | -** SQLITE_READONLY_CANTINIT and set pShmNode->isUnlocked=1. | |
| 51185 | +** The DMS lock has not yet been taken on the shm file associated with | |
| 51186 | +** pShmNode. Take the lock. Truncate the *-shm file if required. | |
| 51187 | +** Return SQLITE_OK if successful, or an SQLite error code otherwise. | |
| 50799 | 51188 | */ |
| 50800 | -static int winLockSharedMemory(winShmNode *pShmNode){ | |
| 50801 | - int rc = winShmSystemLock(pShmNode, WINSHM_WRLCK, WIN_SHM_DMS, 1); | |
| 51189 | +static int winLockSharedMemory(winShmNode *pShmNode, DWORD nMs){ | |
| 51190 | + HANDLE h = pShmNode->hSharedShm; | |
| 51191 | + int rc = SQLITE_OK; | |
| 50802 | 51192 | |
| 51193 | + assert( sqlite3_mutex_held(pShmNode->mutex) ); | |
| 51194 | + rc = winHandleLockTimeout(h, WIN_SHM_DMS, 1, 1, 0); | |
| 50803 | 51195 | if( rc==SQLITE_OK ){ |
| 51196 | + /* We have an EXCLUSIVE lock on the DMS byte. This means that this | |
| 51197 | + ** is the first process to open the file. Truncate it to zero bytes | |
| 51198 | + ** in this case. */ | |
| 50804 | 51199 | if( pShmNode->isReadonly ){ |
| 50805 | - pShmNode->isUnlocked = 1; | |
| 50806 | - winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1); | |
| 50807 | - return SQLITE_READONLY_CANTINIT; | |
| 50808 | - }else if( winTruncate((sqlite3_file*)&pShmNode->hFile, 0) ){ | |
| 50809 | - winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1); | |
| 50810 | - return winLogError(SQLITE_IOERR_SHMOPEN, osGetLastError(), | |
| 50811 | - "winLockSharedMemory", pShmNode->zFilename); | |
| 50812 | - } | |
| 50813 | - } | |
| 50814 | - | |
| 50815 | - if( rc==SQLITE_OK ){ | |
| 50816 | - winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1); | |
| 50817 | - } | |
| 50818 | - | |
| 50819 | - return winShmSystemLock(pShmNode, WINSHM_RDLCK, WIN_SHM_DMS, 1); | |
| 50820 | -} | |
| 50821 | - | |
| 50822 | -/* | |
| 50823 | -** Open the shared-memory area associated with database file pDbFd. | |
| 50824 | -** | |
| 50825 | -** When opening a new shared-memory file, if no other instances of that | |
| 50826 | -** file are currently open, in this process or in other processes, then | |
| 50827 | -** the file must be truncated to zero length or have its header cleared. | |
| 51200 | + rc = SQLITE_READONLY_CANTINIT; | |
| 51201 | + }else{ | |
| 51202 | + rc = winHandleTruncate(h, 0); | |
| 51203 | + } | |
| 51204 | + | |
| 51205 | + /* Release the EXCLUSIVE lock acquired above. */ | |
| 51206 | + winUnlockFile(&h, WIN_SHM_DMS, 0, 1, 0); | |
| 51207 | + }else if( (rc & 0xFF)==SQLITE_BUSY ){ | |
| 51208 | + rc = SQLITE_OK; | |
| 51209 | + } | |
| 51210 | + | |
| 51211 | + if( rc==SQLITE_OK ){ | |
| 51212 | + /* Take a SHARED lock on the DMS byte. */ | |
| 51213 | + rc = winHandleLockTimeout(h, WIN_SHM_DMS, 1, 0, nMs); | |
| 51214 | + if( rc==SQLITE_OK ){ | |
| 51215 | + pShmNode->isUnlocked = 0; | |
| 51216 | + } | |
| 51217 | + } | |
| 51218 | + | |
| 51219 | + return rc; | |
| 51220 | +} | |
| 51221 | + | |
| 51222 | + | |
| 51223 | +/* | |
| 51224 | +** Convert a UTF-8 filename into whatever form the underlying | |
| 51225 | +** operating system wants filenames in. Space to hold the result | |
| 51226 | +** is obtained from malloc and must be freed by the calling | |
| 51227 | +** function. | |
| 51228 | +*/ | |
| 51229 | +static void *winConvertFromUtf8Filename(const char *zFilename){ | |
| 51230 | + void *zConverted = 0; | |
| 51231 | + if( osIsNT() ){ | |
| 51232 | + zConverted = winUtf8ToUnicode(zFilename); | |
| 51233 | + } | |
| 51234 | +#ifdef SQLITE_WIN32_HAS_ANSI | |
| 51235 | + else{ | |
| 51236 | + zConverted = winUtf8ToMbcs(zFilename, osAreFileApisANSI()); | |
| 51237 | + } | |
| 51238 | +#endif | |
| 51239 | + /* caller will handle out of memory */ | |
| 51240 | + return zConverted; | |
| 51241 | +} | |
| 51242 | + | |
| 51243 | +/* | |
| 51244 | +** This function is used to open a handle on a *-shm file. | |
| 51245 | +** | |
| 51246 | +** If SQLITE_ENABLE_SETLK_TIMEOUT is defined at build time, then the file | |
| 51247 | +** is opened with FILE_FLAG_OVERLAPPED specified. If not, it is not. | |
| 51248 | +*/ | |
| 51249 | +static int winHandleOpen( | |
| 51250 | + const char *zUtf8, /* File to open */ | |
| 51251 | + int *pbReadonly, /* IN/OUT: True for readonly handle */ | |
| 51252 | + HANDLE *ph /* OUT: New HANDLE for file */ | |
| 51253 | +){ | |
| 51254 | + int rc = SQLITE_OK; | |
| 51255 | + void *zConverted = 0; | |
| 51256 | + int bReadonly = *pbReadonly; | |
| 51257 | + HANDLE h = INVALID_HANDLE_VALUE; | |
| 51258 | + | |
| 51259 | +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT | |
| 51260 | + const DWORD flag_overlapped = FILE_FLAG_OVERLAPPED; | |
| 51261 | +#else | |
| 51262 | + const DWORD flag_overlapped = 0; | |
| 51263 | +#endif | |
| 51264 | + | |
| 51265 | + /* Convert the filename to the system encoding. */ | |
| 51266 | + zConverted = winConvertFromUtf8Filename(zUtf8); | |
| 51267 | + if( zConverted==0 ){ | |
| 51268 | + OSTRACE(("OPEN name=%s, rc=SQLITE_IOERR_NOMEM", zUtf8)); | |
| 51269 | + rc = SQLITE_IOERR_NOMEM_BKPT; | |
| 51270 | + goto winopenfile_out; | |
| 51271 | + } | |
| 51272 | + | |
| 51273 | + /* Ensure the file we are trying to open is not actually a directory. */ | |
| 51274 | + if( winIsDir(zConverted) ){ | |
| 51275 | + OSTRACE(("OPEN name=%s, rc=SQLITE_CANTOPEN_ISDIR", zUtf8)); | |
| 51276 | + rc = SQLITE_CANTOPEN_ISDIR; | |
| 51277 | + goto winopenfile_out; | |
| 51278 | + } | |
| 51279 | + | |
| 51280 | + /* TODO: platforms. | |
| 51281 | + ** TODO: retry-on-ioerr. | |
| 51282 | + */ | |
| 51283 | + if( osIsNT() ){ | |
| 51284 | +#if SQLITE_OS_WINRT | |
| 51285 | + CREATEFILE2_EXTENDED_PARAMETERS extendedParameters; | |
| 51286 | + memset(&extendedParameters, 0, sizeof(extendedParameters)); | |
| 51287 | + extendedParameters.dwSize = sizeof(extendedParameters); | |
| 51288 | + extendedParameters.dwFileAttributes = FILE_ATTRIBUTE_NORMAL; | |
| 51289 | + extendedParameters.dwFileFlags = flag_overlapped; | |
| 51290 | + extendedParameters.dwSecurityQosFlags = SECURITY_ANONYMOUS; | |
| 51291 | + h = osCreateFile2((LPCWSTR)zConverted, | |
| 51292 | + (GENERIC_READ | (bReadonly ? 0 : GENERIC_WRITE)),/* dwDesiredAccess */ | |
| 51293 | + FILE_SHARE_READ | FILE_SHARE_WRITE, /* dwShareMode */ | |
| 51294 | + OPEN_ALWAYS, /* dwCreationDisposition */ | |
| 51295 | + &extendedParameters | |
| 51296 | + ); | |
| 51297 | +#else | |
| 51298 | + h = osCreateFileW((LPCWSTR)zConverted, /* lpFileName */ | |
| 51299 | + (GENERIC_READ | (bReadonly ? 0 : GENERIC_WRITE)), /* dwDesiredAccess */ | |
| 51300 | + FILE_SHARE_READ | FILE_SHARE_WRITE, /* dwShareMode */ | |
| 51301 | + NULL, /* lpSecurityAttributes */ | |
| 51302 | + OPEN_ALWAYS, /* dwCreationDisposition */ | |
| 51303 | + FILE_ATTRIBUTE_NORMAL|flag_overlapped, | |
| 51304 | + NULL | |
| 51305 | + ); | |
| 51306 | +#endif | |
| 51307 | + }else{ | |
| 51308 | + /* Due to pre-processor directives earlier in this file, | |
| 51309 | + ** SQLITE_WIN32_HAS_ANSI is always defined if osIsNT() is false. */ | |
| 51310 | +#ifdef SQLITE_WIN32_HAS_ANSI | |
| 51311 | + h = osCreateFileA((LPCSTR)zConverted, | |
| 51312 | + (GENERIC_READ | (bReadonly ? 0 : GENERIC_WRITE)), /* dwDesiredAccess */ | |
| 51313 | + FILE_SHARE_READ | FILE_SHARE_WRITE, /* dwShareMode */ | |
| 51314 | + NULL, /* lpSecurityAttributes */ | |
| 51315 | + OPEN_ALWAYS, /* dwCreationDisposition */ | |
| 51316 | + FILE_ATTRIBUTE_NORMAL|flag_overlapped, | |
| 51317 | + NULL | |
| 51318 | + ); | |
| 51319 | +#endif | |
| 51320 | + } | |
| 51321 | + | |
| 51322 | + if( h==INVALID_HANDLE_VALUE ){ | |
| 51323 | + if( bReadonly==0 ){ | |
| 51324 | + bReadonly = 1; | |
| 51325 | + rc = winHandleOpen(zUtf8, &bReadonly, &h); | |
| 51326 | + }else{ | |
| 51327 | + rc = SQLITE_CANTOPEN_BKPT; | |
| 51328 | + } | |
| 51329 | + } | |
| 51330 | + | |
| 51331 | + winopenfile_out: | |
| 51332 | + sqlite3_free(zConverted); | |
| 51333 | + *pbReadonly = bReadonly; | |
| 51334 | + *ph = h; | |
| 51335 | + return rc; | |
| 51336 | +} | |
| 51337 | + | |
| 51338 | + | |
| 51339 | +/* | |
| 51340 | +** Open the shared-memory area associated with database file pDbFd. | |
| 50828 | 51341 | */ |
| 50829 | 51342 | static int winOpenSharedMemory(winFile *pDbFd){ |
| 50830 | 51343 | struct winShm *p; /* The connection to be opened */ |
| 50831 | 51344 | winShmNode *pShmNode = 0; /* The underlying mmapped file */ |
| 50832 | 51345 | int rc = SQLITE_OK; /* Result code */ |
| @@ -50834,102 +51347,87 @@ | ||
| 50834 | 51347 | int nName; /* Size of zName in bytes */ |
| 50835 | 51348 | |
| 50836 | 51349 | assert( pDbFd->pShm==0 ); /* Not previously opened */ |
| 50837 | 51350 | |
| 50838 | 51351 | /* Allocate space for the new sqlite3_shm object. Also speculatively |
| 50839 | - ** allocate space for a new winShmNode and filename. | |
| 50840 | - */ | |
| 51352 | + ** allocate space for a new winShmNode and filename. */ | |
| 50841 | 51353 | p = sqlite3MallocZero( sizeof(*p) ); |
| 50842 | 51354 | if( p==0 ) return SQLITE_IOERR_NOMEM_BKPT; |
| 50843 | 51355 | nName = sqlite3Strlen30(pDbFd->zPath); |
| 50844 | 51356 | pNew = sqlite3MallocZero( sizeof(*pShmNode) + (i64)nName + 17 ); |
| 50845 | 51357 | if( pNew==0 ){ |
| 50846 | 51358 | sqlite3_free(p); |
| 50847 | 51359 | return SQLITE_IOERR_NOMEM_BKPT; |
| 50848 | 51360 | } |
| 50849 | 51361 | pNew->zFilename = (char*)&pNew[1]; |
| 51362 | + pNew->hSharedShm = INVALID_HANDLE_VALUE; | |
| 51363 | + pNew->isUnlocked = 1; | |
| 50850 | 51364 | sqlite3_snprintf(nName+15, pNew->zFilename, "%s-shm", pDbFd->zPath); |
| 50851 | 51365 | sqlite3FileSuffix3(pDbFd->zPath, pNew->zFilename); |
| 51366 | + | |
| 51367 | + /* Open a file-handle on the *-shm file for this connection. This file-handle | |
| 51368 | + ** is only used for locking. The mapping of the *-shm file is created using | |
| 51369 | + ** the shared file handle in winShmNode.hSharedShm. */ | |
| 51370 | + p->bReadonly = sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0); | |
| 51371 | + rc = winHandleOpen(pNew->zFilename, &p->bReadonly, &p->hShm); | |
| 50852 | 51372 | |
| 50853 | 51373 | /* Look to see if there is an existing winShmNode that can be used. |
| 50854 | - ** If no matching winShmNode currently exists, create a new one. | |
| 50855 | - */ | |
| 51374 | + ** If no matching winShmNode currently exists, then create a new one. */ | |
| 50856 | 51375 | winShmEnterMutex(); |
| 50857 | 51376 | for(pShmNode = winShmNodeList; pShmNode; pShmNode=pShmNode->pNext){ |
| 50858 | 51377 | /* TBD need to come up with better match here. Perhaps |
| 50859 | - ** use FILE_ID_BOTH_DIR_INFO Structure. | |
| 50860 | - */ | |
| 51378 | + ** use FILE_ID_BOTH_DIR_INFO Structure. */ | |
| 50861 | 51379 | if( sqlite3StrICmp(pShmNode->zFilename, pNew->zFilename)==0 ) break; |
| 50862 | 51380 | } |
| 50863 | - if( pShmNode ){ | |
| 50864 | - sqlite3_free(pNew); | |
| 50865 | - }else{ | |
| 50866 | - int inFlags = SQLITE_OPEN_WAL; | |
| 50867 | - int outFlags = 0; | |
| 50868 | - | |
| 51381 | + if( pShmNode==0 ){ | |
| 50869 | 51382 | pShmNode = pNew; |
| 50870 | - pNew = 0; | |
| 50871 | - ((winFile*)(&pShmNode->hFile))->h = INVALID_HANDLE_VALUE; | |
| 50872 | - pShmNode->pNext = winShmNodeList; | |
| 50873 | - winShmNodeList = pShmNode; | |
| 50874 | 51383 | |
| 51384 | + /* Allocate a mutex for this winShmNode object, if one is required. */ | |
| 50875 | 51385 | if( sqlite3GlobalConfig.bCoreMutex ){ |
| 50876 | 51386 | pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); |
| 50877 | - if( pShmNode->mutex==0 ){ | |
| 50878 | - rc = SQLITE_IOERR_NOMEM_BKPT; | |
| 50879 | - goto shm_open_err; | |
| 50880 | - } | |
| 50881 | - } | |
| 50882 | - | |
| 50883 | - if( 0==sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){ | |
| 50884 | - inFlags |= SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; | |
| 50885 | - }else{ | |
| 50886 | - inFlags |= SQLITE_OPEN_READONLY; | |
| 50887 | - } | |
| 50888 | - rc = winOpen(pDbFd->pVfs, pShmNode->zFilename, | |
| 50889 | - (sqlite3_file*)&pShmNode->hFile, | |
| 50890 | - inFlags, &outFlags); | |
| 50891 | - if( rc!=SQLITE_OK ){ | |
| 50892 | - rc = winLogError(rc, osGetLastError(), "winOpenShm", | |
| 50893 | - pShmNode->zFilename); | |
| 50894 | - goto shm_open_err; | |
| 50895 | - } | |
| 50896 | - if( outFlags==SQLITE_OPEN_READONLY ) pShmNode->isReadonly = 1; | |
| 50897 | - | |
| 50898 | - rc = winLockSharedMemory(pShmNode); | |
| 50899 | - if( rc!=SQLITE_OK && rc!=SQLITE_READONLY_CANTINIT ) goto shm_open_err; | |
| 50900 | - } | |
| 50901 | - | |
| 50902 | - /* Make the new connection a child of the winShmNode */ | |
| 50903 | - p->pShmNode = pShmNode; | |
| 51387 | + if( pShmNode->mutex==0 ) rc = SQLITE_IOERR_NOMEM_BKPT; | |
| 51388 | + } | |
| 51389 | + | |
| 51390 | + /* Open a file-handle to use for mappings, and for the DMS lock. */ | |
| 51391 | + if( rc==SQLITE_OK ){ | |
| 51392 | + HANDLE h = INVALID_HANDLE_VALUE; | |
| 51393 | + pShmNode->isReadonly = p->bReadonly; | |
| 51394 | + rc = winHandleOpen(pNew->zFilename, &pShmNode->isReadonly, &h); | |
| 51395 | + pShmNode->hSharedShm = h; | |
| 51396 | + } | |
| 51397 | + | |
| 51398 | + /* If successful, link the new winShmNode into the global list. If an | |
| 51399 | + ** error occurred, free the object. */ | |
| 51400 | + if( rc==SQLITE_OK ){ | |
| 51401 | + pShmNode->pNext = winShmNodeList; | |
| 51402 | + winShmNodeList = pShmNode; | |
| 51403 | + pNew = 0; | |
| 51404 | + }else{ | |
| 51405 | + sqlite3_mutex_free(pShmNode->mutex); | |
| 51406 | + if( pShmNode->hSharedShm!=INVALID_HANDLE_VALUE ){ | |
| 51407 | + osCloseHandle(pShmNode->hSharedShm); | |
| 51408 | + } | |
| 51409 | + } | |
| 51410 | + } | |
| 51411 | + | |
| 51412 | + /* If no error has occurred, link the winShm object to the winShmNode and | |
| 51413 | + ** the winShm to pDbFd. */ | |
| 51414 | + if( rc==SQLITE_OK ){ | |
| 51415 | + p->pShmNode = pShmNode; | |
| 51416 | + pShmNode->nRef++; | |
| 50904 | 51417 | #if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) |
| 50905 | - p->id = pShmNode->nextShmId++; | |
| 50906 | -#endif | |
| 50907 | - pShmNode->nRef++; | |
| 50908 | - pDbFd->pShm = p; | |
| 50909 | - winShmLeaveMutex(); | |
| 50910 | - | |
| 50911 | - /* The reference count on pShmNode has already been incremented under | |
| 50912 | - ** the cover of the winShmEnterMutex() mutex and the pointer from the | |
| 50913 | - ** new (struct winShm) object to the pShmNode has been set. All that is | |
| 50914 | - ** left to do is to link the new object into the linked list starting | |
| 50915 | - ** at pShmNode->pFirst. This must be done while holding the pShmNode->mutex | |
| 50916 | - ** mutex. | |
| 50917 | - */ | |
| 50918 | - sqlite3_mutex_enter(pShmNode->mutex); | |
| 50919 | - p->pNext = pShmNode->pFirst; | |
| 50920 | - pShmNode->pFirst = p; | |
| 50921 | - sqlite3_mutex_leave(pShmNode->mutex); | |
| 50922 | - return rc; | |
| 50923 | - | |
| 50924 | - /* Jump here on any error */ | |
| 50925 | -shm_open_err: | |
| 50926 | - winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1); | |
| 50927 | - winShmPurge(pDbFd->pVfs, 0); /* This call frees pShmNode if required */ | |
| 50928 | - sqlite3_free(p); | |
| 50929 | - sqlite3_free(pNew); | |
| 50930 | - winShmLeaveMutex(); | |
| 51418 | + p->id = pShmNode->nextShmId++; | |
| 51419 | +#endif | |
| 51420 | + pDbFd->pShm = p; | |
| 51421 | + }else if( p ){ | |
| 51422 | + winHandleClose(p->hShm); | |
| 51423 | + sqlite3_free(p); | |
| 51424 | + } | |
| 51425 | + | |
| 51426 | + assert( rc!=SQLITE_OK || pShmNode->isUnlocked==0 || pShmNode->nRegion==0 ); | |
| 51427 | + winShmLeaveMutex(); | |
| 51428 | + sqlite3_free(pNew); | |
| 50931 | 51429 | return rc; |
| 50932 | 51430 | } |
| 50933 | 51431 | |
| 50934 | 51432 | /* |
| 50935 | 51433 | ** Close a connection to shared-memory. Delete the underlying |
| @@ -50940,38 +51438,33 @@ | ||
| 50940 | 51438 | int deleteFlag /* Delete after closing if true */ |
| 50941 | 51439 | ){ |
| 50942 | 51440 | winFile *pDbFd; /* Database holding shared-memory */ |
| 50943 | 51441 | winShm *p; /* The connection to be closed */ |
| 50944 | 51442 | winShmNode *pShmNode; /* The underlying shared-memory file */ |
| 50945 | - winShm **pp; /* For looping over sibling connections */ | |
| 50946 | 51443 | |
| 50947 | 51444 | pDbFd = (winFile*)fd; |
| 50948 | 51445 | p = pDbFd->pShm; |
| 50949 | 51446 | if( p==0 ) return SQLITE_OK; |
| 51447 | + if( p->hShm!=INVALID_HANDLE_VALUE ){ | |
| 51448 | + osCloseHandle(p->hShm); | |
| 51449 | + } | |
| 51450 | + | |
| 50950 | 51451 | pShmNode = p->pShmNode; |
| 50951 | - | |
| 50952 | - /* Remove connection p from the set of connections associated | |
| 50953 | - ** with pShmNode */ | |
| 50954 | - sqlite3_mutex_enter(pShmNode->mutex); | |
| 50955 | - for(pp=&pShmNode->pFirst; (*pp)!=p; pp = &(*pp)->pNext){} | |
| 50956 | - *pp = p->pNext; | |
| 50957 | - | |
| 50958 | - /* Free the connection p */ | |
| 50959 | - sqlite3_free(p); | |
| 50960 | - pDbFd->pShm = 0; | |
| 50961 | - sqlite3_mutex_leave(pShmNode->mutex); | |
| 51452 | + winShmEnterMutex(); | |
| 50962 | 51453 | |
| 50963 | 51454 | /* If pShmNode->nRef has reached 0, then close the underlying |
| 50964 | - ** shared-memory file, too */ | |
| 50965 | - winShmEnterMutex(); | |
| 51455 | + ** shared-memory file, too. */ | |
| 50966 | 51456 | assert( pShmNode->nRef>0 ); |
| 50967 | 51457 | pShmNode->nRef--; |
| 50968 | 51458 | if( pShmNode->nRef==0 ){ |
| 50969 | 51459 | winShmPurge(pDbFd->pVfs, deleteFlag); |
| 50970 | 51460 | } |
| 50971 | 51461 | winShmLeaveMutex(); |
| 50972 | 51462 | |
| 51463 | + /* Free the connection p */ | |
| 51464 | + sqlite3_free(p); | |
| 51465 | + pDbFd->pShm = 0; | |
| 50973 | 51466 | return SQLITE_OK; |
| 50974 | 51467 | } |
| 50975 | 51468 | |
| 50976 | 51469 | /* |
| 50977 | 51470 | ** Change the lock state for a shared-memory segment. |
| @@ -50982,14 +51475,13 @@ | ||
| 50982 | 51475 | int n, /* Number of locks to acquire or release */ |
| 50983 | 51476 | int flags /* What to do with the lock */ |
| 50984 | 51477 | ){ |
| 50985 | 51478 | winFile *pDbFd = (winFile*)fd; /* Connection holding shared memory */ |
| 50986 | 51479 | winShm *p = pDbFd->pShm; /* The shared memory being locked */ |
| 50987 | - winShm *pX; /* For looping over all siblings */ | |
| 50988 | 51480 | winShmNode *pShmNode; |
| 50989 | 51481 | int rc = SQLITE_OK; /* Result code */ |
| 50990 | - u16 mask; /* Mask of locks to take or release */ | |
| 51482 | + u16 mask = (u16)((1U<<(ofst+n)) - (1U<<ofst)); /* Mask of locks to [un]take */ | |
| 50991 | 51483 | |
| 50992 | 51484 | if( p==0 ) return SQLITE_IOERR_SHMLOCK; |
| 50993 | 51485 | pShmNode = p->pShmNode; |
| 50994 | 51486 | if( NEVER(pShmNode==0) ) return SQLITE_IOERR_SHMLOCK; |
| 50995 | 51487 | |
| @@ -50999,89 +51491,86 @@ | ||
| 50999 | 51491 | || flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE) |
| 51000 | 51492 | || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED) |
| 51001 | 51493 | || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) ); |
| 51002 | 51494 | assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 ); |
| 51003 | 51495 | |
| 51004 | - mask = (u16)((1U<<(ofst+n)) - (1U<<ofst)); | |
| 51005 | - assert( n>1 || mask==(1<<ofst) ); | |
| 51006 | - sqlite3_mutex_enter(pShmNode->mutex); | |
| 51007 | - if( flags & SQLITE_SHM_UNLOCK ){ | |
| 51008 | - u16 allMask = 0; /* Mask of locks held by siblings */ | |
| 51009 | - | |
| 51010 | - /* See if any siblings hold this same lock */ | |
| 51011 | - for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ | |
| 51012 | - if( pX==p ) continue; | |
| 51013 | - assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 ); | |
| 51014 | - allMask |= pX->sharedMask; | |
| 51015 | - } | |
| 51016 | - | |
| 51017 | - /* Unlock the system-level locks */ | |
| 51018 | - if( (mask & allMask)==0 ){ | |
| 51019 | - rc = winShmSystemLock(pShmNode, WINSHM_UNLCK, ofst+WIN_SHM_BASE, n); | |
| 51020 | - }else{ | |
| 51021 | - rc = SQLITE_OK; | |
| 51022 | - } | |
| 51023 | - | |
| 51024 | - /* Undo the local locks */ | |
| 51025 | - if( rc==SQLITE_OK ){ | |
| 51026 | - p->exclMask &= ~mask; | |
| 51027 | - p->sharedMask &= ~mask; | |
| 51028 | - } | |
| 51029 | - }else if( flags & SQLITE_SHM_SHARED ){ | |
| 51030 | - u16 allShared = 0; /* Union of locks held by connections other than "p" */ | |
| 51031 | - | |
| 51032 | - /* Find out which shared locks are already held by sibling connections. | |
| 51033 | - ** If any sibling already holds an exclusive lock, go ahead and return | |
| 51034 | - ** SQLITE_BUSY. | |
| 51035 | - */ | |
| 51036 | - for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ | |
| 51037 | - if( (pX->exclMask & mask)!=0 ){ | |
| 51038 | - rc = SQLITE_BUSY; | |
| 51039 | - break; | |
| 51040 | - } | |
| 51041 | - allShared |= pX->sharedMask; | |
| 51042 | - } | |
| 51043 | - | |
| 51044 | - /* Get shared locks at the system level, if necessary */ | |
| 51045 | - if( rc==SQLITE_OK ){ | |
| 51046 | - if( (allShared & mask)==0 ){ | |
| 51047 | - rc = winShmSystemLock(pShmNode, WINSHM_RDLCK, ofst+WIN_SHM_BASE, n); | |
| 51048 | - }else{ | |
| 51049 | - rc = SQLITE_OK; | |
| 51050 | - } | |
| 51051 | - } | |
| 51052 | - | |
| 51053 | - /* Get the local shared locks */ | |
| 51054 | - if( rc==SQLITE_OK ){ | |
| 51055 | - p->sharedMask |= mask; | |
| 51056 | - } | |
| 51057 | - }else{ | |
| 51058 | - /* Make sure no sibling connections hold locks that will block this | |
| 51059 | - ** lock. If any do, return SQLITE_BUSY right away. | |
| 51060 | - */ | |
| 51061 | - for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ | |
| 51062 | - if( (pX->exclMask & mask)!=0 || (pX->sharedMask & mask)!=0 ){ | |
| 51063 | - rc = SQLITE_BUSY; | |
| 51064 | - break; | |
| 51065 | - } | |
| 51066 | - } | |
| 51067 | - | |
| 51068 | - /* Get the exclusive locks at the system level. Then if successful | |
| 51069 | - ** also mark the local connection as being locked. | |
| 51070 | - */ | |
| 51071 | - if( rc==SQLITE_OK ){ | |
| 51072 | - rc = winShmSystemLock(pShmNode, WINSHM_WRLCK, ofst+WIN_SHM_BASE, n); | |
| 51073 | - if( rc==SQLITE_OK ){ | |
| 51074 | - assert( (p->sharedMask & mask)==0 ); | |
| 51075 | - p->exclMask |= mask; | |
| 51076 | - } | |
| 51077 | - } | |
| 51078 | - } | |
| 51079 | - sqlite3_mutex_leave(pShmNode->mutex); | |
| 51080 | - OSTRACE(("SHM-LOCK pid=%lu, id=%d, sharedMask=%03x, exclMask=%03x, rc=%s\n", | |
| 51081 | - osGetCurrentProcessId(), p->id, p->sharedMask, p->exclMask, | |
| 51082 | - sqlite3ErrName(rc))); | |
| 51496 | + /* Check that, if this to be a blocking lock, no locks that occur later | |
| 51497 | + ** in the following list than the lock being obtained are already held: | |
| 51498 | + ** | |
| 51499 | + ** 1. Checkpointer lock (ofst==1). | |
| 51500 | + ** 2. Write lock (ofst==0). | |
| 51501 | + ** 3. Read locks (ofst>=3 && ofst<SQLITE_SHM_NLOCK). | |
| 51502 | + ** | |
| 51503 | + ** In other words, if this is a blocking lock, none of the locks that | |
| 51504 | + ** occur later in the above list than the lock being obtained may be | |
| 51505 | + ** held. | |
| 51506 | + ** | |
| 51507 | + ** It is not permitted to block on the RECOVER lock. | |
| 51508 | + */ | |
| 51509 | +#if defined(SQLITE_ENABLE_SETLK_TIMEOUT) && defined(SQLITE_DEBUG) | |
| 51510 | + { | |
| 51511 | + u16 lockMask = (p->exclMask|p->sharedMask); | |
| 51512 | + assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || ( | |
| 51513 | + (ofst!=2) /* not RECOVER */ | |
| 51514 | + && (ofst!=1 || lockMask==0 || lockMask==2) | |
| 51515 | + && (ofst!=0 || lockMask<3) | |
| 51516 | + && (ofst<3 || lockMask<(1<<ofst)) | |
| 51517 | + )); | |
| 51518 | + } | |
| 51519 | +#endif | |
| 51520 | + | |
| 51521 | + /* Check if there is any work to do. There are three cases: | |
| 51522 | + ** | |
| 51523 | + ** a) An unlock operation where there are locks to unlock, | |
| 51524 | + ** b) An shared lock where the requested lock is not already held | |
| 51525 | + ** c) An exclusive lock where the requested lock is not already held | |
| 51526 | + ** | |
| 51527 | + ** The SQLite core never requests an exclusive lock that it already holds. | |
| 51528 | + ** This is assert()ed immediately below. */ | |
| 51529 | + assert( flags!=(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK) | |
| 51530 | + || 0==(p->exclMask & mask) | |
| 51531 | + ); | |
| 51532 | + if( ((flags & SQLITE_SHM_UNLOCK) && ((p->exclMask|p->sharedMask) & mask)) | |
| 51533 | + || (flags==(SQLITE_SHM_SHARED|SQLITE_SHM_LOCK) && 0==(p->sharedMask & mask)) | |
| 51534 | + || (flags==(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK)) | |
| 51535 | + ){ | |
| 51536 | + | |
| 51537 | + if( flags & SQLITE_SHM_UNLOCK ){ | |
| 51538 | + /* Case (a) - unlock. */ | |
| 51539 | + | |
| 51540 | + assert( (p->exclMask & p->sharedMask)==0 ); | |
| 51541 | + assert( !(flags & SQLITE_SHM_EXCLUSIVE) || (p->exclMask & mask)==mask ); | |
| 51542 | + assert( !(flags & SQLITE_SHM_SHARED) || (p->sharedMask & mask)==mask ); | |
| 51543 | + | |
| 51544 | + rc = winHandleUnlock(p->hShm, ofst+WIN_SHM_BASE, n); | |
| 51545 | + | |
| 51546 | + /* If successful, also clear the bits in sharedMask/exclMask */ | |
| 51547 | + if( rc==SQLITE_OK ){ | |
| 51548 | + p->exclMask = (p->exclMask & ~mask); | |
| 51549 | + p->sharedMask = (p->sharedMask & ~mask); | |
| 51550 | + } | |
| 51551 | + }else{ | |
| 51552 | + int bExcl = ((flags & SQLITE_SHM_EXCLUSIVE) ? 1 : 0); | |
| 51553 | + DWORD nMs = winFileBusyTimeout(pDbFd); | |
| 51554 | + rc = winHandleLockTimeout(p->hShm, ofst+WIN_SHM_BASE, n, bExcl, nMs); | |
| 51555 | + if( rc==SQLITE_OK ){ | |
| 51556 | + if( bExcl ){ | |
| 51557 | + p->exclMask = (p->exclMask | mask); | |
| 51558 | + }else{ | |
| 51559 | + p->sharedMask = (p->sharedMask | mask); | |
| 51560 | + } | |
| 51561 | + } | |
| 51562 | + } | |
| 51563 | + } | |
| 51564 | + | |
| 51565 | + OSTRACE(( | |
| 51566 | + "SHM-LOCK(%d,%d,%d) pid=%lu, id=%d, sharedMask=%03x, exclMask=%03x," | |
| 51567 | + " rc=%s\n", | |
| 51568 | + ofst, n, flags, | |
| 51569 | + osGetCurrentProcessId(), p->id, p->sharedMask, p->exclMask, | |
| 51570 | + sqlite3ErrName(rc)) | |
| 51571 | + ); | |
| 51083 | 51572 | return rc; |
| 51084 | 51573 | } |
| 51085 | 51574 | |
| 51086 | 51575 | /* |
| 51087 | 51576 | ** Implement a memory barrier or memory fence on shared memory. |
| @@ -51139,17 +51628,19 @@ | ||
| 51139 | 51628 | } |
| 51140 | 51629 | pShmNode = pShm->pShmNode; |
| 51141 | 51630 | |
| 51142 | 51631 | sqlite3_mutex_enter(pShmNode->mutex); |
| 51143 | 51632 | if( pShmNode->isUnlocked ){ |
| 51144 | - rc = winLockSharedMemory(pShmNode); | |
| 51633 | + /* Take the DMS lock. */ | |
| 51634 | + assert( pShmNode->nRegion==0 ); | |
| 51635 | + rc = winLockSharedMemory(pShmNode, winFileBusyTimeout(pDbFd)); | |
| 51145 | 51636 | if( rc!=SQLITE_OK ) goto shmpage_out; |
| 51146 | - pShmNode->isUnlocked = 0; | |
| 51147 | 51637 | } |
| 51638 | + | |
| 51148 | 51639 | assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 ); |
| 51149 | - | |
| 51150 | 51640 | if( pShmNode->nRegion<=iRegion ){ |
| 51641 | + HANDLE hShared = pShmNode->hSharedShm; | |
| 51151 | 51642 | struct ShmRegion *apNew; /* New aRegion[] array */ |
| 51152 | 51643 | int nByte = (iRegion+1)*szRegion; /* Minimum required file size */ |
| 51153 | 51644 | sqlite3_int64 sz; /* Current size of wal-index file */ |
| 51154 | 51645 | |
| 51155 | 51646 | pShmNode->szRegion = szRegion; |
| @@ -51156,35 +51647,32 @@ | ||
| 51156 | 51647 | |
| 51157 | 51648 | /* The requested region is not mapped into this processes address space. |
| 51158 | 51649 | ** Check to see if it has been allocated (i.e. if the wal-index file is |
| 51159 | 51650 | ** large enough to contain the requested region). |
| 51160 | 51651 | */ |
| 51161 | - rc = winFileSize((sqlite3_file *)&pShmNode->hFile, &sz); | |
| 51652 | + rc = winHandleSize(hShared, &sz); | |
| 51162 | 51653 | if( rc!=SQLITE_OK ){ |
| 51163 | - rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(), | |
| 51164 | - "winShmMap1", pDbFd->zPath); | |
| 51654 | + rc = winLogError(rc, osGetLastError(), "winShmMap1", pDbFd->zPath); | |
| 51165 | 51655 | goto shmpage_out; |
| 51166 | 51656 | } |
| 51167 | 51657 | |
| 51168 | 51658 | if( sz<nByte ){ |
| 51169 | 51659 | /* The requested memory region does not exist. If isWrite is set to |
| 51170 | 51660 | ** zero, exit early. *pp will be set to NULL and SQLITE_OK returned. |
| 51171 | 51661 | ** |
| 51172 | 51662 | ** Alternatively, if isWrite is non-zero, use ftruncate() to allocate |
| 51173 | - ** the requested memory region. | |
| 51174 | - */ | |
| 51663 | + ** the requested memory region. */ | |
| 51175 | 51664 | if( !isWrite ) goto shmpage_out; |
| 51176 | - rc = winTruncate((sqlite3_file *)&pShmNode->hFile, nByte); | |
| 51665 | + rc = winHandleTruncate(hShared, nByte); | |
| 51177 | 51666 | if( rc!=SQLITE_OK ){ |
| 51178 | - rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(), | |
| 51179 | - "winShmMap2", pDbFd->zPath); | |
| 51667 | + rc = winLogError(rc, osGetLastError(), "winShmMap2", pDbFd->zPath); | |
| 51180 | 51668 | goto shmpage_out; |
| 51181 | 51669 | } |
| 51182 | 51670 | } |
| 51183 | 51671 | |
| 51184 | 51672 | /* Map the requested memory region into this processes address space. */ |
| 51185 | - apNew = (struct ShmRegion *)sqlite3_realloc64( | |
| 51673 | + apNew = (struct ShmRegion*)sqlite3_realloc64( | |
| 51186 | 51674 | pShmNode->aRegion, (iRegion+1)*sizeof(apNew[0]) |
| 51187 | 51675 | ); |
| 51188 | 51676 | if( !apNew ){ |
| 51189 | 51677 | rc = SQLITE_IOERR_NOMEM_BKPT; |
| 51190 | 51678 | goto shmpage_out; |
| @@ -51199,22 +51687,17 @@ | ||
| 51199 | 51687 | while( pShmNode->nRegion<=iRegion ){ |
| 51200 | 51688 | HANDLE hMap = NULL; /* file-mapping handle */ |
| 51201 | 51689 | void *pMap = 0; /* Mapped memory region */ |
| 51202 | 51690 | |
| 51203 | 51691 | #if SQLITE_OS_WINRT |
| 51204 | - hMap = osCreateFileMappingFromApp(pShmNode->hFile.h, | |
| 51205 | - NULL, protect, nByte, NULL | |
| 51206 | - ); | |
| 51692 | + hMap = osCreateFileMappingFromApp(hShared, NULL, protect, nByte, NULL); | |
| 51207 | 51693 | #elif defined(SQLITE_WIN32_HAS_WIDE) |
| 51208 | - hMap = osCreateFileMappingW(pShmNode->hFile.h, | |
| 51209 | - NULL, protect, 0, nByte, NULL | |
| 51210 | - ); | |
| 51694 | + hMap = osCreateFileMappingW(hShared, NULL, protect, 0, nByte, NULL); | |
| 51211 | 51695 | #elif defined(SQLITE_WIN32_HAS_ANSI) && SQLITE_WIN32_CREATEFILEMAPPINGA |
| 51212 | - hMap = osCreateFileMappingA(pShmNode->hFile.h, | |
| 51213 | - NULL, protect, 0, nByte, NULL | |
| 51214 | - ); | |
| 51696 | + hMap = osCreateFileMappingA(hShared, NULL, protect, 0, nByte, NULL); | |
| 51215 | 51697 | #endif |
| 51698 | + | |
| 51216 | 51699 | OSTRACE(("SHM-MAP-CREATE pid=%lu, region=%d, size=%d, rc=%s\n", |
| 51217 | 51700 | osGetCurrentProcessId(), pShmNode->nRegion, nByte, |
| 51218 | 51701 | hMap ? "ok" : "failed")); |
| 51219 | 51702 | if( hMap ){ |
| 51220 | 51703 | int iOffset = pShmNode->nRegion*szRegion; |
| @@ -51253,11 +51736,13 @@ | ||
| 51253 | 51736 | char *p = (char *)pShmNode->aRegion[iRegion].pMap; |
| 51254 | 51737 | *pp = (void *)&p[iOffsetShift]; |
| 51255 | 51738 | }else{ |
| 51256 | 51739 | *pp = 0; |
| 51257 | 51740 | } |
| 51258 | - if( pShmNode->isReadonly && rc==SQLITE_OK ) rc = SQLITE_READONLY; | |
| 51741 | + if( pShmNode->isReadonly && rc==SQLITE_OK ){ | |
| 51742 | + rc = SQLITE_READONLY; | |
| 51743 | + } | |
| 51259 | 51744 | sqlite3_mutex_leave(pShmNode->mutex); |
| 51260 | 51745 | return rc; |
| 51261 | 51746 | } |
| 51262 | 51747 | |
| 51263 | 51748 | #else |
| @@ -51594,30 +52079,10 @@ | ||
| 51594 | 52079 | /* caller will handle out of memory */ |
| 51595 | 52080 | return zConverted; |
| 51596 | 52081 | } |
| 51597 | 52082 | #endif |
| 51598 | 52083 | |
| 51599 | -/* | |
| 51600 | -** Convert a UTF-8 filename into whatever form the underlying | |
| 51601 | -** operating system wants filenames in. Space to hold the result | |
| 51602 | -** is obtained from malloc and must be freed by the calling | |
| 51603 | -** function. | |
| 51604 | -*/ | |
| 51605 | -static void *winConvertFromUtf8Filename(const char *zFilename){ | |
| 51606 | - void *zConverted = 0; | |
| 51607 | - if( osIsNT() ){ | |
| 51608 | - zConverted = winUtf8ToUnicode(zFilename); | |
| 51609 | - } | |
| 51610 | -#ifdef SQLITE_WIN32_HAS_ANSI | |
| 51611 | - else{ | |
| 51612 | - zConverted = winUtf8ToMbcs(zFilename, osAreFileApisANSI()); | |
| 51613 | - } | |
| 51614 | -#endif | |
| 51615 | - /* caller will handle out of memory */ | |
| 51616 | - return zConverted; | |
| 51617 | -} | |
| 51618 | - | |
| 51619 | 52084 | /* |
| 51620 | 52085 | ** This function returns non-zero if the specified UTF-8 string buffer |
| 51621 | 52086 | ** ends with a directory separator character or one was successfully |
| 51622 | 52087 | ** added to it. |
| 51623 | 52088 | */ |
| @@ -53067,11 +53532,11 @@ | ||
| 53067 | 53532 | }; |
| 53068 | 53533 | #endif |
| 53069 | 53534 | |
| 53070 | 53535 | /* Double-check that the aSyscall[] array has been constructed |
| 53071 | 53536 | ** correctly. See ticket [bb3a86e890c8e96ab] */ |
| 53072 | - assert( ArraySize(aSyscall)==80 ); | |
| 53537 | + assert( ArraySize(aSyscall)==82 ); | |
| 53073 | 53538 | |
| 53074 | 53539 | /* get memory map allocation granularity */ |
| 53075 | 53540 | memset(&winSysInfo, 0, sizeof(SYSTEM_INFO)); |
| 53076 | 53541 | #if SQLITE_OS_WINRT |
| 53077 | 53542 | osGetNativeSystemInfo(&winSysInfo); |
| @@ -54125,11 +54590,11 @@ | ||
| 54125 | 54590 | ** Empirical testing showed that the *37 multiplier |
| 54126 | 54591 | ** (an arbitrary prime)in the hash function provided |
| 54127 | 54592 | ** no fewer collisions than the no-op *1. */ |
| 54128 | 54593 | #define BITVEC_HASH(X) (((X)*1)%BITVEC_NINT) |
| 54129 | 54594 | |
| 54130 | -#define BITVEC_NPTR (BITVEC_USIZE/sizeof(Bitvec *)) | |
| 54595 | +#define BITVEC_NPTR ((u32)(BITVEC_USIZE/sizeof(Bitvec *))) | |
| 54131 | 54596 | |
| 54132 | 54597 | |
| 54133 | 54598 | /* |
| 54134 | 54599 | ** A bitmap is an instance of the following structure. |
| 54135 | 54600 | ** |
| @@ -54308,11 +54773,11 @@ | ||
| 54308 | 54773 | if (!p) { |
| 54309 | 54774 | return; |
| 54310 | 54775 | } |
| 54311 | 54776 | } |
| 54312 | 54777 | if( p->iSize<=BITVEC_NBIT ){ |
| 54313 | - p->u.aBitmap[i/BITVEC_SZELEM] &= ~(1 << (i&(BITVEC_SZELEM-1))); | |
| 54778 | + p->u.aBitmap[i/BITVEC_SZELEM] &= ~(BITVEC_TELEM)(1<<(i&(BITVEC_SZELEM-1))); | |
| 54314 | 54779 | }else{ |
| 54315 | 54780 | unsigned int j; |
| 54316 | 54781 | u32 *aiValues = pBuf; |
| 54317 | 54782 | memcpy(aiValues, p->u.aHash, sizeof(p->u.aHash)); |
| 54318 | 54783 | memset(p->u.aHash, 0, sizeof(p->u.aHash)); |
| @@ -54359,11 +54824,11 @@ | ||
| 54359 | 54824 | ** up to N bits. Let I be an integer between 0 and N. 0<=I<N. |
| 54360 | 54825 | ** Then the following macros can be used to set, clear, or test |
| 54361 | 54826 | ** individual bits within V. |
| 54362 | 54827 | */ |
| 54363 | 54828 | #define SETBIT(V,I) V[I>>3] |= (1<<(I&7)) |
| 54364 | -#define CLEARBIT(V,I) V[I>>3] &= ~(1<<(I&7)) | |
| 54829 | +#define CLEARBIT(V,I) V[I>>3] &= ~(BITVEC_TELEM)(1<<(I&7)) | |
| 54365 | 54830 | #define TESTBIT(V,I) (V[I>>3]&(1<<(I&7)))!=0 |
| 54366 | 54831 | |
| 54367 | 54832 | /* |
| 54368 | 54833 | ** This routine runs an extensive test of the Bitvec code. |
| 54369 | 54834 | ** |
| @@ -55643,14 +56108,10 @@ | ||
| 55643 | 56108 | void *pStart, *pEnd; /* Bounds of global page cache memory */ |
| 55644 | 56109 | /* Above requires no mutex. Use mutex below for variable that follow. */ |
| 55645 | 56110 | sqlite3_mutex *mutex; /* Mutex for accessing the following: */ |
| 55646 | 56111 | PgFreeslot *pFree; /* Free page blocks */ |
| 55647 | 56112 | int nFreeSlot; /* Number of unused pcache slots */ |
| 55648 | - /* The following value requires a mutex to change. We skip the mutex on | |
| 55649 | - ** reading because (1) most platforms read a 32-bit integer atomically and | |
| 55650 | - ** (2) even if an incorrect value is read, no great harm is done since this | |
| 55651 | - ** is really just an optimization. */ | |
| 55652 | 56113 | int bUnderPressure; /* True if low on PAGECACHE memory */ |
| 55653 | 56114 | } pcache1_g; |
| 55654 | 56115 | |
| 55655 | 56116 | /* |
| 55656 | 56117 | ** All code in this file should access the global structure above via the |
| @@ -55694,11 +56155,11 @@ | ||
| 55694 | 56155 | pcache1.szSlot = sz; |
| 55695 | 56156 | pcache1.nSlot = pcache1.nFreeSlot = n; |
| 55696 | 56157 | pcache1.nReserve = n>90 ? 10 : (n/10 + 1); |
| 55697 | 56158 | pcache1.pStart = pBuf; |
| 55698 | 56159 | pcache1.pFree = 0; |
| 55699 | - pcache1.bUnderPressure = 0; | |
| 56160 | + AtomicStore(&pcache1.bUnderPressure,0); | |
| 55700 | 56161 | while( n-- ){ |
| 55701 | 56162 | p = (PgFreeslot*)pBuf; |
| 55702 | 56163 | p->pNext = pcache1.pFree; |
| 55703 | 56164 | pcache1.pFree = p; |
| 55704 | 56165 | pBuf = (void*)&((char*)pBuf)[sz]; |
| @@ -55762,11 +56223,11 @@ | ||
| 55762 | 56223 | sqlite3_mutex_enter(pcache1.mutex); |
| 55763 | 56224 | p = (PgHdr1 *)pcache1.pFree; |
| 55764 | 56225 | if( p ){ |
| 55765 | 56226 | pcache1.pFree = pcache1.pFree->pNext; |
| 55766 | 56227 | pcache1.nFreeSlot--; |
| 55767 | - pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve; | |
| 56228 | + AtomicStore(&pcache1.bUnderPressure,pcache1.nFreeSlot<pcache1.nReserve); | |
| 55768 | 56229 | assert( pcache1.nFreeSlot>=0 ); |
| 55769 | 56230 | sqlite3StatusHighwater(SQLITE_STATUS_PAGECACHE_SIZE, nByte); |
| 55770 | 56231 | sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_USED, 1); |
| 55771 | 56232 | } |
| 55772 | 56233 | sqlite3_mutex_leave(pcache1.mutex); |
| @@ -55801,11 +56262,11 @@ | ||
| 55801 | 56262 | sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_USED, 1); |
| 55802 | 56263 | pSlot = (PgFreeslot*)p; |
| 55803 | 56264 | pSlot->pNext = pcache1.pFree; |
| 55804 | 56265 | pcache1.pFree = pSlot; |
| 55805 | 56266 | pcache1.nFreeSlot++; |
| 55806 | - pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve; | |
| 56267 | + AtomicStore(&pcache1.bUnderPressure,pcache1.nFreeSlot<pcache1.nReserve); | |
| 55807 | 56268 | assert( pcache1.nFreeSlot<=pcache1.nSlot ); |
| 55808 | 56269 | sqlite3_mutex_leave(pcache1.mutex); |
| 55809 | 56270 | }else{ |
| 55810 | 56271 | assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) ); |
| 55811 | 56272 | sqlite3MemdebugSetType(p, MEMTYPE_HEAP); |
| @@ -55932,11 +56393,11 @@ | ||
| 55932 | 56393 | ** allocating a new page cache entry in order to avoid stressing |
| 55933 | 56394 | ** the heap even further. |
| 55934 | 56395 | */ |
| 55935 | 56396 | static int pcache1UnderMemoryPressure(PCache1 *pCache){ |
| 55936 | 56397 | if( pcache1.nSlot && (pCache->szPage+pCache->szExtra)<=pcache1.szSlot ){ |
| 55937 | - return pcache1.bUnderPressure; | |
| 56398 | + return AtomicLoad(&pcache1.bUnderPressure); | |
| 55938 | 56399 | }else{ |
| 55939 | 56400 | return sqlite3HeapNearlyFull(); |
| 55940 | 56401 | } |
| 55941 | 56402 | } |
| 55942 | 56403 | |
| @@ -59211,10 +59672,19 @@ | ||
| 59211 | 59672 | pPager->pInJournal = 0; |
| 59212 | 59673 | releaseAllSavepoints(pPager); |
| 59213 | 59674 | |
| 59214 | 59675 | if( pagerUseWal(pPager) ){ |
| 59215 | 59676 | assert( !isOpen(pPager->jfd) ); |
| 59677 | + if( pPager->eState==PAGER_ERROR ){ | |
| 59678 | + /* If an IO error occurs in wal.c while attempting to wrap the wal file, | |
| 59679 | + ** then the Wal object may be holding a write-lock but no read-lock. | |
| 59680 | + ** This call ensures that the write-lock is dropped as well. We cannot | |
| 59681 | + ** have sqlite3WalEndReadTransaction() drop the write-lock, as it once | |
| 59682 | + ** did, because this would break "BEGIN EXCLUSIVE" handling for | |
| 59683 | + ** SQLITE_ENABLE_SETLK_TIMEOUT builds. */ | |
| 59684 | + sqlite3WalEndWriteTransaction(pPager->pWal); | |
| 59685 | + } | |
| 59216 | 59686 | sqlite3WalEndReadTransaction(pPager->pWal); |
| 59217 | 59687 | pPager->eState = PAGER_OPEN; |
| 59218 | 59688 | }else if( !pPager->exclusiveMode ){ |
| 59219 | 59689 | int rc; /* Error code returned by pagerUnlockDb() */ |
| 59220 | 59690 | int iDc = isOpen(pPager->fd)?sqlite3OsDeviceCharacteristics(pPager->fd):0; |
| @@ -65669,10 +66139,15 @@ | ||
| 65669 | 66139 | ) |
| 65670 | 66140 | |
| 65671 | 66141 | /* |
| 65672 | 66142 | ** An open write-ahead log file is represented by an instance of the |
| 65673 | 66143 | ** following object. |
| 66144 | +** | |
| 66145 | +** writeLock: | |
| 66146 | +** This is usually set to 1 whenever the WRITER lock is held. However, | |
| 66147 | +** if it is set to 2, then the WRITER lock is held but must be released | |
| 66148 | +** by walHandleException() if a SEH exception is thrown. | |
| 65674 | 66149 | */ |
| 65675 | 66150 | struct Wal { |
| 65676 | 66151 | sqlite3_vfs *pVfs; /* The VFS used to create pDbFd */ |
| 65677 | 66152 | sqlite3_file *pDbFd; /* File handle for the database file */ |
| 65678 | 66153 | sqlite3_file *pWalFd; /* File handle for WAL file */ |
| @@ -65759,12 +66234,16 @@ | ||
| 65759 | 66234 | int iNext; /* Next slot in aIndex[] not yet returned */ |
| 65760 | 66235 | ht_slot *aIndex; /* i0, i1, i2... such that aPgno[iN] ascend */ |
| 65761 | 66236 | u32 *aPgno; /* Array of page numbers. */ |
| 65762 | 66237 | int nEntry; /* Nr. of entries in aPgno[] and aIndex[] */ |
| 65763 | 66238 | int iZero; /* Frame number associated with aPgno[0] */ |
| 65764 | - } aSegment[1]; /* One for every 32KB page in the wal-index */ | |
| 66239 | + } aSegment[FLEXARRAY]; /* One for every 32KB page in the wal-index */ | |
| 65765 | 66240 | }; |
| 66241 | + | |
| 66242 | +/* Size (in bytes) of a WalIterator object suitable for N or fewer segments */ | |
| 66243 | +#define SZ_WALITERATOR(N) \ | |
| 66244 | + (offsetof(WalIterator,aSegment)*(N)*sizeof(struct WalSegment)) | |
| 65766 | 66245 | |
| 65767 | 66246 | /* |
| 65768 | 66247 | ** Define the parameters of the hash tables in the wal-index file. There |
| 65769 | 66248 | ** is a hash-table following every HASHTABLE_NPAGE page numbers in the |
| 65770 | 66249 | ** wal-index. |
| @@ -67122,12 +67601,11 @@ | ||
| 67122 | 67601 | assert( pWal->ckptLock && pWal->hdr.mxFrame>0 ); |
| 67123 | 67602 | iLast = pWal->hdr.mxFrame; |
| 67124 | 67603 | |
| 67125 | 67604 | /* Allocate space for the WalIterator object. */ |
| 67126 | 67605 | nSegment = walFramePage(iLast) + 1; |
| 67127 | - nByte = sizeof(WalIterator) | |
| 67128 | - + (nSegment-1)*sizeof(struct WalSegment) | |
| 67606 | + nByte = SZ_WALITERATOR(nSegment) | |
| 67129 | 67607 | + iLast*sizeof(ht_slot); |
| 67130 | 67608 | p = (WalIterator *)sqlite3_malloc64(nByte |
| 67131 | 67609 | + sizeof(ht_slot) * (iLast>HASHTABLE_NPAGE?HASHTABLE_NPAGE:iLast) |
| 67132 | 67610 | ); |
| 67133 | 67611 | if( !p ){ |
| @@ -67194,11 +67672,11 @@ | ||
| 67194 | 67672 | ** or 0 otherwise. |
| 67195 | 67673 | */ |
| 67196 | 67674 | static int walEnableBlocking(Wal *pWal){ |
| 67197 | 67675 | int res = 0; |
| 67198 | 67676 | if( pWal->db ){ |
| 67199 | - int tmout = pWal->db->busyTimeout; | |
| 67677 | + int tmout = pWal->db->setlkTimeout; | |
| 67200 | 67678 | if( tmout ){ |
| 67201 | 67679 | res = walEnableBlockingMs(pWal, tmout); |
| 67202 | 67680 | } |
| 67203 | 67681 | } |
| 67204 | 67682 | return res; |
| @@ -67580,11 +68058,13 @@ | ||
| 67580 | 68058 | static int walHandleException(Wal *pWal){ |
| 67581 | 68059 | if( pWal->exclusiveMode==0 ){ |
| 67582 | 68060 | static const int S = 1; |
| 67583 | 68061 | static const int E = (1<<SQLITE_SHM_NLOCK); |
| 67584 | 68062 | int ii; |
| 67585 | - u32 mUnlock = pWal->lockMask & ~( | |
| 68063 | + u32 mUnlock; | |
| 68064 | + if( pWal->writeLock==2 ) pWal->writeLock = 0; | |
| 68065 | + mUnlock = pWal->lockMask & ~( | |
| 67586 | 68066 | (pWal->readLock<0 ? 0 : (S << WAL_READ_LOCK(pWal->readLock))) |
| 67587 | 68067 | | (pWal->writeLock ? (E << WAL_WRITE_LOCK) : 0) |
| 67588 | 68068 | | (pWal->ckptLock ? (E << WAL_CKPT_LOCK) : 0) |
| 67589 | 68069 | ); |
| 67590 | 68070 | for(ii=0; ii<SQLITE_SHM_NLOCK; ii++){ |
| @@ -67852,11 +68332,16 @@ | ||
| 67852 | 68332 | }else{ |
| 67853 | 68333 | int bWriteLock = pWal->writeLock; |
| 67854 | 68334 | if( bWriteLock |
| 67855 | 68335 | || SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1)) |
| 67856 | 68336 | ){ |
| 67857 | - pWal->writeLock = 1; | |
| 68337 | + /* If the write-lock was just obtained, set writeLock to 2 instead of | |
| 68338 | + ** the usual 1. This causes walIndexPage() to behave as if the | |
| 68339 | + ** write-lock were held (so that it allocates new pages as required), | |
| 68340 | + ** and walHandleException() to unlock the write-lock if a SEH exception | |
| 68341 | + ** is thrown. */ | |
| 68342 | + if( !bWriteLock ) pWal->writeLock = 2; | |
| 67858 | 68343 | if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){ |
| 67859 | 68344 | badHdr = walIndexTryHdr(pWal, pChanged); |
| 67860 | 68345 | if( badHdr ){ |
| 67861 | 68346 | /* If the wal-index header is still malformed even while holding |
| 67862 | 68347 | ** a WRITE lock, it can only mean that the header is corrupted and |
| @@ -68637,12 +69122,15 @@ | ||
| 68637 | 69122 | /* |
| 68638 | 69123 | ** Finish with a read transaction. All this does is release the |
| 68639 | 69124 | ** read-lock. |
| 68640 | 69125 | */ |
| 68641 | 69126 | SQLITE_PRIVATE void sqlite3WalEndReadTransaction(Wal *pWal){ |
| 68642 | - sqlite3WalEndWriteTransaction(pWal); | |
| 69127 | +#ifndef SQLITE_ENABLE_SETLK_TIMEOUT | |
| 69128 | + assert( pWal->writeLock==0 || pWal->readLock<0 ); | |
| 69129 | +#endif | |
| 68643 | 69130 | if( pWal->readLock>=0 ){ |
| 69131 | + sqlite3WalEndWriteTransaction(pWal); | |
| 68644 | 69132 | walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock)); |
| 68645 | 69133 | pWal->readLock = -1; |
| 68646 | 69134 | } |
| 68647 | 69135 | } |
| 68648 | 69136 | |
| @@ -68831,11 +69319,11 @@ | ||
| 68831 | 69319 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 68832 | 69320 | /* If the write-lock is already held, then it was obtained before the |
| 68833 | 69321 | ** read-transaction was even opened, making this call a no-op. |
| 68834 | 69322 | ** Return early. */ |
| 68835 | 69323 | if( pWal->writeLock ){ |
| 68836 | - assert( !memcmp(&pWal->hdr,(void *)walIndexHdr(pWal),sizeof(WalIndexHdr)) ); | |
| 69324 | + assert( !memcmp(&pWal->hdr,(void*)pWal->apWiData[0],sizeof(WalIndexHdr)) ); | |
| 68837 | 69325 | return SQLITE_OK; |
| 68838 | 69326 | } |
| 68839 | 69327 | #endif |
| 68840 | 69328 | |
| 68841 | 69329 | /* Cannot start a write transaction without first holding a read |
| @@ -70280,10 +70768,16 @@ | ||
| 70280 | 70768 | ** If a tree that appears to be taller than this is encountered, it is |
| 70281 | 70769 | ** assumed that the database is corrupt. |
| 70282 | 70770 | */ |
| 70283 | 70771 | #define BTCURSOR_MAX_DEPTH 20 |
| 70284 | 70772 | |
| 70773 | +/* | |
| 70774 | +** Maximum amount of storage local to a database page, regardless of | |
| 70775 | +** page size. | |
| 70776 | +*/ | |
| 70777 | +#define BT_MAX_LOCAL 65501 /* 65536 - 35 */ | |
| 70778 | + | |
| 70285 | 70779 | /* |
| 70286 | 70780 | ** A cursor is a pointer to a particular entry within a particular |
| 70287 | 70781 | ** b-tree within a database file. |
| 70288 | 70782 | ** |
| 70289 | 70783 | ** The entry is identified by its MemPage and the index in |
| @@ -70688,11 +71182,11 @@ | ||
| 70688 | 71182 | ** two or more btrees in common both try to lock all their btrees |
| 70689 | 71183 | ** at the same instant. |
| 70690 | 71184 | */ |
| 70691 | 71185 | static void SQLITE_NOINLINE btreeEnterAll(sqlite3 *db){ |
| 70692 | 71186 | int i; |
| 70693 | - int skipOk = 1; | |
| 71187 | + u8 skipOk = 1; | |
| 70694 | 71188 | Btree *p; |
| 70695 | 71189 | assert( sqlite3_mutex_held(db->mutex) ); |
| 70696 | 71190 | for(i=0; i<db->nDb; i++){ |
| 70697 | 71191 | p = db->aDb[i].pBt; |
| 70698 | 71192 | if( p && p->sharable ){ |
| @@ -71834,11 +72328,11 @@ | ||
| 71834 | 72328 | /* |
| 71835 | 72329 | ** Provide flag hints to the cursor. |
| 71836 | 72330 | */ |
| 71837 | 72331 | SQLITE_PRIVATE void sqlite3BtreeCursorHintFlags(BtCursor *pCur, unsigned x){ |
| 71838 | 72332 | assert( x==BTREE_SEEK_EQ || x==BTREE_BULKLOAD || x==0 ); |
| 71839 | - pCur->hints = x; | |
| 72333 | + pCur->hints = (u8)x; | |
| 71840 | 72334 | } |
| 71841 | 72335 | |
| 71842 | 72336 | |
| 71843 | 72337 | #ifndef SQLITE_OMIT_AUTOVACUUM |
| 71844 | 72338 | /* |
| @@ -72028,18 +72522,19 @@ | ||
| 72028 | 72522 | ** page pPage, return the number of bytes of payload stored locally. |
| 72029 | 72523 | */ |
| 72030 | 72524 | static int btreePayloadToLocal(MemPage *pPage, i64 nPayload){ |
| 72031 | 72525 | int maxLocal; /* Maximum amount of payload held locally */ |
| 72032 | 72526 | maxLocal = pPage->maxLocal; |
| 72527 | + assert( nPayload>=0 ); | |
| 72033 | 72528 | if( nPayload<=maxLocal ){ |
| 72034 | - return nPayload; | |
| 72529 | + return (int)nPayload; | |
| 72035 | 72530 | }else{ |
| 72036 | 72531 | int minLocal; /* Minimum amount of payload held locally */ |
| 72037 | 72532 | int surplus; /* Overflow payload available for local storage */ |
| 72038 | 72533 | minLocal = pPage->minLocal; |
| 72039 | - surplus = minLocal + (nPayload - minLocal)%(pPage->pBt->usableSize-4); | |
| 72040 | - return ( surplus <= maxLocal ) ? surplus : minLocal; | |
| 72534 | + surplus = (int)(minLocal +(nPayload - minLocal)%(pPage->pBt->usableSize-4)); | |
| 72535 | + return (surplus <= maxLocal) ? surplus : minLocal; | |
| 72041 | 72536 | } |
| 72042 | 72537 | } |
| 72043 | 72538 | |
| 72044 | 72539 | /* |
| 72045 | 72540 | ** The following routines are implementations of the MemPage.xParseCell() |
| @@ -72145,15 +72640,17 @@ | ||
| 72145 | 72640 | pInfo->nKey = *(i64*)&iKey; |
| 72146 | 72641 | pInfo->nPayload = nPayload; |
| 72147 | 72642 | pInfo->pPayload = pIter; |
| 72148 | 72643 | testcase( nPayload==pPage->maxLocal ); |
| 72149 | 72644 | testcase( nPayload==(u32)pPage->maxLocal+1 ); |
| 72645 | + assert( nPayload>=0 ); | |
| 72646 | + assert( pPage->maxLocal <= BT_MAX_LOCAL ); | |
| 72150 | 72647 | if( nPayload<=pPage->maxLocal ){ |
| 72151 | 72648 | /* This is the (easy) common case where the entire payload fits |
| 72152 | 72649 | ** on the local page. No overflow is required. |
| 72153 | 72650 | */ |
| 72154 | - pInfo->nSize = nPayload + (u16)(pIter - pCell); | |
| 72651 | + pInfo->nSize = (u16)nPayload + (u16)(pIter - pCell); | |
| 72155 | 72652 | if( pInfo->nSize<4 ) pInfo->nSize = 4; |
| 72156 | 72653 | pInfo->nLocal = (u16)nPayload; |
| 72157 | 72654 | }else{ |
| 72158 | 72655 | btreeParseCellAdjustSizeForOverflow(pPage, pCell, pInfo); |
| 72159 | 72656 | } |
| @@ -72182,15 +72679,17 @@ | ||
| 72182 | 72679 | pInfo->nKey = nPayload; |
| 72183 | 72680 | pInfo->nPayload = nPayload; |
| 72184 | 72681 | pInfo->pPayload = pIter; |
| 72185 | 72682 | testcase( nPayload==pPage->maxLocal ); |
| 72186 | 72683 | testcase( nPayload==(u32)pPage->maxLocal+1 ); |
| 72684 | + assert( nPayload>=0 ); | |
| 72685 | + assert( pPage->maxLocal <= BT_MAX_LOCAL ); | |
| 72187 | 72686 | if( nPayload<=pPage->maxLocal ){ |
| 72188 | 72687 | /* This is the (easy) common case where the entire payload fits |
| 72189 | 72688 | ** on the local page. No overflow is required. |
| 72190 | 72689 | */ |
| 72191 | - pInfo->nSize = nPayload + (u16)(pIter - pCell); | |
| 72690 | + pInfo->nSize = (u16)nPayload + (u16)(pIter - pCell); | |
| 72192 | 72691 | if( pInfo->nSize<4 ) pInfo->nSize = 4; |
| 72193 | 72692 | pInfo->nLocal = (u16)nPayload; |
| 72194 | 72693 | }else{ |
| 72195 | 72694 | btreeParseCellAdjustSizeForOverflow(pPage, pCell, pInfo); |
| 72196 | 72695 | } |
| @@ -72725,18 +73224,18 @@ | ||
| 72725 | 73224 | ** that routine will not detect overlap between cells or freeblocks. Nor |
| 72726 | 73225 | ** does it detect cells or freeblocks that encroach into the reserved bytes |
| 72727 | 73226 | ** at the end of the page. So do additional corruption checks inside this |
| 72728 | 73227 | ** routine and return SQLITE_CORRUPT if any problems are found. |
| 72729 | 73228 | */ |
| 72730 | -static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ | |
| 72731 | - u16 iPtr; /* Address of ptr to next freeblock */ | |
| 72732 | - u16 iFreeBlk; /* Address of the next freeblock */ | |
| 73229 | +static int freeSpace(MemPage *pPage, int iStart, int iSize){ | |
| 73230 | + int iPtr; /* Address of ptr to next freeblock */ | |
| 73231 | + int iFreeBlk; /* Address of the next freeblock */ | |
| 72733 | 73232 | u8 hdr; /* Page header size. 0 or 100 */ |
| 72734 | - u8 nFrag = 0; /* Reduction in fragmentation */ | |
| 72735 | - u16 iOrigSize = iSize; /* Original value of iSize */ | |
| 72736 | - u16 x; /* Offset to cell content area */ | |
| 72737 | - u32 iEnd = iStart + iSize; /* First byte past the iStart buffer */ | |
| 73233 | + int nFrag = 0; /* Reduction in fragmentation */ | |
| 73234 | + int iOrigSize = iSize; /* Original value of iSize */ | |
| 73235 | + int x; /* Offset to cell content area */ | |
| 73236 | + int iEnd = iStart + iSize; /* First byte past the iStart buffer */ | |
| 72738 | 73237 | unsigned char *data = pPage->aData; /* Page content */ |
| 72739 | 73238 | u8 *pTmp; /* Temporary ptr into data[] */ |
| 72740 | 73239 | |
| 72741 | 73240 | assert( pPage->pBt!=0 ); |
| 72742 | 73241 | assert( sqlite3PagerIswriteable(pPage->pDbPage) ); |
| @@ -72759,11 +73258,11 @@ | ||
| 72759 | 73258 | if( iFreeBlk==0 ) break; /* TH3: corrupt082.100 */ |
| 72760 | 73259 | return SQLITE_CORRUPT_PAGE(pPage); |
| 72761 | 73260 | } |
| 72762 | 73261 | iPtr = iFreeBlk; |
| 72763 | 73262 | } |
| 72764 | - if( iFreeBlk>pPage->pBt->usableSize-4 ){ /* TH3: corrupt081.100 */ | |
| 73263 | + if( iFreeBlk>(int)pPage->pBt->usableSize-4 ){ /* TH3: corrupt081.100 */ | |
| 72765 | 73264 | return SQLITE_CORRUPT_PAGE(pPage); |
| 72766 | 73265 | } |
| 72767 | 73266 | assert( iFreeBlk>iPtr || iFreeBlk==0 || CORRUPT_DB ); |
| 72768 | 73267 | |
| 72769 | 73268 | /* At this point: |
| @@ -72774,11 +73273,11 @@ | ||
| 72774 | 73273 | */ |
| 72775 | 73274 | if( iFreeBlk && iEnd+3>=iFreeBlk ){ |
| 72776 | 73275 | nFrag = iFreeBlk - iEnd; |
| 72777 | 73276 | if( iEnd>iFreeBlk ) return SQLITE_CORRUPT_PAGE(pPage); |
| 72778 | 73277 | iEnd = iFreeBlk + get2byte(&data[iFreeBlk+2]); |
| 72779 | - if( iEnd > pPage->pBt->usableSize ){ | |
| 73278 | + if( iEnd > (int)pPage->pBt->usableSize ){ | |
| 72780 | 73279 | return SQLITE_CORRUPT_PAGE(pPage); |
| 72781 | 73280 | } |
| 72782 | 73281 | iSize = iEnd - iStart; |
| 72783 | 73282 | iFreeBlk = get2byte(&data[iFreeBlk]); |
| 72784 | 73283 | } |
| @@ -72795,11 +73294,11 @@ | ||
| 72795 | 73294 | iSize = iEnd - iPtr; |
| 72796 | 73295 | iStart = iPtr; |
| 72797 | 73296 | } |
| 72798 | 73297 | } |
| 72799 | 73298 | if( nFrag>data[hdr+7] ) return SQLITE_CORRUPT_PAGE(pPage); |
| 72800 | - data[hdr+7] -= nFrag; | |
| 73299 | + data[hdr+7] -= (u8)nFrag; | |
| 72801 | 73300 | } |
| 72802 | 73301 | pTmp = &data[hdr+5]; |
| 72803 | 73302 | x = get2byte(pTmp); |
| 72804 | 73303 | if( pPage->pBt->btsFlags & BTS_FAST_SECURE ){ |
| 72805 | 73304 | /* Overwrite deleted information with zeros when the secure_delete |
| @@ -72816,11 +73315,12 @@ | ||
| 72816 | 73315 | put2byte(&data[hdr+5], iEnd); |
| 72817 | 73316 | }else{ |
| 72818 | 73317 | /* Insert the new freeblock into the freelist */ |
| 72819 | 73318 | put2byte(&data[iPtr], iStart); |
| 72820 | 73319 | put2byte(&data[iStart], iFreeBlk); |
| 72821 | - put2byte(&data[iStart+2], iSize); | |
| 73320 | + assert( iSize>=0 && iSize<=0xffff ); | |
| 73321 | + put2byte(&data[iStart+2], (u16)iSize); | |
| 72822 | 73322 | } |
| 72823 | 73323 | pPage->nFree += iOrigSize; |
| 72824 | 73324 | return SQLITE_OK; |
| 72825 | 73325 | } |
| 72826 | 73326 | |
| @@ -73042,11 +73542,11 @@ | ||
| 73042 | 73542 | return SQLITE_CORRUPT_PAGE(pPage); |
| 73043 | 73543 | } |
| 73044 | 73544 | assert( pBt->pageSize>=512 && pBt->pageSize<=65536 ); |
| 73045 | 73545 | pPage->maskPage = (u16)(pBt->pageSize - 1); |
| 73046 | 73546 | pPage->nOverflow = 0; |
| 73047 | - pPage->cellOffset = pPage->hdrOffset + 8 + pPage->childPtrSize; | |
| 73547 | + pPage->cellOffset = (u16)(pPage->hdrOffset + 8 + pPage->childPtrSize); | |
| 73048 | 73548 | pPage->aCellIdx = data + pPage->childPtrSize + 8; |
| 73049 | 73549 | pPage->aDataEnd = pPage->aData + pBt->pageSize; |
| 73050 | 73550 | pPage->aDataOfst = pPage->aData + pPage->childPtrSize; |
| 73051 | 73551 | /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the |
| 73052 | 73552 | ** number of cells on the page. */ |
| @@ -73076,12 +73576,12 @@ | ||
| 73076 | 73576 | ** no entries. |
| 73077 | 73577 | */ |
| 73078 | 73578 | static void zeroPage(MemPage *pPage, int flags){ |
| 73079 | 73579 | unsigned char *data = pPage->aData; |
| 73080 | 73580 | BtShared *pBt = pPage->pBt; |
| 73081 | - u8 hdr = pPage->hdrOffset; | |
| 73082 | - u16 first; | |
| 73581 | + int hdr = pPage->hdrOffset; | |
| 73582 | + int first; | |
| 73083 | 73583 | |
| 73084 | 73584 | assert( sqlite3PagerPagenumber(pPage->pDbPage)==pPage->pgno || CORRUPT_DB ); |
| 73085 | 73585 | assert( sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage ); |
| 73086 | 73586 | assert( sqlite3PagerGetData(pPage->pDbPage) == data ); |
| 73087 | 73587 | assert( sqlite3PagerIswriteable(pPage->pDbPage) ); |
| @@ -73094,11 +73594,11 @@ | ||
| 73094 | 73594 | memset(&data[hdr+1], 0, 4); |
| 73095 | 73595 | data[hdr+7] = 0; |
| 73096 | 73596 | put2byte(&data[hdr+5], pBt->usableSize); |
| 73097 | 73597 | pPage->nFree = (u16)(pBt->usableSize - first); |
| 73098 | 73598 | decodeFlags(pPage, flags); |
| 73099 | - pPage->cellOffset = first; | |
| 73599 | + pPage->cellOffset = (u16)first; | |
| 73100 | 73600 | pPage->aDataEnd = &data[pBt->pageSize]; |
| 73101 | 73601 | pPage->aCellIdx = &data[first]; |
| 73102 | 73602 | pPage->aDataOfst = &data[pPage->childPtrSize]; |
| 73103 | 73603 | pPage->nOverflow = 0; |
| 73104 | 73604 | assert( pBt->pageSize>=512 && pBt->pageSize<=65536 ); |
| @@ -73880,11 +74380,11 @@ | ||
| 73880 | 74380 | int rc = SQLITE_OK; |
| 73881 | 74381 | int x; |
| 73882 | 74382 | BtShared *pBt = p->pBt; |
| 73883 | 74383 | assert( nReserve>=0 && nReserve<=255 ); |
| 73884 | 74384 | sqlite3BtreeEnter(p); |
| 73885 | - pBt->nReserveWanted = nReserve; | |
| 74385 | + pBt->nReserveWanted = (u8)nReserve; | |
| 73886 | 74386 | x = pBt->pageSize - pBt->usableSize; |
| 73887 | 74387 | if( nReserve<x ) nReserve = x; |
| 73888 | 74388 | if( pBt->btsFlags & BTS_PAGESIZE_FIXED ){ |
| 73889 | 74389 | sqlite3BtreeLeave(p); |
| 73890 | 74390 | return SQLITE_READONLY; |
| @@ -73986,11 +74486,11 @@ | ||
| 73986 | 74486 | sqlite3BtreeEnter(p); |
| 73987 | 74487 | assert( BTS_OVERWRITE==BTS_SECURE_DELETE*2 ); |
| 73988 | 74488 | assert( BTS_FAST_SECURE==(BTS_OVERWRITE|BTS_SECURE_DELETE) ); |
| 73989 | 74489 | if( newFlag>=0 ){ |
| 73990 | 74490 | p->pBt->btsFlags &= ~BTS_FAST_SECURE; |
| 73991 | - p->pBt->btsFlags |= BTS_SECURE_DELETE*newFlag; | |
| 74491 | + p->pBt->btsFlags |= (u16)(BTS_SECURE_DELETE*newFlag); | |
| 73992 | 74492 | } |
| 73993 | 74493 | b = (p->pBt->btsFlags & BTS_FAST_SECURE)/BTS_SECURE_DELETE; |
| 73994 | 74494 | sqlite3BtreeLeave(p); |
| 73995 | 74495 | return b; |
| 73996 | 74496 | } |
| @@ -78434,11 +78934,12 @@ | ||
| 78434 | 78934 | pSrcEnd = pCArray->apEnd[k]; |
| 78435 | 78935 | } |
| 78436 | 78936 | } |
| 78437 | 78937 | |
| 78438 | 78938 | /* The pPg->nFree field is now set incorrectly. The caller will fix it. */ |
| 78439 | - pPg->nCell = nCell; | |
| 78939 | + assert( nCell < 10922 ); | |
| 78940 | + pPg->nCell = (u16)nCell; | |
| 78440 | 78941 | pPg->nOverflow = 0; |
| 78441 | 78942 | |
| 78442 | 78943 | put2byte(&aData[hdr+1], 0); |
| 78443 | 78944 | put2byte(&aData[hdr+3], pPg->nCell); |
| 78444 | 78945 | put2byte(&aData[hdr+5], pData - aData); |
| @@ -78681,13 +79182,17 @@ | ||
| 78681 | 79182 | assert( nCell>=0 ); |
| 78682 | 79183 | pCellptr = &pPg->aCellIdx[nCell*2]; |
| 78683 | 79184 | if( pageInsertArray( |
| 78684 | 79185 | pPg, pBegin, &pData, pCellptr, |
| 78685 | 79186 | iNew+nCell, nNew-nCell, pCArray |
| 78686 | - ) ) goto editpage_fail; | |
| 79187 | + ) | |
| 79188 | + ){ | |
| 79189 | + goto editpage_fail; | |
| 79190 | + } | |
| 78687 | 79191 | |
| 78688 | - pPg->nCell = nNew; | |
| 79192 | + assert( nNew < 10922 ); | |
| 79193 | + pPg->nCell = (u16)nNew; | |
| 78689 | 79194 | pPg->nOverflow = 0; |
| 78690 | 79195 | |
| 78691 | 79196 | put2byte(&aData[hdr+3], pPg->nCell); |
| 78692 | 79197 | put2byte(&aData[hdr+5], pData - aData); |
| 78693 | 79198 | |
| @@ -78992,11 +79497,11 @@ | ||
| 78992 | 79497 | int leafData; /* True if pPage is a leaf of a LEAFDATA tree */ |
| 78993 | 79498 | int usableSpace; /* Bytes in pPage beyond the header */ |
| 78994 | 79499 | int pageFlags; /* Value of pPage->aData[0] */ |
| 78995 | 79500 | int iSpace1 = 0; /* First unused byte of aSpace1[] */ |
| 78996 | 79501 | int iOvflSpace = 0; /* First unused byte of aOvflSpace[] */ |
| 78997 | - int szScratch; /* Size of scratch memory requested */ | |
| 79502 | + u64 szScratch; /* Size of scratch memory requested */ | |
| 78998 | 79503 | MemPage *apOld[NB]; /* pPage and up to two siblings */ |
| 78999 | 79504 | MemPage *apNew[NB+2]; /* pPage and up to NB siblings after balancing */ |
| 79000 | 79505 | u8 *pRight; /* Location in parent of right-sibling pointer */ |
| 79001 | 79506 | u8 *apDiv[NB-1]; /* Divider cells in pParent */ |
| 79002 | 79507 | int cntNew[NB+2]; /* Index in b.paCell[] of cell after i-th page */ |
| @@ -80277,11 +80782,11 @@ | ||
| 80277 | 80782 | if( loc==0 ){ |
| 80278 | 80783 | getCellInfo(pCur); |
| 80279 | 80784 | if( pCur->info.nKey==pX->nKey ){ |
| 80280 | 80785 | BtreePayload x2; |
| 80281 | 80786 | x2.pData = pX->pKey; |
| 80282 | - x2.nData = pX->nKey; | |
| 80787 | + x2.nData = (int)pX->nKey; assert( pX->nKey<=0x7fffffff ); | |
| 80283 | 80788 | x2.nZero = 0; |
| 80284 | 80789 | return btreeOverwriteCell(pCur, &x2); |
| 80285 | 80790 | } |
| 80286 | 80791 | } |
| 80287 | 80792 | } |
| @@ -80458,11 +80963,11 @@ | ||
| 80458 | 80963 | u32 nIn; /* Size of input buffer aIn[] */ |
| 80459 | 80964 | u32 nRem; /* Bytes of data still to copy */ |
| 80460 | 80965 | |
| 80461 | 80966 | getCellInfo(pSrc); |
| 80462 | 80967 | if( pSrc->info.nPayload<0x80 ){ |
| 80463 | - *(aOut++) = pSrc->info.nPayload; | |
| 80968 | + *(aOut++) = (u8)pSrc->info.nPayload; | |
| 80464 | 80969 | }else{ |
| 80465 | 80970 | aOut += sqlite3PutVarint(aOut, pSrc->info.nPayload); |
| 80466 | 80971 | } |
| 80467 | 80972 | if( pDest->pKeyInfo==0 ) aOut += putVarint(aOut, iKey); |
| 80468 | 80973 | nIn = pSrc->info.nLocal; |
| @@ -80471,11 +80976,11 @@ | ||
| 80471 | 80976 | return SQLITE_CORRUPT_PAGE(pSrc->pPage); |
| 80472 | 80977 | } |
| 80473 | 80978 | nRem = pSrc->info.nPayload; |
| 80474 | 80979 | if( nIn==nRem && nIn<pDest->pPage->maxLocal ){ |
| 80475 | 80980 | memcpy(aOut, aIn, nIn); |
| 80476 | - pBt->nPreformatSize = nIn + (aOut - pBt->pTmpSpace); | |
| 80981 | + pBt->nPreformatSize = nIn + (int)(aOut - pBt->pTmpSpace); | |
| 80477 | 80982 | return SQLITE_OK; |
| 80478 | 80983 | }else{ |
| 80479 | 80984 | int rc = SQLITE_OK; |
| 80480 | 80985 | Pager *pSrcPager = pSrc->pBt->pPager; |
| 80481 | 80986 | u8 *pPgnoOut = 0; |
| @@ -80483,11 +80988,11 @@ | ||
| 80483 | 80988 | DbPage *pPageIn = 0; |
| 80484 | 80989 | MemPage *pPageOut = 0; |
| 80485 | 80990 | u32 nOut; /* Size of output buffer aOut[] */ |
| 80486 | 80991 | |
| 80487 | 80992 | nOut = btreePayloadToLocal(pDest->pPage, pSrc->info.nPayload); |
| 80488 | - pBt->nPreformatSize = nOut + (aOut - pBt->pTmpSpace); | |
| 80993 | + pBt->nPreformatSize = (int)nOut + (int)(aOut - pBt->pTmpSpace); | |
| 80489 | 80994 | if( nOut<pSrc->info.nPayload ){ |
| 80490 | 80995 | pPgnoOut = &aOut[nOut]; |
| 80491 | 80996 | pBt->nPreformatSize += 4; |
| 80492 | 80997 | } |
| 80493 | 80998 | |
| @@ -85584,16 +86089,14 @@ | ||
| 85584 | 86089 | int nArg, /* Number of argument */ |
| 85585 | 86090 | const FuncDef *pFunc, /* The function to be invoked */ |
| 85586 | 86091 | int eCallCtx /* Calling context */ |
| 85587 | 86092 | ){ |
| 85588 | 86093 | Vdbe *v = pParse->pVdbe; |
| 85589 | - int nByte; | |
| 85590 | 86094 | int addr; |
| 85591 | 86095 | sqlite3_context *pCtx; |
| 85592 | 86096 | assert( v ); |
| 85593 | - nByte = sizeof(*pCtx) + (nArg-1)*sizeof(sqlite3_value*); | |
| 85594 | - pCtx = sqlite3DbMallocRawNN(pParse->db, nByte); | |
| 86097 | + pCtx = sqlite3DbMallocRawNN(pParse->db, SZ_CONTEXT(nArg)); | |
| 85595 | 86098 | if( pCtx==0 ){ |
| 85596 | 86099 | assert( pParse->db->mallocFailed ); |
| 85597 | 86100 | freeEphemeralFunction(pParse->db, (FuncDef*)pFunc); |
| 85598 | 86101 | return 0; |
| 85599 | 86102 | } |
| @@ -90665,25 +91168,26 @@ | ||
| 90665 | 91168 | |
| 90666 | 91169 | preupdate.v = v; |
| 90667 | 91170 | preupdate.pCsr = pCsr; |
| 90668 | 91171 | preupdate.op = op; |
| 90669 | 91172 | preupdate.iNewReg = iReg; |
| 90670 | - preupdate.keyinfo.db = db; | |
| 90671 | - preupdate.keyinfo.enc = ENC(db); | |
| 90672 | - preupdate.keyinfo.nKeyField = pTab->nCol; | |
| 90673 | - preupdate.keyinfo.aSortFlags = (u8*)&fakeSortOrder; | |
| 91173 | + preupdate.pKeyinfo = (KeyInfo*)&preupdate.keyinfoSpace; | |
| 91174 | + preupdate.pKeyinfo->db = db; | |
| 91175 | + preupdate.pKeyinfo->enc = ENC(db); | |
| 91176 | + preupdate.pKeyinfo->nKeyField = pTab->nCol; | |
| 91177 | + preupdate.pKeyinfo->aSortFlags = (u8*)&fakeSortOrder; | |
| 90674 | 91178 | preupdate.iKey1 = iKey1; |
| 90675 | 91179 | preupdate.iKey2 = iKey2; |
| 90676 | 91180 | preupdate.pTab = pTab; |
| 90677 | 91181 | preupdate.iBlobWrite = iBlobWrite; |
| 90678 | 91182 | |
| 90679 | 91183 | db->pPreUpdate = &preupdate; |
| 90680 | 91184 | db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2); |
| 90681 | 91185 | db->pPreUpdate = 0; |
| 90682 | 91186 | sqlite3DbFree(db, preupdate.aRecord); |
| 90683 | - vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pUnpacked); | |
| 90684 | - vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pNewUnpacked); | |
| 91187 | + vdbeFreeUnpacked(db, preupdate.pKeyinfo->nKeyField+1,preupdate.pUnpacked); | |
| 91188 | + vdbeFreeUnpacked(db, preupdate.pKeyinfo->nKeyField+1,preupdate.pNewUnpacked); | |
| 90685 | 91189 | sqlite3VdbeMemRelease(&preupdate.oldipk); |
| 90686 | 91190 | if( preupdate.aNew ){ |
| 90687 | 91191 | int i; |
| 90688 | 91192 | for(i=0; i<pCsr->nField; i++){ |
| 90689 | 91193 | sqlite3VdbeMemRelease(&preupdate.aNew[i]); |
| @@ -92918,11 +93422,11 @@ | ||
| 92918 | 93422 | nRec = sqlite3BtreePayloadSize(p->pCsr->uc.pCursor); |
| 92919 | 93423 | aRec = sqlite3DbMallocRaw(db, nRec); |
| 92920 | 93424 | if( !aRec ) goto preupdate_old_out; |
| 92921 | 93425 | rc = sqlite3BtreePayload(p->pCsr->uc.pCursor, 0, nRec, aRec); |
| 92922 | 93426 | if( rc==SQLITE_OK ){ |
| 92923 | - p->pUnpacked = vdbeUnpackRecord(&p->keyinfo, nRec, aRec); | |
| 93427 | + p->pUnpacked = vdbeUnpackRecord(p->pKeyinfo, nRec, aRec); | |
| 92924 | 93428 | if( !p->pUnpacked ) rc = SQLITE_NOMEM; |
| 92925 | 93429 | } |
| 92926 | 93430 | if( rc!=SQLITE_OK ){ |
| 92927 | 93431 | sqlite3DbFree(db, aRec); |
| 92928 | 93432 | goto preupdate_old_out; |
| @@ -92983,11 +93487,11 @@ | ||
| 92983 | 93487 | #ifdef SQLITE_ENABLE_API_ARMOR |
| 92984 | 93488 | p = db!=0 ? db->pPreUpdate : 0; |
| 92985 | 93489 | #else |
| 92986 | 93490 | p = db->pPreUpdate; |
| 92987 | 93491 | #endif |
| 92988 | - return (p ? p->keyinfo.nKeyField : 0); | |
| 93492 | + return (p ? p->pKeyinfo->nKeyField : 0); | |
| 92989 | 93493 | } |
| 92990 | 93494 | #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ |
| 92991 | 93495 | |
| 92992 | 93496 | #ifdef SQLITE_ENABLE_PREUPDATE_HOOK |
| 92993 | 93497 | /* |
| @@ -93066,11 +93570,11 @@ | ||
| 93066 | 93570 | UnpackedRecord *pUnpack = p->pNewUnpacked; |
| 93067 | 93571 | if( !pUnpack ){ |
| 93068 | 93572 | Mem *pData = &p->v->aMem[p->iNewReg]; |
| 93069 | 93573 | rc = ExpandBlob(pData); |
| 93070 | 93574 | if( rc!=SQLITE_OK ) goto preupdate_new_out; |
| 93071 | - pUnpack = vdbeUnpackRecord(&p->keyinfo, pData->n, pData->z); | |
| 93575 | + pUnpack = vdbeUnpackRecord(p->pKeyinfo, pData->n, pData->z); | |
| 93072 | 93576 | if( !pUnpack ){ |
| 93073 | 93577 | rc = SQLITE_NOMEM; |
| 93074 | 93578 | goto preupdate_new_out; |
| 93075 | 93579 | } |
| 93076 | 93580 | p->pNewUnpacked = pUnpack; |
| @@ -93860,13 +94364,13 @@ | ||
| 93860 | 94364 | */ |
| 93861 | 94365 | Mem *pMem = iCur>0 ? &p->aMem[p->nMem-iCur] : p->aMem; |
| 93862 | 94366 | |
| 93863 | 94367 | i64 nByte; |
| 93864 | 94368 | VdbeCursor *pCx = 0; |
| 93865 | - nByte = | |
| 93866 | - ROUND8P(sizeof(VdbeCursor)) + 2*sizeof(u32)*nField + | |
| 93867 | - (eCurType==CURTYPE_BTREE?sqlite3BtreeCursorSize():0); | |
| 94369 | + nByte = SZ_VDBECURSOR(nField); | |
| 94370 | + assert( ROUND8(nByte)==nByte ); | |
| 94371 | + if( eCurType==CURTYPE_BTREE ) nByte += sqlite3BtreeCursorSize(); | |
| 93868 | 94372 | |
| 93869 | 94373 | assert( iCur>=0 && iCur<p->nCursor ); |
| 93870 | 94374 | if( p->apCsr[iCur] ){ /*OPTIMIZATION-IF-FALSE*/ |
| 93871 | 94375 | sqlite3VdbeFreeCursorNN(p, p->apCsr[iCur]); |
| 93872 | 94376 | p->apCsr[iCur] = 0; |
| @@ -93895,12 +94399,12 @@ | ||
| 93895 | 94399 | memset(pCx, 0, offsetof(VdbeCursor,pAltCursor)); |
| 93896 | 94400 | pCx->eCurType = eCurType; |
| 93897 | 94401 | pCx->nField = nField; |
| 93898 | 94402 | pCx->aOffset = &pCx->aType[nField]; |
| 93899 | 94403 | if( eCurType==CURTYPE_BTREE ){ |
| 93900 | - pCx->uc.pCursor = (BtCursor*) | |
| 93901 | - &pMem->z[ROUND8P(sizeof(VdbeCursor))+2*sizeof(u32)*nField]; | |
| 94404 | + assert( ROUND8(SZ_VDBECURSOR(nField))==SZ_VDBECURSOR(nField) ); | |
| 94405 | + pCx->uc.pCursor = (BtCursor*)&pMem->z[SZ_VDBECURSOR(nField)]; | |
| 93902 | 94406 | sqlite3BtreeCursorZero(pCx->uc.pCursor); |
| 93903 | 94407 | } |
| 93904 | 94408 | return pCx; |
| 93905 | 94409 | } |
| 93906 | 94410 | |
| @@ -99638,11 +100142,11 @@ | ||
| 99638 | 100142 | pCrsr = pC->uc.pCursor; |
| 99639 | 100143 | |
| 99640 | 100144 | /* The OP_RowData opcodes always follow OP_NotExists or |
| 99641 | 100145 | ** OP_SeekRowid or OP_Rewind/Op_Next with no intervening instructions |
| 99642 | 100146 | ** that might invalidate the cursor. |
| 99643 | - ** If this where not the case, on of the following assert()s | |
| 100147 | + ** If this were not the case, one of the following assert()s | |
| 99644 | 100148 | ** would fail. Should this ever change (because of changes in the code |
| 99645 | 100149 | ** generator) then the fix would be to insert a call to |
| 99646 | 100150 | ** sqlite3VdbeCursorMoveto(). |
| 99647 | 100151 | */ |
| 99648 | 100152 | assert( pC->deferredMoveto==0 ); |
| @@ -101287,11 +101791,11 @@ | ||
| 101287 | 101791 | ** cell in which to store the accumulation. Be careful that the memory |
| 101288 | 101792 | ** cell is 8-byte aligned, even on platforms where a pointer is 32-bits. |
| 101289 | 101793 | ** |
| 101290 | 101794 | ** Note: We could avoid this by using a regular memory cell from aMem[] for |
| 101291 | 101795 | ** the accumulator, instead of allocating one here. */ |
| 101292 | - nAlloc = ROUND8P( sizeof(pCtx[0]) + (n-1)*sizeof(sqlite3_value*) ); | |
| 101796 | + nAlloc = ROUND8P( SZ_CONTEXT(n) ); | |
| 101293 | 101797 | pCtx = sqlite3DbMallocRawNN(db, nAlloc + sizeof(Mem)); |
| 101294 | 101798 | if( pCtx==0 ) goto no_mem; |
| 101295 | 101799 | pCtx->pOut = (Mem*)((u8*)pCtx + nAlloc); |
| 101296 | 101800 | assert( EIGHT_BYTE_ALIGNMENT(pCtx->pOut) ); |
| 101297 | 101801 | |
| @@ -102945,10 +103449,11 @@ | ||
| 102945 | 103449 | int iCol; /* Index of zColumn in row-record */ |
| 102946 | 103450 | int rc = SQLITE_OK; |
| 102947 | 103451 | char *zErr = 0; |
| 102948 | 103452 | Table *pTab; |
| 102949 | 103453 | Incrblob *pBlob = 0; |
| 103454 | + int iDb; | |
| 102950 | 103455 | Parse sParse; |
| 102951 | 103456 | |
| 102952 | 103457 | #ifdef SQLITE_ENABLE_API_ARMOR |
| 102953 | 103458 | if( ppBlob==0 ){ |
| 102954 | 103459 | return SQLITE_MISUSE_BKPT; |
| @@ -102990,11 +103495,14 @@ | ||
| 102990 | 103495 | if( pTab && IsView(pTab) ){ |
| 102991 | 103496 | pTab = 0; |
| 102992 | 103497 | sqlite3ErrorMsg(&sParse, "cannot open view: %s", zTable); |
| 102993 | 103498 | } |
| 102994 | 103499 | #endif |
| 102995 | - if( !pTab ){ | |
| 103500 | + if( pTab==0 | |
| 103501 | + || ((iDb = sqlite3SchemaToIndex(db, pTab->pSchema))==1 && | |
| 103502 | + sqlite3OpenTempDatabase(&sParse)) | |
| 103503 | + ){ | |
| 102996 | 103504 | if( sParse.zErrMsg ){ |
| 102997 | 103505 | sqlite3DbFree(db, zErr); |
| 102998 | 103506 | zErr = sParse.zErrMsg; |
| 102999 | 103507 | sParse.zErrMsg = 0; |
| 103000 | 103508 | } |
| @@ -103001,11 +103509,11 @@ | ||
| 103001 | 103509 | rc = SQLITE_ERROR; |
| 103002 | 103510 | sqlite3BtreeLeaveAll(db); |
| 103003 | 103511 | goto blob_open_out; |
| 103004 | 103512 | } |
| 103005 | 103513 | pBlob->pTab = pTab; |
| 103006 | - pBlob->zDb = db->aDb[sqlite3SchemaToIndex(db, pTab->pSchema)].zDbSName; | |
| 103514 | + pBlob->zDb = db->aDb[iDb].zDbSName; | |
| 103007 | 103515 | |
| 103008 | 103516 | /* Now search pTab for the exact column. */ |
| 103009 | 103517 | iCol = sqlite3ColumnIndex(pTab, zColumn); |
| 103010 | 103518 | if( iCol<0 ){ |
| 103011 | 103519 | sqlite3DbFree(db, zErr); |
| @@ -103085,11 +103593,10 @@ | ||
| 103085 | 103593 | {OP_Column, 0, 0, 1}, /* 3 */ |
| 103086 | 103594 | {OP_ResultRow, 1, 0, 0}, /* 4 */ |
| 103087 | 103595 | {OP_Halt, 0, 0, 0}, /* 5 */ |
| 103088 | 103596 | }; |
| 103089 | 103597 | Vdbe *v = (Vdbe *)pBlob->pStmt; |
| 103090 | - int iDb = sqlite3SchemaToIndex(db, pTab->pSchema); | |
| 103091 | 103598 | VdbeOp *aOp; |
| 103092 | 103599 | |
| 103093 | 103600 | sqlite3VdbeAddOp4Int(v, OP_Transaction, iDb, wrFlag, |
| 103094 | 103601 | pTab->pSchema->schema_cookie, |
| 103095 | 103602 | pTab->pSchema->iGeneration); |
| @@ -103663,12 +104170,15 @@ | ||
| 103663 | 104170 | u8 bUsePMA; /* True if one or more PMAs created */ |
| 103664 | 104171 | u8 bUseThreads; /* True to use background threads */ |
| 103665 | 104172 | u8 iPrev; /* Previous thread used to flush PMA */ |
| 103666 | 104173 | u8 nTask; /* Size of aTask[] array */ |
| 103667 | 104174 | u8 typeMask; |
| 103668 | - SortSubtask aTask[1]; /* One or more subtasks */ | |
| 104175 | + SortSubtask aTask[FLEXARRAY]; /* One or more subtasks */ | |
| 103669 | 104176 | }; |
| 104177 | + | |
| 104178 | +/* Size (in bytes) of a VdbeSorter object that works with N or fewer subtasks */ | |
| 104179 | +#define SZ_VDBESORTER(N) (offsetof(VdbeSorter,aTask)+(N)*sizeof(SortSubtask)) | |
| 103670 | 104180 | |
| 103671 | 104181 | #define SORTER_TYPE_INTEGER 0x01 |
| 103672 | 104182 | #define SORTER_TYPE_TEXT 0x02 |
| 103673 | 104183 | |
| 103674 | 104184 | /* |
| @@ -104297,12 +104807,12 @@ | ||
| 104297 | 104807 | assert( pCsr->pKeyInfo ); |
| 104298 | 104808 | assert( !pCsr->isEphemeral ); |
| 104299 | 104809 | assert( pCsr->eCurType==CURTYPE_SORTER ); |
| 104300 | 104810 | assert( sizeof(KeyInfo) + UMXV(pCsr->pKeyInfo->nKeyField)*sizeof(CollSeq*) |
| 104301 | 104811 | < 0x7fffffff ); |
| 104302 | - szKeyInfo = sizeof(KeyInfo) + (pCsr->pKeyInfo->nKeyField-1)*sizeof(CollSeq*); | |
| 104303 | - sz = sizeof(VdbeSorter) + nWorker * sizeof(SortSubtask); | |
| 104812 | + szKeyInfo = SZ_KEYINFO(pCsr->pKeyInfo->nKeyField+1); | |
| 104813 | + sz = SZ_VDBESORTER(nWorker+1); | |
| 104304 | 104814 | |
| 104305 | 104815 | pSorter = (VdbeSorter*)sqlite3DbMallocZero(db, sz + szKeyInfo); |
| 104306 | 104816 | pCsr->uc.pSorter = pSorter; |
| 104307 | 104817 | if( pSorter==0 ){ |
| 104308 | 104818 | rc = SQLITE_NOMEM_BKPT; |
| @@ -104762,10 +105272,14 @@ | ||
| 104762 | 105272 | } |
| 104763 | 105273 | |
| 104764 | 105274 | p->u.pNext = 0; |
| 104765 | 105275 | for(i=0; aSlot[i]; i++){ |
| 104766 | 105276 | p = vdbeSorterMerge(pTask, p, aSlot[i]); |
| 105277 | + /* ,--Each aSlot[] holds twice as much as the previous. So we cannot use | |
| 105278 | + ** | up all 64 aSlots[] with only a 64-bit address space. | |
| 105279 | + ** v */ | |
| 105280 | + assert( i<ArraySize(aSlot) ); | |
| 104767 | 105281 | aSlot[i] = 0; |
| 104768 | 105282 | } |
| 104769 | 105283 | aSlot[i] = p; |
| 104770 | 105284 | p = pNext; |
| 104771 | 105285 | } |
| @@ -109536,32 +110050,34 @@ | ||
| 109536 | 110050 | Table *pTab, /* The table being referenced, or NULL */ |
| 109537 | 110051 | int type, /* NC_IsCheck, NC_PartIdx, NC_IdxExpr, NC_GenCol, or 0 */ |
| 109538 | 110052 | Expr *pExpr, /* Expression to resolve. May be NULL. */ |
| 109539 | 110053 | ExprList *pList /* Expression list to resolve. May be NULL. */ |
| 109540 | 110054 | ){ |
| 109541 | - SrcList sSrc; /* Fake SrcList for pParse->pNewTable */ | |
| 110055 | + SrcList *pSrc; /* Fake SrcList for pParse->pNewTable */ | |
| 109542 | 110056 | NameContext sNC; /* Name context for pParse->pNewTable */ |
| 109543 | 110057 | int rc; |
| 110058 | + u8 srcSpace[SZ_SRCLIST_1]; /* Memory space for the fake SrcList */ | |
| 109544 | 110059 | |
| 109545 | 110060 | assert( type==0 || pTab!=0 ); |
| 109546 | 110061 | assert( type==NC_IsCheck || type==NC_PartIdx || type==NC_IdxExpr |
| 109547 | 110062 | || type==NC_GenCol || pTab==0 ); |
| 109548 | 110063 | memset(&sNC, 0, sizeof(sNC)); |
| 109549 | - memset(&sSrc, 0, sizeof(sSrc)); | |
| 110064 | + pSrc = (SrcList*)srcSpace; | |
| 110065 | + memset(pSrc, 0, SZ_SRCLIST_1); | |
| 109550 | 110066 | if( pTab ){ |
| 109551 | - sSrc.nSrc = 1; | |
| 109552 | - sSrc.a[0].zName = pTab->zName; | |
| 109553 | - sSrc.a[0].pSTab = pTab; | |
| 109554 | - sSrc.a[0].iCursor = -1; | |
| 110067 | + pSrc->nSrc = 1; | |
| 110068 | + pSrc->a[0].zName = pTab->zName; | |
| 110069 | + pSrc->a[0].pSTab = pTab; | |
| 110070 | + pSrc->a[0].iCursor = -1; | |
| 109555 | 110071 | if( pTab->pSchema!=pParse->db->aDb[1].pSchema ){ |
| 109556 | 110072 | /* Cause EP_FromDDL to be set on TK_FUNCTION nodes of non-TEMP |
| 109557 | 110073 | ** schema elements */ |
| 109558 | 110074 | type |= NC_FromDDL; |
| 109559 | 110075 | } |
| 109560 | 110076 | } |
| 109561 | 110077 | sNC.pParse = pParse; |
| 109562 | - sNC.pSrcList = &sSrc; | |
| 110078 | + sNC.pSrcList = pSrc; | |
| 109563 | 110079 | sNC.ncFlags = type | NC_IsDDL; |
| 109564 | 110080 | if( (rc = sqlite3ResolveExprNames(&sNC, pExpr))!=SQLITE_OK ) return rc; |
| 109565 | 110081 | if( pList ) rc = sqlite3ResolveExprListNames(&sNC, pList); |
| 109566 | 110082 | return rc; |
| 109567 | 110083 | } |
| @@ -111306,11 +111822,11 @@ | ||
| 111306 | 111822 | */ |
| 111307 | 111823 | #ifndef SQLITE_OMIT_CTE |
| 111308 | 111824 | SQLITE_PRIVATE With *sqlite3WithDup(sqlite3 *db, With *p){ |
| 111309 | 111825 | With *pRet = 0; |
| 111310 | 111826 | if( p ){ |
| 111311 | - sqlite3_int64 nByte = sizeof(*p) + sizeof(p->a[0]) * (p->nCte-1); | |
| 111827 | + sqlite3_int64 nByte = SZ_WITH(p->nCte); | |
| 111312 | 111828 | pRet = sqlite3DbMallocZero(db, nByte); |
| 111313 | 111829 | if( pRet ){ |
| 111314 | 111830 | int i; |
| 111315 | 111831 | pRet->nCte = p->nCte; |
| 111316 | 111832 | for(i=0; i<p->nCte; i++){ |
| @@ -111433,15 +111949,13 @@ | ||
| 111433 | 111949 | #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) \ |
| 111434 | 111950 | || !defined(SQLITE_OMIT_SUBQUERY) |
| 111435 | 111951 | SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3 *db, const SrcList *p, int flags){ |
| 111436 | 111952 | SrcList *pNew; |
| 111437 | 111953 | int i; |
| 111438 | - int nByte; | |
| 111439 | 111954 | assert( db!=0 ); |
| 111440 | 111955 | if( p==0 ) return 0; |
| 111441 | - nByte = sizeof(*p) + (p->nSrc>0 ? sizeof(p->a[0]) * (p->nSrc-1) : 0); | |
| 111442 | - pNew = sqlite3DbMallocRawNN(db, nByte ); | |
| 111956 | + pNew = sqlite3DbMallocRawNN(db, SZ_SRCLIST(p->nSrc) ); | |
| 111443 | 111957 | if( pNew==0 ) return 0; |
| 111444 | 111958 | pNew->nSrc = pNew->nAlloc = p->nSrc; |
| 111445 | 111959 | for(i=0; i<p->nSrc; i++){ |
| 111446 | 111960 | SrcItem *pNewItem = &pNew->a[i]; |
| 111447 | 111961 | const SrcItem *pOldItem = &p->a[i]; |
| @@ -111499,11 +112013,11 @@ | ||
| 111499 | 112013 | SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3 *db, const IdList *p){ |
| 111500 | 112014 | IdList *pNew; |
| 111501 | 112015 | int i; |
| 111502 | 112016 | assert( db!=0 ); |
| 111503 | 112017 | if( p==0 ) return 0; |
| 111504 | - pNew = sqlite3DbMallocRawNN(db, sizeof(*pNew)+(p->nId-1)*sizeof(p->a[0]) ); | |
| 112018 | + pNew = sqlite3DbMallocRawNN(db, SZ_IDLIST(p->nId)); | |
| 111505 | 112019 | if( pNew==0 ) return 0; |
| 111506 | 112020 | pNew->nId = p->nId; |
| 111507 | 112021 | for(i=0; i<p->nId; i++){ |
| 111508 | 112022 | struct IdList_item *pNewItem = &pNew->a[i]; |
| 111509 | 112023 | const struct IdList_item *pOldItem = &p->a[i]; |
| @@ -111531,11 +112045,11 @@ | ||
| 111531 | 112045 | pNew->pNext = pNext; |
| 111532 | 112046 | pNew->pPrior = 0; |
| 111533 | 112047 | pNew->pLimit = sqlite3ExprDup(db, p->pLimit, flags); |
| 111534 | 112048 | pNew->iLimit = 0; |
| 111535 | 112049 | pNew->iOffset = 0; |
| 111536 | - pNew->selFlags = p->selFlags & ~SF_UsesEphemeral; | |
| 112050 | + pNew->selFlags = p->selFlags & ~(u32)SF_UsesEphemeral; | |
| 111537 | 112051 | pNew->addrOpenEphm[0] = -1; |
| 111538 | 112052 | pNew->addrOpenEphm[1] = -1; |
| 111539 | 112053 | pNew->nSelectRow = p->nSelectRow; |
| 111540 | 112054 | pNew->pWith = sqlite3WithDup(db, p->pWith); |
| 111541 | 112055 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| @@ -111583,11 +112097,11 @@ | ||
| 111583 | 112097 | Expr *pExpr /* Expression to be appended. Might be NULL */ |
| 111584 | 112098 | ){ |
| 111585 | 112099 | struct ExprList_item *pItem; |
| 111586 | 112100 | ExprList *pList; |
| 111587 | 112101 | |
| 111588 | - pList = sqlite3DbMallocRawNN(db, sizeof(ExprList)+sizeof(pList->a[0])*4 ); | |
| 112102 | + pList = sqlite3DbMallocRawNN(db, SZ_EXPRLIST(4)); | |
| 111589 | 112103 | if( pList==0 ){ |
| 111590 | 112104 | sqlite3ExprDelete(db, pExpr); |
| 111591 | 112105 | return 0; |
| 111592 | 112106 | } |
| 111593 | 112107 | pList->nAlloc = 4; |
| @@ -111603,12 +112117,11 @@ | ||
| 111603 | 112117 | Expr *pExpr /* Expression to be appended. Might be NULL */ |
| 111604 | 112118 | ){ |
| 111605 | 112119 | struct ExprList_item *pItem; |
| 111606 | 112120 | ExprList *pNew; |
| 111607 | 112121 | pList->nAlloc *= 2; |
| 111608 | - pNew = sqlite3DbRealloc(db, pList, | |
| 111609 | - sizeof(*pList)+(pList->nAlloc-1)*sizeof(pList->a[0])); | |
| 112122 | + pNew = sqlite3DbRealloc(db, pList, SZ_EXPRLIST(pList->nAlloc)); | |
| 111610 | 112123 | if( pNew==0 ){ |
| 111611 | 112124 | sqlite3ExprListDelete(db, pList); |
| 111612 | 112125 | sqlite3ExprDelete(db, pExpr); |
| 111613 | 112126 | return 0; |
| 111614 | 112127 | }else{ |
| @@ -114240,11 +114753,11 @@ | ||
| 114240 | 114753 | return -1; /* Not found */ |
| 114241 | 114754 | } |
| 114242 | 114755 | |
| 114243 | 114756 | |
| 114244 | 114757 | /* |
| 114245 | -** Expresion pExpr is guaranteed to be a TK_COLUMN or equivalent. This | |
| 114758 | +** Expression pExpr is guaranteed to be a TK_COLUMN or equivalent. This | |
| 114246 | 114759 | ** function checks the Parse.pIdxPartExpr list to see if this column |
| 114247 | 114760 | ** can be replaced with a constant value. If so, it generates code to |
| 114248 | 114761 | ** put the constant value in a register (ideally, but not necessarily, |
| 114249 | 114762 | ** register iTarget) and returns the register number. |
| 114250 | 114763 | ** |
| @@ -117481,17 +117994,17 @@ | ||
| 117481 | 117994 | pNew->nTabRef = 1; |
| 117482 | 117995 | pNew->nCol = pTab->nCol; |
| 117483 | 117996 | assert( pNew->nCol>0 ); |
| 117484 | 117997 | nAlloc = (((pNew->nCol-1)/8)*8)+8; |
| 117485 | 117998 | assert( nAlloc>=pNew->nCol && nAlloc%8==0 && nAlloc-pNew->nCol<8 ); |
| 117486 | - pNew->aCol = (Column*)sqlite3DbMallocZero(db, sizeof(Column)*nAlloc); | |
| 117999 | + pNew->aCol = (Column*)sqlite3DbMallocZero(db, sizeof(Column)*(u32)nAlloc); | |
| 117487 | 118000 | pNew->zName = sqlite3MPrintf(db, "sqlite_altertab_%s", pTab->zName); |
| 117488 | 118001 | if( !pNew->aCol || !pNew->zName ){ |
| 117489 | 118002 | assert( db->mallocFailed ); |
| 117490 | 118003 | goto exit_begin_add_column; |
| 117491 | 118004 | } |
| 117492 | - memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*pNew->nCol); | |
| 118005 | + memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*(size_t)pNew->nCol); | |
| 117493 | 118006 | for(i=0; i<pNew->nCol; i++){ |
| 117494 | 118007 | Column *pCol = &pNew->aCol[i]; |
| 117495 | 118008 | pCol->zCnName = sqlite3DbStrDup(db, pCol->zCnName); |
| 117496 | 118009 | pCol->hName = sqlite3StrIHash(pCol->zCnName); |
| 117497 | 118010 | } |
| @@ -118086,23 +118599,34 @@ | ||
| 118086 | 118599 | sqlite3 *db, /* Database handle */ |
| 118087 | 118600 | const char *zSql, /* SQL to parse */ |
| 118088 | 118601 | int bTemp /* True if SQL is from temp schema */ |
| 118089 | 118602 | ){ |
| 118090 | 118603 | int rc; |
| 118604 | + u64 flags; | |
| 118091 | 118605 | |
| 118092 | 118606 | sqlite3ParseObjectInit(p, db); |
| 118093 | 118607 | if( zSql==0 ){ |
| 118094 | 118608 | return SQLITE_NOMEM; |
| 118095 | 118609 | } |
| 118096 | 118610 | if( sqlite3StrNICmp(zSql,"CREATE ",7)!=0 ){ |
| 118097 | 118611 | return SQLITE_CORRUPT_BKPT; |
| 118098 | 118612 | } |
| 118099 | - db->init.iDb = bTemp ? 1 : sqlite3FindDbName(db, zDb); | |
| 118613 | + if( bTemp ){ | |
| 118614 | + db->init.iDb = 1; | |
| 118615 | + }else{ | |
| 118616 | + int iDb = sqlite3FindDbName(db, zDb); | |
| 118617 | + assert( iDb>=0 && iDb<=0xff ); | |
| 118618 | + db->init.iDb = (u8)iDb; | |
| 118619 | + } | |
| 118100 | 118620 | p->eParseMode = PARSE_MODE_RENAME; |
| 118101 | 118621 | p->db = db; |
| 118102 | 118622 | p->nQueryLoop = 1; |
| 118623 | + flags = db->flags; | |
| 118624 | + testcase( (db->flags & SQLITE_Comments)==0 && strstr(zSql," /* ")!=0 ); | |
| 118625 | + db->flags |= SQLITE_Comments; | |
| 118103 | 118626 | rc = sqlite3RunParser(p, zSql); |
| 118627 | + db->flags = flags; | |
| 118104 | 118628 | if( db->mallocFailed ) rc = SQLITE_NOMEM; |
| 118105 | 118629 | if( rc==SQLITE_OK |
| 118106 | 118630 | && NEVER(p->pNewTable==0 && p->pNewIndex==0 && p->pNewTrigger==0) |
| 118107 | 118631 | ){ |
| 118108 | 118632 | rc = SQLITE_CORRUPT_BKPT; |
| @@ -118161,14 +118685,15 @@ | ||
| 118161 | 118685 | return SQLITE_NOMEM; |
| 118162 | 118686 | }else{ |
| 118163 | 118687 | nQuot = sqlite3Strlen30(zQuot)-1; |
| 118164 | 118688 | } |
| 118165 | 118689 | |
| 118166 | - assert( nQuot>=nNew ); | |
| 118167 | - zOut = sqlite3DbMallocZero(db, nSql + pRename->nList*nQuot + 1); | |
| 118690 | + assert( nQuot>=nNew && nSql>=0 && nNew>=0 ); | |
| 118691 | + zOut = sqlite3DbMallocZero(db, (u64)(nSql + pRename->nList*nQuot + 1)); | |
| 118168 | 118692 | }else{ |
| 118169 | - zOut = (char*)sqlite3DbMallocZero(db, (nSql*2+1) * 3); | |
| 118693 | + assert( nSql>0 ); | |
| 118694 | + zOut = (char*)sqlite3DbMallocZero(db, (u64)(nSql*2+1) * 3); | |
| 118170 | 118695 | if( zOut ){ |
| 118171 | 118696 | zBuf1 = &zOut[nSql*2+1]; |
| 118172 | 118697 | zBuf2 = &zOut[nSql*4+2]; |
| 118173 | 118698 | } |
| 118174 | 118699 | } |
| @@ -118176,20 +118701,21 @@ | ||
| 118176 | 118701 | /* At this point pRename->pList contains a list of RenameToken objects |
| 118177 | 118702 | ** corresponding to all tokens in the input SQL that must be replaced |
| 118178 | 118703 | ** with the new column name, or with single-quoted versions of themselves. |
| 118179 | 118704 | ** All that remains is to construct and return the edited SQL string. */ |
| 118180 | 118705 | if( zOut ){ |
| 118181 | - int nOut = nSql; | |
| 118182 | - memcpy(zOut, zSql, nSql); | |
| 118706 | + i64 nOut = nSql; | |
| 118707 | + assert( nSql>0 ); | |
| 118708 | + memcpy(zOut, zSql, (size_t)nSql); | |
| 118183 | 118709 | while( pRename->pList ){ |
| 118184 | 118710 | int iOff; /* Offset of token to replace in zOut */ |
| 118185 | - u32 nReplace; | |
| 118711 | + i64 nReplace; | |
| 118186 | 118712 | const char *zReplace; |
| 118187 | 118713 | RenameToken *pBest = renameColumnTokenNext(pRename); |
| 118188 | 118714 | |
| 118189 | 118715 | if( zNew ){ |
| 118190 | - if( bQuote==0 && sqlite3IsIdChar(*pBest->t.z) ){ | |
| 118716 | + if( bQuote==0 && sqlite3IsIdChar(*(u8*)pBest->t.z) ){ | |
| 118191 | 118717 | nReplace = nNew; |
| 118192 | 118718 | zReplace = zNew; |
| 118193 | 118719 | }else{ |
| 118194 | 118720 | nReplace = nQuot; |
| 118195 | 118721 | zReplace = zQuot; |
| @@ -118203,18 +118729,19 @@ | ||
| 118203 | 118729 | ** token. This is so that (SELECT "string"'alias') maps to |
| 118204 | 118730 | ** (SELECT 'string' 'alias'), and not (SELECT 'string''alias'). */ |
| 118205 | 118731 | memcpy(zBuf1, pBest->t.z, pBest->t.n); |
| 118206 | 118732 | zBuf1[pBest->t.n] = 0; |
| 118207 | 118733 | sqlite3Dequote(zBuf1); |
| 118208 | - sqlite3_snprintf(nSql*2, zBuf2, "%Q%s", zBuf1, | |
| 118734 | + assert( nSql < 0x15555554 /* otherwise malloc would have failed */ ); | |
| 118735 | + sqlite3_snprintf((int)(nSql*2), zBuf2, "%Q%s", zBuf1, | |
| 118209 | 118736 | pBest->t.z[pBest->t.n]=='\'' ? " " : "" |
| 118210 | 118737 | ); |
| 118211 | 118738 | zReplace = zBuf2; |
| 118212 | 118739 | nReplace = sqlite3Strlen30(zReplace); |
| 118213 | 118740 | } |
| 118214 | 118741 | |
| 118215 | - iOff = pBest->t.z - zSql; | |
| 118742 | + iOff = (int)(pBest->t.z - zSql); | |
| 118216 | 118743 | if( pBest->t.n!=nReplace ){ |
| 118217 | 118744 | memmove(&zOut[iOff + nReplace], &zOut[iOff + pBest->t.n], |
| 118218 | 118745 | nOut - (iOff + pBest->t.n) |
| 118219 | 118746 | ); |
| 118220 | 118747 | nOut += nReplace - pBest->t.n; |
| @@ -118236,15 +118763,16 @@ | ||
| 118236 | 118763 | |
| 118237 | 118764 | /* |
| 118238 | 118765 | ** Set all pEList->a[].fg.eEName fields in the expression-list to val. |
| 118239 | 118766 | */ |
| 118240 | 118767 | static void renameSetENames(ExprList *pEList, int val){ |
| 118768 | + assert( val==ENAME_NAME || val==ENAME_TAB || val==ENAME_SPAN ); | |
| 118241 | 118769 | if( pEList ){ |
| 118242 | 118770 | int i; |
| 118243 | 118771 | for(i=0; i<pEList->nExpr; i++){ |
| 118244 | 118772 | assert( val==ENAME_NAME || pEList->a[i].fg.eEName==ENAME_NAME ); |
| 118245 | - pEList->a[i].fg.eEName = val; | |
| 118773 | + pEList->a[i].fg.eEName = val&0x3; | |
| 118246 | 118774 | } |
| 118247 | 118775 | } |
| 118248 | 118776 | } |
| 118249 | 118777 | |
| 118250 | 118778 | /* |
| @@ -118497,11 +119025,11 @@ | ||
| 118497 | 119025 | sCtx.pTab = pTab; |
| 118498 | 119026 | if( rc!=SQLITE_OK ) goto renameColumnFunc_done; |
| 118499 | 119027 | if( sParse.pNewTable ){ |
| 118500 | 119028 | if( IsView(sParse.pNewTable) ){ |
| 118501 | 119029 | Select *pSelect = sParse.pNewTable->u.view.pSelect; |
| 118502 | - pSelect->selFlags &= ~SF_View; | |
| 119030 | + pSelect->selFlags &= ~(u32)SF_View; | |
| 118503 | 119031 | sParse.rc = SQLITE_OK; |
| 118504 | 119032 | sqlite3SelectPrep(&sParse, pSelect, 0); |
| 118505 | 119033 | rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc); |
| 118506 | 119034 | if( rc==SQLITE_OK ){ |
| 118507 | 119035 | sqlite3WalkSelect(&sWalker, pSelect); |
| @@ -118715,11 +119243,11 @@ | ||
| 118715 | 119243 | NameContext sNC; |
| 118716 | 119244 | memset(&sNC, 0, sizeof(sNC)); |
| 118717 | 119245 | sNC.pParse = &sParse; |
| 118718 | 119246 | |
| 118719 | 119247 | assert( pSelect->selFlags & SF_View ); |
| 118720 | - pSelect->selFlags &= ~SF_View; | |
| 119248 | + pSelect->selFlags &= ~(u32)SF_View; | |
| 118721 | 119249 | sqlite3SelectPrep(&sParse, pTab->u.view.pSelect, &sNC); |
| 118722 | 119250 | if( sParse.nErr ){ |
| 118723 | 119251 | rc = sParse.rc; |
| 118724 | 119252 | }else{ |
| 118725 | 119253 | sqlite3WalkSelect(&sWalker, pTab->u.view.pSelect); |
| @@ -118888,11 +119416,11 @@ | ||
| 118888 | 119416 | sWalker.u.pRename = &sCtx; |
| 118889 | 119417 | |
| 118890 | 119418 | if( sParse.pNewTable ){ |
| 118891 | 119419 | if( IsView(sParse.pNewTable) ){ |
| 118892 | 119420 | Select *pSelect = sParse.pNewTable->u.view.pSelect; |
| 118893 | - pSelect->selFlags &= ~SF_View; | |
| 119421 | + pSelect->selFlags &= ~(u32)SF_View; | |
| 118894 | 119422 | sParse.rc = SQLITE_OK; |
| 118895 | 119423 | sqlite3SelectPrep(&sParse, pSelect, 0); |
| 118896 | 119424 | rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc); |
| 118897 | 119425 | if( rc==SQLITE_OK ){ |
| 118898 | 119426 | sqlite3WalkSelect(&sWalker, pSelect); |
| @@ -118987,14 +119515,14 @@ | ||
| 118987 | 119515 | UNUSED_PARAMETER(NotUsed); |
| 118988 | 119516 | |
| 118989 | 119517 | if( zDb && zInput ){ |
| 118990 | 119518 | int rc; |
| 118991 | 119519 | Parse sParse; |
| 118992 | - int flags = db->flags; | |
| 119520 | + u64 flags = db->flags; | |
| 118993 | 119521 | if( bNoDQS ) db->flags &= ~(SQLITE_DqsDML|SQLITE_DqsDDL); |
| 118994 | 119522 | rc = renameParseSql(&sParse, zDb, db, zInput, bTemp); |
| 118995 | - db->flags |= (flags & (SQLITE_DqsDML|SQLITE_DqsDDL)); | |
| 119523 | + db->flags = flags; | |
| 118996 | 119524 | if( rc==SQLITE_OK ){ |
| 118997 | 119525 | if( isLegacy==0 && sParse.pNewTable && IsView(sParse.pNewTable) ){ |
| 118998 | 119526 | NameContext sNC; |
| 118999 | 119527 | memset(&sNC, 0, sizeof(sNC)); |
| 119000 | 119528 | sNC.pParse = &sParse; |
| @@ -119710,11 +120238,11 @@ | ||
| 119710 | 120238 | } |
| 119711 | 120239 | |
| 119712 | 120240 | p->db = db; |
| 119713 | 120241 | p->nEst = sqlite3_value_int64(argv[2]); |
| 119714 | 120242 | p->nRow = 0; |
| 119715 | - p->nLimit = sqlite3_value_int64(argv[3]); | |
| 120243 | + p->nLimit = sqlite3_value_int(argv[3]); | |
| 119716 | 120244 | p->nCol = nCol; |
| 119717 | 120245 | p->nKeyCol = nKeyCol; |
| 119718 | 120246 | p->nSkipAhead = 0; |
| 119719 | 120247 | p->current.anDLt = (tRowcnt*)&p[1]; |
| 119720 | 120248 | |
| @@ -121519,10 +122047,17 @@ | ||
| 121519 | 122047 | */ |
| 121520 | 122048 | if( rc==SQLITE_OK ){ |
| 121521 | 122049 | sqlite3BtreeEnterAll(db); |
| 121522 | 122050 | db->init.iDb = 0; |
| 121523 | 122051 | db->mDbFlags &= ~(DBFLAG_SchemaKnownOk); |
| 122052 | +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT | |
| 122053 | + if( db->setlkFlags & SQLITE_SETLK_BLOCK_ON_CONNECT ){ | |
| 122054 | + int val = 1; | |
| 122055 | + sqlite3_file *fd = sqlite3PagerFile(sqlite3BtreePager(pNew->pBt)); | |
| 122056 | + sqlite3OsFileControlHint(fd, SQLITE_FCNTL_BLOCK_ON_CONNECT, &val); | |
| 122057 | + } | |
| 122058 | +#endif | |
| 121524 | 122059 | if( !REOPEN_AS_MEMDB(db) ){ |
| 121525 | 122060 | rc = sqlite3Init(db, &zErrDyn); |
| 121526 | 122061 | } |
| 121527 | 122062 | sqlite3BtreeLeaveAll(db); |
| 121528 | 122063 | assert( zErrDyn==0 || rc!=SQLITE_OK ); |
| @@ -123240,14 +123775,20 @@ | ||
| 123240 | 123775 | ** Convert an table column number into a index column number. That is, |
| 123241 | 123776 | ** for the column iCol in the table (as defined by the CREATE TABLE statement) |
| 123242 | 123777 | ** find the (first) offset of that column in index pIdx. Or return -1 |
| 123243 | 123778 | ** if column iCol is not used in index pIdx. |
| 123244 | 123779 | */ |
| 123245 | -SQLITE_PRIVATE i16 sqlite3TableColumnToIndex(Index *pIdx, i16 iCol){ | |
| 123780 | +SQLITE_PRIVATE int sqlite3TableColumnToIndex(Index *pIdx, int iCol){ | |
| 123246 | 123781 | int i; |
| 123782 | + i16 iCol16; | |
| 123783 | + assert( iCol>=(-1) && iCol<=SQLITE_MAX_COLUMN ); | |
| 123784 | + assert( pIdx->nColumn<=SQLITE_MAX_COLUMN ); | |
| 123785 | + iCol16 = iCol; | |
| 123247 | 123786 | for(i=0; i<pIdx->nColumn; i++){ |
| 123248 | - if( iCol==pIdx->aiColumn[i] ) return i; | |
| 123787 | + if( iCol16==pIdx->aiColumn[i] ){ | |
| 123788 | + return i; | |
| 123789 | + } | |
| 123249 | 123790 | } |
| 123250 | 123791 | return -1; |
| 123251 | 123792 | } |
| 123252 | 123793 | |
| 123253 | 123794 | #ifndef SQLITE_OMIT_GENERATED_COLUMNS |
| @@ -124340,16 +124881,21 @@ | ||
| 124340 | 124881 | |
| 124341 | 124882 | /* |
| 124342 | 124883 | ** Resize an Index object to hold N columns total. Return SQLITE_OK |
| 124343 | 124884 | ** on success and SQLITE_NOMEM on an OOM error. |
| 124344 | 124885 | */ |
| 124345 | -static int resizeIndexObject(sqlite3 *db, Index *pIdx, int N){ | |
| 124886 | +static int resizeIndexObject(Parse *pParse, Index *pIdx, int N){ | |
| 124346 | 124887 | char *zExtra; |
| 124347 | - int nByte; | |
| 124888 | + u64 nByte; | |
| 124889 | + sqlite3 *db; | |
| 124348 | 124890 | if( pIdx->nColumn>=N ) return SQLITE_OK; |
| 124891 | + db = pParse->db; | |
| 124892 | + assert( N>0 ); | |
| 124893 | + assert( N <= SQLITE_MAX_COLUMN*2 /* tag-20250221-1 */ ); | |
| 124894 | + testcase( N==2*pParse->db->aLimit[SQLITE_LIMIT_COLUMN] ); | |
| 124349 | 124895 | assert( pIdx->isResized==0 ); |
| 124350 | - nByte = (sizeof(char*) + sizeof(LogEst) + sizeof(i16) + 1)*N; | |
| 124896 | + nByte = (sizeof(char*) + sizeof(LogEst) + sizeof(i16) + 1)*(u64)N; | |
| 124351 | 124897 | zExtra = sqlite3DbMallocZero(db, nByte); |
| 124352 | 124898 | if( zExtra==0 ) return SQLITE_NOMEM_BKPT; |
| 124353 | 124899 | memcpy(zExtra, pIdx->azColl, sizeof(char*)*pIdx->nColumn); |
| 124354 | 124900 | pIdx->azColl = (const char**)zExtra; |
| 124355 | 124901 | zExtra += sizeof(char*)*N; |
| @@ -124359,11 +124905,11 @@ | ||
| 124359 | 124905 | memcpy(zExtra, pIdx->aiColumn, sizeof(i16)*pIdx->nColumn); |
| 124360 | 124906 | pIdx->aiColumn = (i16*)zExtra; |
| 124361 | 124907 | zExtra += sizeof(i16)*N; |
| 124362 | 124908 | memcpy(zExtra, pIdx->aSortOrder, pIdx->nColumn); |
| 124363 | 124909 | pIdx->aSortOrder = (u8*)zExtra; |
| 124364 | - pIdx->nColumn = N; | |
| 124910 | + pIdx->nColumn = (u16)N; /* See tag-20250221-1 above for proof of safety */ | |
| 124365 | 124911 | pIdx->isResized = 1; |
| 124366 | 124912 | return SQLITE_OK; |
| 124367 | 124913 | } |
| 124368 | 124914 | |
| 124369 | 124915 | /* |
| @@ -124613,11 +125159,11 @@ | ||
| 124613 | 125159 | if( n==0 ){ |
| 124614 | 125160 | /* This index is a superset of the primary key */ |
| 124615 | 125161 | pIdx->nColumn = pIdx->nKeyCol; |
| 124616 | 125162 | continue; |
| 124617 | 125163 | } |
| 124618 | - if( resizeIndexObject(db, pIdx, pIdx->nKeyCol+n) ) return; | |
| 125164 | + if( resizeIndexObject(pParse, pIdx, pIdx->nKeyCol+n) ) return; | |
| 124619 | 125165 | for(i=0, j=pIdx->nKeyCol; i<nPk; i++){ |
| 124620 | 125166 | if( !isDupColumn(pIdx, pIdx->nKeyCol, pPk, i) ){ |
| 124621 | 125167 | testcase( hasColumn(pIdx->aiColumn, pIdx->nKeyCol, pPk->aiColumn[i]) ); |
| 124622 | 125168 | pIdx->aiColumn[j] = pPk->aiColumn[i]; |
| 124623 | 125169 | pIdx->azColl[j] = pPk->azColl[i]; |
| @@ -124637,11 +125183,11 @@ | ||
| 124637 | 125183 | nExtra = 0; |
| 124638 | 125184 | for(i=0; i<pTab->nCol; i++){ |
| 124639 | 125185 | if( !hasColumn(pPk->aiColumn, nPk, i) |
| 124640 | 125186 | && (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 ) nExtra++; |
| 124641 | 125187 | } |
| 124642 | - if( resizeIndexObject(db, pPk, nPk+nExtra) ) return; | |
| 125188 | + if( resizeIndexObject(pParse, pPk, nPk+nExtra) ) return; | |
| 124643 | 125189 | for(i=0, j=nPk; i<pTab->nCol; i++){ |
| 124644 | 125190 | if( !hasColumn(pPk->aiColumn, j, i) |
| 124645 | 125191 | && (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 |
| 124646 | 125192 | ){ |
| 124647 | 125193 | assert( j<pPk->nColumn ); |
| @@ -125795,11 +126341,11 @@ | ||
| 125795 | 126341 | "columns in the referenced table"); |
| 125796 | 126342 | goto fk_end; |
| 125797 | 126343 | }else{ |
| 125798 | 126344 | nCol = pFromCol->nExpr; |
| 125799 | 126345 | } |
| 125800 | - nByte = sizeof(*pFKey) + (nCol-1)*sizeof(pFKey->aCol[0]) + pTo->n + 1; | |
| 126346 | + nByte = SZ_FKEY(nCol) + pTo->n + 1; | |
| 125801 | 126347 | if( pToCol ){ |
| 125802 | 126348 | for(i=0; i<pToCol->nExpr; i++){ |
| 125803 | 126349 | nByte += sqlite3Strlen30(pToCol->a[i].zEName) + 1; |
| 125804 | 126350 | } |
| 125805 | 126351 | } |
| @@ -126021,17 +126567,18 @@ | ||
| 126021 | 126567 | ** of 8-byte aligned space after the Index object and return a |
| 126022 | 126568 | ** pointer to this extra space in *ppExtra. |
| 126023 | 126569 | */ |
| 126024 | 126570 | SQLITE_PRIVATE Index *sqlite3AllocateIndexObject( |
| 126025 | 126571 | sqlite3 *db, /* Database connection */ |
| 126026 | - i16 nCol, /* Total number of columns in the index */ | |
| 126572 | + int nCol, /* Total number of columns in the index */ | |
| 126027 | 126573 | int nExtra, /* Number of bytes of extra space to alloc */ |
| 126028 | 126574 | char **ppExtra /* Pointer to the "extra" space */ |
| 126029 | 126575 | ){ |
| 126030 | 126576 | Index *p; /* Allocated index object */ |
| 126031 | 126577 | i64 nByte; /* Bytes of space for Index object + arrays */ |
| 126032 | 126578 | |
| 126579 | + assert( nCol <= 2*db->aLimit[SQLITE_LIMIT_COLUMN] ); | |
| 126033 | 126580 | nByte = ROUND8(sizeof(Index)) + /* Index structure */ |
| 126034 | 126581 | ROUND8(sizeof(char*)*nCol) + /* Index.azColl */ |
| 126035 | 126582 | ROUND8(sizeof(LogEst)*(nCol+1) + /* Index.aiRowLogEst */ |
| 126036 | 126583 | sizeof(i16)*nCol + /* Index.aiColumn */ |
| 126037 | 126584 | sizeof(u8)*nCol); /* Index.aSortOrder */ |
| @@ -126040,12 +126587,13 @@ | ||
| 126040 | 126587 | char *pExtra = ((char*)p)+ROUND8(sizeof(Index)); |
| 126041 | 126588 | p->azColl = (const char**)pExtra; pExtra += ROUND8(sizeof(char*)*nCol); |
| 126042 | 126589 | p->aiRowLogEst = (LogEst*)pExtra; pExtra += sizeof(LogEst)*(nCol+1); |
| 126043 | 126590 | p->aiColumn = (i16*)pExtra; pExtra += sizeof(i16)*nCol; |
| 126044 | 126591 | p->aSortOrder = (u8*)pExtra; |
| 126045 | - p->nColumn = nCol; | |
| 126046 | - p->nKeyCol = nCol - 1; | |
| 126592 | + assert( nCol>0 ); | |
| 126593 | + p->nColumn = (u16)nCol; | |
| 126594 | + p->nKeyCol = (u16)(nCol - 1); | |
| 126047 | 126595 | *ppExtra = ((char*)p) + nByte; |
| 126048 | 126596 | } |
| 126049 | 126597 | return p; |
| 126050 | 126598 | } |
| 126051 | 126599 | |
| @@ -126852,16 +127400,15 @@ | ||
| 126852 | 127400 | */ |
| 126853 | 127401 | SQLITE_PRIVATE IdList *sqlite3IdListAppend(Parse *pParse, IdList *pList, Token *pToken){ |
| 126854 | 127402 | sqlite3 *db = pParse->db; |
| 126855 | 127403 | int i; |
| 126856 | 127404 | if( pList==0 ){ |
| 126857 | - pList = sqlite3DbMallocZero(db, sizeof(IdList) ); | |
| 127405 | + pList = sqlite3DbMallocZero(db, SZ_IDLIST(1)); | |
| 126858 | 127406 | if( pList==0 ) return 0; |
| 126859 | 127407 | }else{ |
| 126860 | 127408 | IdList *pNew; |
| 126861 | - pNew = sqlite3DbRealloc(db, pList, | |
| 126862 | - sizeof(IdList) + pList->nId*sizeof(pList->a)); | |
| 127409 | + pNew = sqlite3DbRealloc(db, pList, SZ_IDLIST(pList->nId+1)); | |
| 126863 | 127410 | if( pNew==0 ){ |
| 126864 | 127411 | sqlite3IdListDelete(db, pList); |
| 126865 | 127412 | return 0; |
| 126866 | 127413 | } |
| 126867 | 127414 | pList = pNew; |
| @@ -126956,12 +127503,11 @@ | ||
| 126956 | 127503 | sqlite3ErrorMsg(pParse, "too many FROM clause terms, max: %d", |
| 126957 | 127504 | SQLITE_MAX_SRCLIST); |
| 126958 | 127505 | return 0; |
| 126959 | 127506 | } |
| 126960 | 127507 | if( nAlloc>SQLITE_MAX_SRCLIST ) nAlloc = SQLITE_MAX_SRCLIST; |
| 126961 | - pNew = sqlite3DbRealloc(db, pSrc, | |
| 126962 | - sizeof(*pSrc) + (nAlloc-1)*sizeof(pSrc->a[0]) ); | |
| 127508 | + pNew = sqlite3DbRealloc(db, pSrc, SZ_SRCLIST(nAlloc)); | |
| 126963 | 127509 | if( pNew==0 ){ |
| 126964 | 127510 | assert( db->mallocFailed ); |
| 126965 | 127511 | return 0; |
| 126966 | 127512 | } |
| 126967 | 127513 | pSrc = pNew; |
| @@ -127032,11 +127578,11 @@ | ||
| 127032 | 127578 | assert( pDatabase==0 || pTable!=0 ); /* Cannot have C without B */ |
| 127033 | 127579 | assert( pParse!=0 ); |
| 127034 | 127580 | assert( pParse->db!=0 ); |
| 127035 | 127581 | db = pParse->db; |
| 127036 | 127582 | if( pList==0 ){ |
| 127037 | - pList = sqlite3DbMallocRawNN(pParse->db, sizeof(SrcList) ); | |
| 127583 | + pList = sqlite3DbMallocRawNN(pParse->db, SZ_SRCLIST(1)); | |
| 127038 | 127584 | if( pList==0 ) return 0; |
| 127039 | 127585 | pList->nAlloc = 1; |
| 127040 | 127586 | pList->nSrc = 1; |
| 127041 | 127587 | memset(&pList->a[0], 0, sizeof(pList->a[0])); |
| 127042 | 127588 | pList->a[0].iCursor = -1; |
| @@ -127918,14 +128464,13 @@ | ||
| 127918 | 128464 | } |
| 127919 | 128465 | } |
| 127920 | 128466 | } |
| 127921 | 128467 | |
| 127922 | 128468 | if( pWith ){ |
| 127923 | - sqlite3_int64 nByte = sizeof(*pWith) + (sizeof(pWith->a[1]) * pWith->nCte); | |
| 127924 | - pNew = sqlite3DbRealloc(db, pWith, nByte); | |
| 128469 | + pNew = sqlite3DbRealloc(db, pWith, SZ_WITH(pWith->nCte+1)); | |
| 127925 | 128470 | }else{ |
| 127926 | - pNew = sqlite3DbMallocZero(db, sizeof(*pWith)); | |
| 128471 | + pNew = sqlite3DbMallocZero(db, SZ_WITH(1)); | |
| 127927 | 128472 | } |
| 127928 | 128473 | assert( (pNew!=0 && zName!=0) || db->mallocFailed ); |
| 127929 | 128474 | |
| 127930 | 128475 | if( db->mallocFailed ){ |
| 127931 | 128476 | sqlite3CteDelete(db, pCte); |
| @@ -130629,11 +131174,11 @@ | ||
| 130629 | 131174 | |
| 130630 | 131175 | /* |
| 130631 | 131176 | ** Append to pStr text that is the SQL literal representation of the |
| 130632 | 131177 | ** value contained in pValue. |
| 130633 | 131178 | */ |
| 130634 | -SQLITE_PRIVATE void sqlite3QuoteValue(StrAccum *pStr, sqlite3_value *pValue){ | |
| 131179 | +SQLITE_PRIVATE void sqlite3QuoteValue(StrAccum *pStr, sqlite3_value *pValue, int bEscape){ | |
| 130635 | 131180 | /* As currently implemented, the string must be initially empty. |
| 130636 | 131181 | ** we might relax this requirement in the future, but that will |
| 130637 | 131182 | ** require enhancements to the implementation. */ |
| 130638 | 131183 | assert( pStr!=0 && pStr->nChar==0 ); |
| 130639 | 131184 | |
| @@ -130677,20 +131222,119 @@ | ||
| 130677 | 131222 | } |
| 130678 | 131223 | break; |
| 130679 | 131224 | } |
| 130680 | 131225 | case SQLITE_TEXT: { |
| 130681 | 131226 | const unsigned char *zArg = sqlite3_value_text(pValue); |
| 130682 | - sqlite3_str_appendf(pStr, "%Q", zArg); | |
| 131227 | + sqlite3_str_appendf(pStr, bEscape ? "%#Q" : "%Q", zArg); | |
| 130683 | 131228 | break; |
| 130684 | 131229 | } |
| 130685 | 131230 | default: { |
| 130686 | 131231 | assert( sqlite3_value_type(pValue)==SQLITE_NULL ); |
| 130687 | 131232 | sqlite3_str_append(pStr, "NULL", 4); |
| 130688 | 131233 | break; |
| 130689 | 131234 | } |
| 130690 | 131235 | } |
| 130691 | 131236 | } |
| 131237 | + | |
| 131238 | +/* | |
| 131239 | +** Return true if z[] begins with N hexadecimal digits, and write | |
| 131240 | +** a decoding of those digits into *pVal. Or return false if any | |
| 131241 | +** one of the first N characters in z[] is not a hexadecimal digit. | |
| 131242 | +*/ | |
| 131243 | +static int isNHex(const char *z, int N, u32 *pVal){ | |
| 131244 | + int i; | |
| 131245 | + int v = 0; | |
| 131246 | + for(i=0; i<N; i++){ | |
| 131247 | + if( !sqlite3Isxdigit(z[i]) ) return 0; | |
| 131248 | + v = (v<<4) + sqlite3HexToInt(z[i]); | |
| 131249 | + } | |
| 131250 | + *pVal = v; | |
| 131251 | + return 1; | |
| 131252 | +} | |
| 131253 | + | |
| 131254 | +/* | |
| 131255 | +** Implementation of the UNISTR() function. | |
| 131256 | +** | |
| 131257 | +** This is intended to be a work-alike of the UNISTR() function in | |
| 131258 | +** PostgreSQL. Quoting from the PG documentation (PostgreSQL 17 - | |
| 131259 | +** scraped on 2025-02-22): | |
| 131260 | +** | |
| 131261 | +** Evaluate escaped Unicode characters in the argument. Unicode | |
| 131262 | +** characters can be specified as \XXXX (4 hexadecimal digits), | |
| 131263 | +** \+XXXXXX (6 hexadecimal digits), \uXXXX (4 hexadecimal digits), | |
| 131264 | +** or \UXXXXXXXX (8 hexadecimal digits). To specify a backslash, | |
| 131265 | +** write two backslashes. All other characters are taken literally. | |
| 131266 | +*/ | |
| 131267 | +static void unistrFunc( | |
| 131268 | + sqlite3_context *context, | |
| 131269 | + int argc, | |
| 131270 | + sqlite3_value **argv | |
| 131271 | +){ | |
| 131272 | + char *zOut; | |
| 131273 | + const char *zIn; | |
| 131274 | + int nIn; | |
| 131275 | + int i, j, n; | |
| 131276 | + u32 v; | |
| 131277 | + | |
| 131278 | + assert( argc==1 ); | |
| 131279 | + UNUSED_PARAMETER( argc ); | |
| 131280 | + zIn = (const char*)sqlite3_value_text(argv[0]); | |
| 131281 | + if( zIn==0 ) return; | |
| 131282 | + nIn = sqlite3_value_bytes(argv[0]); | |
| 131283 | + zOut = sqlite3_malloc64(nIn+1); | |
| 131284 | + if( zOut==0 ){ | |
| 131285 | + sqlite3_result_error_nomem(context); | |
| 131286 | + return; | |
| 131287 | + } | |
| 131288 | + i = j = 0; | |
| 131289 | + while( i<nIn ){ | |
| 131290 | + char *z = strchr(&zIn[i],'\\'); | |
| 131291 | + if( z==0 ){ | |
| 131292 | + n = nIn - i; | |
| 131293 | + memmove(&zOut[j], &zIn[i], n); | |
| 131294 | + j += n; | |
| 131295 | + break; | |
| 131296 | + } | |
| 131297 | + n = z - &zIn[i]; | |
| 131298 | + if( n>0 ){ | |
| 131299 | + memmove(&zOut[j], &zIn[i], n); | |
| 131300 | + j += n; | |
| 131301 | + i += n; | |
| 131302 | + } | |
| 131303 | + if( zIn[i+1]=='\\' ){ | |
| 131304 | + i += 2; | |
| 131305 | + zOut[j++] = '\\'; | |
| 131306 | + }else if( sqlite3Isxdigit(zIn[i+1]) ){ | |
| 131307 | + if( !isNHex(&zIn[i+1], 4, &v) ) goto unistr_error; | |
| 131308 | + i += 5; | |
| 131309 | + j += sqlite3AppendOneUtf8Character(&zOut[j], v); | |
| 131310 | + }else if( zIn[i+1]=='+' ){ | |
| 131311 | + if( !isNHex(&zIn[i+2], 6, &v) ) goto unistr_error; | |
| 131312 | + i += 8; | |
| 131313 | + j += sqlite3AppendOneUtf8Character(&zOut[j], v); | |
| 131314 | + }else if( zIn[i+1]=='u' ){ | |
| 131315 | + if( !isNHex(&zIn[i+2], 4, &v) ) goto unistr_error; | |
| 131316 | + i += 6; | |
| 131317 | + j += sqlite3AppendOneUtf8Character(&zOut[j], v); | |
| 131318 | + }else if( zIn[i+1]=='U' ){ | |
| 131319 | + if( !isNHex(&zIn[i+2], 8, &v) ) goto unistr_error; | |
| 131320 | + i += 10; | |
| 131321 | + j += sqlite3AppendOneUtf8Character(&zOut[j], v); | |
| 131322 | + }else{ | |
| 131323 | + goto unistr_error; | |
| 131324 | + } | |
| 131325 | + } | |
| 131326 | + zOut[j] = 0; | |
| 131327 | + sqlite3_result_text64(context, zOut, j, sqlite3_free, SQLITE_UTF8); | |
| 131328 | + return; | |
| 131329 | + | |
| 131330 | +unistr_error: | |
| 131331 | + sqlite3_free(zOut); | |
| 131332 | + sqlite3_result_error(context, "invalid Unicode escape", -1); | |
| 131333 | + return; | |
| 131334 | +} | |
| 131335 | + | |
| 130692 | 131336 | |
| 130693 | 131337 | /* |
| 130694 | 131338 | ** Implementation of the QUOTE() function. |
| 130695 | 131339 | ** |
| 130696 | 131340 | ** The quote(X) function returns the text of an SQL literal which is the |
| @@ -130697,18 +131341,22 @@ | ||
| 130697 | 131341 | ** value of its argument suitable for inclusion into an SQL statement. |
| 130698 | 131342 | ** Strings are surrounded by single-quotes with escapes on interior quotes |
| 130699 | 131343 | ** as needed. BLOBs are encoded as hexadecimal literals. Strings with |
| 130700 | 131344 | ** embedded NUL characters cannot be represented as string literals in SQL |
| 130701 | 131345 | ** and hence the returned string literal is truncated prior to the first NUL. |
| 131346 | +** | |
| 131347 | +** If sqlite3_user_data() is non-zero, then the UNISTR_QUOTE() function is | |
| 131348 | +** implemented instead. The difference is that UNISTR_QUOTE() uses the | |
| 131349 | +** UNISTR() function to escape control characters. | |
| 130702 | 131350 | */ |
| 130703 | 131351 | static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ |
| 130704 | 131352 | sqlite3_str str; |
| 130705 | 131353 | sqlite3 *db = sqlite3_context_db_handle(context); |
| 130706 | 131354 | assert( argc==1 ); |
| 130707 | 131355 | UNUSED_PARAMETER(argc); |
| 130708 | 131356 | sqlite3StrAccumInit(&str, db, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]); |
| 130709 | - sqlite3QuoteValue(&str,argv[0]); | |
| 131357 | + sqlite3QuoteValue(&str,argv[0],SQLITE_PTR_TO_INT(sqlite3_user_data(context))); | |
| 130710 | 131358 | sqlite3_result_text(context, sqlite3StrAccumFinish(&str), str.nChar, |
| 130711 | 131359 | SQLITE_DYNAMIC); |
| 130712 | 131360 | if( str.accError!=SQLITE_OK ){ |
| 130713 | 131361 | sqlite3_result_null(context); |
| 130714 | 131362 | sqlite3_result_error_code(context, str.accError); |
| @@ -131355,11 +132003,11 @@ | ||
| 131355 | 132003 | ** |
| 131356 | 132004 | ** The SUM() function follows the (broken) SQL standard which means |
| 131357 | 132005 | ** that it returns NULL if it sums over no inputs. TOTAL returns |
| 131358 | 132006 | ** 0.0 in that case. In addition, TOTAL always returns a float where |
| 131359 | 132007 | ** SUM might return an integer if it never encounters a floating point |
| 131360 | -** value. TOTAL never fails, but SUM might through an exception if | |
| 132008 | +** value. TOTAL never fails, but SUM might throw an exception if | |
| 131361 | 132009 | ** it overflows an integer. |
| 131362 | 132010 | */ |
| 131363 | 132011 | static void sumStep(sqlite3_context *context, int argc, sqlite3_value **argv){ |
| 131364 | 132012 | SumCtx *p; |
| 131365 | 132013 | int type; |
| @@ -132275,11 +132923,13 @@ | ||
| 132275 | 132923 | VFUNCTION(randomblob, 1, 0, 0, randomBlob ), |
| 132276 | 132924 | FUNCTION(nullif, 2, 0, 1, nullifFunc ), |
| 132277 | 132925 | DFUNCTION(sqlite_version, 0, 0, 0, versionFunc ), |
| 132278 | 132926 | DFUNCTION(sqlite_source_id, 0, 0, 0, sourceidFunc ), |
| 132279 | 132927 | FUNCTION(sqlite_log, 2, 0, 0, errlogFunc ), |
| 132928 | + FUNCTION(unistr, 1, 0, 0, unistrFunc ), | |
| 132280 | 132929 | FUNCTION(quote, 1, 0, 0, quoteFunc ), |
| 132930 | + FUNCTION(unistr_quote, 1, 1, 0, quoteFunc ), | |
| 132281 | 132931 | VFUNCTION(last_insert_rowid, 0, 0, 0, last_insert_rowid), |
| 132282 | 132932 | VFUNCTION(changes, 0, 0, 0, changes ), |
| 132283 | 132933 | VFUNCTION(total_changes, 0, 0, 0, total_changes ), |
| 132284 | 132934 | FUNCTION(replace, 3, 0, 0, replaceFunc ), |
| 132285 | 132935 | FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc ), |
| @@ -134562,11 +135212,11 @@ | ||
| 134562 | 135212 | }else if( pLeft->pPrior ){ |
| 134563 | 135213 | /* In this case set the SF_MultiValue flag only if it was set on pLeft */ |
| 134564 | 135214 | f = (f & pLeft->selFlags); |
| 134565 | 135215 | } |
| 134566 | 135216 | pSelect = sqlite3SelectNew(pParse, pRow, 0, 0, 0, 0, 0, f, 0); |
| 134567 | - pLeft->selFlags &= ~SF_MultiValue; | |
| 135217 | + pLeft->selFlags &= ~(u32)SF_MultiValue; | |
| 134568 | 135218 | if( pSelect ){ |
| 134569 | 135219 | pSelect->op = TK_ALL; |
| 134570 | 135220 | pSelect->pPrior = pLeft; |
| 134571 | 135221 | pLeft = pSelect; |
| 134572 | 135222 | } |
| @@ -139168,52 +139818,52 @@ | ||
| 139168 | 139818 | /* 11 */ "notnull", |
| 139169 | 139819 | /* 12 */ "dflt_value", |
| 139170 | 139820 | /* 13 */ "pk", |
| 139171 | 139821 | /* 14 */ "hidden", |
| 139172 | 139822 | /* table_info reuses 8 */ |
| 139173 | - /* 15 */ "schema", /* Used by: table_list */ | |
| 139174 | - /* 16 */ "name", | |
| 139823 | + /* 15 */ "name", /* Used by: function_list */ | |
| 139824 | + /* 16 */ "builtin", | |
| 139175 | 139825 | /* 17 */ "type", |
| 139176 | - /* 18 */ "ncol", | |
| 139177 | - /* 19 */ "wr", | |
| 139178 | - /* 20 */ "strict", | |
| 139179 | - /* 21 */ "seqno", /* Used by: index_xinfo */ | |
| 139180 | - /* 22 */ "cid", | |
| 139181 | - /* 23 */ "name", | |
| 139182 | - /* 24 */ "desc", | |
| 139183 | - /* 25 */ "coll", | |
| 139184 | - /* 26 */ "key", | |
| 139185 | - /* 27 */ "name", /* Used by: function_list */ | |
| 139186 | - /* 28 */ "builtin", | |
| 139187 | - /* 29 */ "type", | |
| 139188 | - /* 30 */ "enc", | |
| 139189 | - /* 31 */ "narg", | |
| 139190 | - /* 32 */ "flags", | |
| 139191 | - /* 33 */ "tbl", /* Used by: stats */ | |
| 139192 | - /* 34 */ "idx", | |
| 139193 | - /* 35 */ "wdth", | |
| 139194 | - /* 36 */ "hght", | |
| 139195 | - /* 37 */ "flgs", | |
| 139196 | - /* 38 */ "seq", /* Used by: index_list */ | |
| 139197 | - /* 39 */ "name", | |
| 139198 | - /* 40 */ "unique", | |
| 139199 | - /* 41 */ "origin", | |
| 139200 | - /* 42 */ "partial", | |
| 139826 | + /* 18 */ "enc", | |
| 139827 | + /* 19 */ "narg", | |
| 139828 | + /* 20 */ "flags", | |
| 139829 | + /* 21 */ "schema", /* Used by: table_list */ | |
| 139830 | + /* 22 */ "name", | |
| 139831 | + /* 23 */ "type", | |
| 139832 | + /* 24 */ "ncol", | |
| 139833 | + /* 25 */ "wr", | |
| 139834 | + /* 26 */ "strict", | |
| 139835 | + /* 27 */ "seqno", /* Used by: index_xinfo */ | |
| 139836 | + /* 28 */ "cid", | |
| 139837 | + /* 29 */ "name", | |
| 139838 | + /* 30 */ "desc", | |
| 139839 | + /* 31 */ "coll", | |
| 139840 | + /* 32 */ "key", | |
| 139841 | + /* 33 */ "seq", /* Used by: index_list */ | |
| 139842 | + /* 34 */ "name", | |
| 139843 | + /* 35 */ "unique", | |
| 139844 | + /* 36 */ "origin", | |
| 139845 | + /* 37 */ "partial", | |
| 139846 | + /* 38 */ "tbl", /* Used by: stats */ | |
| 139847 | + /* 39 */ "idx", | |
| 139848 | + /* 40 */ "wdth", | |
| 139849 | + /* 41 */ "hght", | |
| 139850 | + /* 42 */ "flgs", | |
| 139201 | 139851 | /* 43 */ "table", /* Used by: foreign_key_check */ |
| 139202 | 139852 | /* 44 */ "rowid", |
| 139203 | 139853 | /* 45 */ "parent", |
| 139204 | 139854 | /* 46 */ "fkid", |
| 139205 | - /* index_info reuses 21 */ | |
| 139206 | - /* 47 */ "seq", /* Used by: database_list */ | |
| 139207 | - /* 48 */ "name", | |
| 139208 | - /* 49 */ "file", | |
| 139209 | - /* 50 */ "busy", /* Used by: wal_checkpoint */ | |
| 139210 | - /* 51 */ "log", | |
| 139211 | - /* 52 */ "checkpointed", | |
| 139212 | - /* collation_list reuses 38 */ | |
| 139855 | + /* 47 */ "busy", /* Used by: wal_checkpoint */ | |
| 139856 | + /* 48 */ "log", | |
| 139857 | + /* 49 */ "checkpointed", | |
| 139858 | + /* 50 */ "seq", /* Used by: database_list */ | |
| 139859 | + /* 51 */ "name", | |
| 139860 | + /* 52 */ "file", | |
| 139861 | + /* index_info reuses 27 */ | |
| 139213 | 139862 | /* 53 */ "database", /* Used by: lock_status */ |
| 139214 | 139863 | /* 54 */ "status", |
| 139864 | + /* collation_list reuses 33 */ | |
| 139215 | 139865 | /* 55 */ "cache_size", /* Used by: default_cache_size */ |
| 139216 | 139866 | /* module_list pragma_list reuses 9 */ |
| 139217 | 139867 | /* 56 */ "timeout", /* Used by: busy_timeout */ |
| 139218 | 139868 | }; |
| 139219 | 139869 | |
| @@ -139302,11 +139952,11 @@ | ||
| 139302 | 139952 | #endif |
| 139303 | 139953 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) |
| 139304 | 139954 | {/* zName: */ "collation_list", |
| 139305 | 139955 | /* ePragTyp: */ PragTyp_COLLATION_LIST, |
| 139306 | 139956 | /* ePragFlg: */ PragFlg_Result0, |
| 139307 | - /* ColNames: */ 38, 2, | |
| 139957 | + /* ColNames: */ 33, 2, | |
| 139308 | 139958 | /* iArg: */ 0 }, |
| 139309 | 139959 | #endif |
| 139310 | 139960 | #if !defined(SQLITE_OMIT_COMPILEOPTION_DIAGS) |
| 139311 | 139961 | {/* zName: */ "compile_options", |
| 139312 | 139962 | /* ePragTyp: */ PragTyp_COMPILE_OPTIONS, |
| @@ -139337,11 +139987,11 @@ | ||
| 139337 | 139987 | #endif |
| 139338 | 139988 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) |
| 139339 | 139989 | {/* zName: */ "database_list", |
| 139340 | 139990 | /* ePragTyp: */ PragTyp_DATABASE_LIST, |
| 139341 | 139991 | /* ePragFlg: */ PragFlg_Result0, |
| 139342 | - /* ColNames: */ 47, 3, | |
| 139992 | + /* ColNames: */ 50, 3, | |
| 139343 | 139993 | /* iArg: */ 0 }, |
| 139344 | 139994 | #endif |
| 139345 | 139995 | #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED) |
| 139346 | 139996 | {/* zName: */ "default_cache_size", |
| 139347 | 139997 | /* ePragTyp: */ PragTyp_DEFAULT_CACHE_SIZE, |
| @@ -139417,11 +140067,11 @@ | ||
| 139417 | 140067 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) |
| 139418 | 140068 | #if !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS) |
| 139419 | 140069 | {/* zName: */ "function_list", |
| 139420 | 140070 | /* ePragTyp: */ PragTyp_FUNCTION_LIST, |
| 139421 | 140071 | /* ePragFlg: */ PragFlg_Result0, |
| 139422 | - /* ColNames: */ 27, 6, | |
| 140072 | + /* ColNames: */ 15, 6, | |
| 139423 | 140073 | /* iArg: */ 0 }, |
| 139424 | 140074 | #endif |
| 139425 | 140075 | #endif |
| 139426 | 140076 | {/* zName: */ "hard_heap_limit", |
| 139427 | 140077 | /* ePragTyp: */ PragTyp_HARD_HEAP_LIMIT, |
| @@ -139446,21 +140096,21 @@ | ||
| 139446 | 140096 | #endif |
| 139447 | 140097 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) |
| 139448 | 140098 | {/* zName: */ "index_info", |
| 139449 | 140099 | /* ePragTyp: */ PragTyp_INDEX_INFO, |
| 139450 | 140100 | /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, |
| 139451 | - /* ColNames: */ 21, 3, | |
| 140101 | + /* ColNames: */ 27, 3, | |
| 139452 | 140102 | /* iArg: */ 0 }, |
| 139453 | 140103 | {/* zName: */ "index_list", |
| 139454 | 140104 | /* ePragTyp: */ PragTyp_INDEX_LIST, |
| 139455 | 140105 | /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, |
| 139456 | - /* ColNames: */ 38, 5, | |
| 140106 | + /* ColNames: */ 33, 5, | |
| 139457 | 140107 | /* iArg: */ 0 }, |
| 139458 | 140108 | {/* zName: */ "index_xinfo", |
| 139459 | 140109 | /* ePragTyp: */ PragTyp_INDEX_INFO, |
| 139460 | 140110 | /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, |
| 139461 | - /* ColNames: */ 21, 6, | |
| 140111 | + /* ColNames: */ 27, 6, | |
| 139462 | 140112 | /* iArg: */ 1 }, |
| 139463 | 140113 | #endif |
| 139464 | 140114 | #if !defined(SQLITE_OMIT_INTEGRITY_CHECK) |
| 139465 | 140115 | {/* zName: */ "integrity_check", |
| 139466 | 140116 | /* ePragTyp: */ PragTyp_INTEGRITY_CHECK, |
| @@ -139635,11 +140285,11 @@ | ||
| 139635 | 140285 | #endif |
| 139636 | 140286 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) && defined(SQLITE_DEBUG) |
| 139637 | 140287 | {/* zName: */ "stats", |
| 139638 | 140288 | /* ePragTyp: */ PragTyp_STATS, |
| 139639 | 140289 | /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq, |
| 139640 | - /* ColNames: */ 33, 5, | |
| 140290 | + /* ColNames: */ 38, 5, | |
| 139641 | 140291 | /* iArg: */ 0 }, |
| 139642 | 140292 | #endif |
| 139643 | 140293 | #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) |
| 139644 | 140294 | {/* zName: */ "synchronous", |
| 139645 | 140295 | /* ePragTyp: */ PragTyp_SYNCHRONOUS, |
| @@ -139654,11 +140304,11 @@ | ||
| 139654 | 140304 | /* ColNames: */ 8, 6, |
| 139655 | 140305 | /* iArg: */ 0 }, |
| 139656 | 140306 | {/* zName: */ "table_list", |
| 139657 | 140307 | /* ePragTyp: */ PragTyp_TABLE_LIST, |
| 139658 | 140308 | /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1, |
| 139659 | - /* ColNames: */ 15, 6, | |
| 140309 | + /* ColNames: */ 21, 6, | |
| 139660 | 140310 | /* iArg: */ 0 }, |
| 139661 | 140311 | {/* zName: */ "table_xinfo", |
| 139662 | 140312 | /* ePragTyp: */ PragTyp_TABLE_INFO, |
| 139663 | 140313 | /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, |
| 139664 | 140314 | /* ColNames: */ 8, 7, |
| @@ -139731,11 +140381,11 @@ | ||
| 139731 | 140381 | /* ColNames: */ 0, 0, |
| 139732 | 140382 | /* iArg: */ 0 }, |
| 139733 | 140383 | {/* zName: */ "wal_checkpoint", |
| 139734 | 140384 | /* ePragTyp: */ PragTyp_WAL_CHECKPOINT, |
| 139735 | 140385 | /* ePragFlg: */ PragFlg_NeedSchema, |
| 139736 | - /* ColNames: */ 50, 3, | |
| 140386 | + /* ColNames: */ 47, 3, | |
| 139737 | 140387 | /* iArg: */ 0 }, |
| 139738 | 140388 | #endif |
| 139739 | 140389 | #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) |
| 139740 | 140390 | {/* zName: */ "writable_schema", |
| 139741 | 140391 | /* ePragTyp: */ PragTyp_FLAG, |
| @@ -139753,11 +140403,11 @@ | ||
| 139753 | 140403 | ** When the 0x10 bit of PRAGMA optimize is set, any ANALYZE commands |
| 139754 | 140404 | ** will be run with an analysis_limit set to the lessor of the value of |
| 139755 | 140405 | ** the following macro or to the actual analysis_limit if it is non-zero, |
| 139756 | 140406 | ** in order to prevent PRAGMA optimize from running for too long. |
| 139757 | 140407 | ** |
| 139758 | -** The value of 2000 is chosen emperically so that the worst-case run-time | |
| 140408 | +** The value of 2000 is chosen empirically so that the worst-case run-time | |
| 139759 | 140409 | ** for PRAGMA optimize does not exceed 100 milliseconds against a variety |
| 139760 | 140410 | ** of test databases on a RaspberryPI-4 compiled using -Os and without |
| 139761 | 140411 | ** -DSQLITE_DEBUG. Of course, your mileage may vary. For the purpose of |
| 139762 | 140412 | ** this paragraph, "worst-case" means that ANALYZE ends up being |
| 139763 | 140413 | ** run on every table in the database. The worst case typically only |
| @@ -144042,11 +144692,11 @@ | ||
| 144042 | 144692 | pNew->iOffset = 0; |
| 144043 | 144693 | pNew->selId = ++pParse->nSelect; |
| 144044 | 144694 | pNew->addrOpenEphm[0] = -1; |
| 144045 | 144695 | pNew->addrOpenEphm[1] = -1; |
| 144046 | 144696 | pNew->nSelectRow = 0; |
| 144047 | - if( pSrc==0 ) pSrc = sqlite3DbMallocZero(pParse->db, sizeof(*pSrc)); | |
| 144697 | + if( pSrc==0 ) pSrc = sqlite3DbMallocZero(pParse->db, SZ_SRCLIST_1); | |
| 144048 | 144698 | pNew->pSrc = pSrc; |
| 144049 | 144699 | pNew->pWhere = pWhere; |
| 144050 | 144700 | pNew->pGroupBy = pGroupBy; |
| 144051 | 144701 | pNew->pHaving = pHaving; |
| 144052 | 144702 | pNew->pOrderBy = pOrderBy; |
| @@ -145425,20 +146075,20 @@ | ||
| 145425 | 146075 | /* |
| 145426 | 146076 | ** Allocate a KeyInfo object sufficient for an index of N key columns and |
| 145427 | 146077 | ** X extra columns. |
| 145428 | 146078 | */ |
| 145429 | 146079 | SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){ |
| 145430 | - int nExtra = (N+X)*(sizeof(CollSeq*)+1) - sizeof(CollSeq*); | |
| 145431 | - KeyInfo *p = sqlite3DbMallocRawNN(db, sizeof(KeyInfo) + nExtra); | |
| 146080 | + int nExtra = (N+X)*(sizeof(CollSeq*)+1); | |
| 146081 | + KeyInfo *p = sqlite3DbMallocRawNN(db, SZ_KEYINFO(0) + nExtra); | |
| 145432 | 146082 | if( p ){ |
| 145433 | 146083 | p->aSortFlags = (u8*)&p->aColl[N+X]; |
| 145434 | 146084 | p->nKeyField = (u16)N; |
| 145435 | 146085 | p->nAllField = (u16)(N+X); |
| 145436 | 146086 | p->enc = ENC(db); |
| 145437 | 146087 | p->db = db; |
| 145438 | 146088 | p->nRef = 1; |
| 145439 | - memset(&p[1], 0, nExtra); | |
| 146089 | + memset(p->aColl, 0, nExtra); | |
| 145440 | 146090 | }else{ |
| 145441 | 146091 | return (KeyInfo*)sqlite3OomFault(db); |
| 145442 | 146092 | } |
| 145443 | 146093 | return p; |
| 145444 | 146094 | } |
| @@ -149534,11 +150184,11 @@ | ||
| 149534 | 150184 | p->pNext = 0; |
| 149535 | 150185 | p->pWith = 0; |
| 149536 | 150186 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| 149537 | 150187 | p->pWinDefn = 0; |
| 149538 | 150188 | #endif |
| 149539 | - p->selFlags &= ~SF_Compound; | |
| 150189 | + p->selFlags &= ~(u32)SF_Compound; | |
| 149540 | 150190 | assert( (p->selFlags & SF_Converted)==0 ); |
| 149541 | 150191 | p->selFlags |= SF_Converted; |
| 149542 | 150192 | assert( pNew->pPrior!=0 ); |
| 149543 | 150193 | pNew->pPrior->pNext = pNew; |
| 149544 | 150194 | pNew->pLimit = 0; |
| @@ -149950,11 +150600,11 @@ | ||
| 149950 | 150600 | } |
| 149951 | 150601 | pTabList = p->pSrc; |
| 149952 | 150602 | pEList = p->pEList; |
| 149953 | 150603 | if( pParse->pWith && (p->selFlags & SF_View) ){ |
| 149954 | 150604 | if( p->pWith==0 ){ |
| 149955 | - p->pWith = (With*)sqlite3DbMallocZero(db, sizeof(With)); | |
| 150605 | + p->pWith = (With*)sqlite3DbMallocZero(db, SZ_WITH(1) ); | |
| 149956 | 150606 | if( p->pWith==0 ){ |
| 149957 | 150607 | return WRC_Abort; |
| 149958 | 150608 | } |
| 149959 | 150609 | } |
| 149960 | 150610 | p->pWith->bView = 1; |
| @@ -151089,10 +151739,11 @@ | ||
| 151089 | 151739 | ** * The subquery is a UNION ALL of two or more terms |
| 151090 | 151740 | ** * The subquery does not have a LIMIT clause |
| 151091 | 151741 | ** * There is no WHERE or GROUP BY or HAVING clauses on the subqueries |
| 151092 | 151742 | ** * The outer query is a simple count(*) with no WHERE clause or other |
| 151093 | 151743 | ** extraneous syntax. |
| 151744 | +** * None of the subqueries are DISTINCT (forumpost/a860f5fb2e 2025-03-10) | |
| 151094 | 151745 | ** |
| 151095 | 151746 | ** Return TRUE if the optimization is undertaken. |
| 151096 | 151747 | */ |
| 151097 | 151748 | static int countOfViewOptimization(Parse *pParse, Select *p){ |
| 151098 | 151749 | Select *pSub, *pPrior; |
| @@ -151121,11 +151772,15 @@ | ||
| 151121 | 151772 | if( pSub->selFlags & SF_CopyCte ) return 0; /* Not a CTE */ |
| 151122 | 151773 | do{ |
| 151123 | 151774 | if( pSub->op!=TK_ALL && pSub->pPrior ) return 0; /* Must be UNION ALL */ |
| 151124 | 151775 | if( pSub->pWhere ) return 0; /* No WHERE clause */ |
| 151125 | 151776 | if( pSub->pLimit ) return 0; /* No LIMIT clause */ |
| 151126 | - if( pSub->selFlags & SF_Aggregate ) return 0; /* Not an aggregate */ | |
| 151777 | + if( pSub->selFlags & (SF_Aggregate|SF_Distinct) ){ | |
| 151778 | + testcase( pSub->selFlags & SF_Aggregate ); | |
| 151779 | + testcase( pSub->selFlags & SF_Distinct ); | |
| 151780 | + return 0; /* Not an aggregate nor DISTINCT */ | |
| 151781 | + } | |
| 151127 | 151782 | assert( pSub->pHaving==0 ); /* Due to the previous */ |
| 151128 | 151783 | pSub = pSub->pPrior; /* Repeat over compound */ |
| 151129 | 151784 | }while( pSub ); |
| 151130 | 151785 | |
| 151131 | 151786 | /* If we reach this point then it is OK to perform the transformation */ |
| @@ -151133,18 +151788,18 @@ | ||
| 151133 | 151788 | db = pParse->db; |
| 151134 | 151789 | pCount = pExpr; |
| 151135 | 151790 | pExpr = 0; |
| 151136 | 151791 | pSub = sqlite3SubqueryDetach(db, pFrom); |
| 151137 | 151792 | sqlite3SrcListDelete(db, p->pSrc); |
| 151138 | - p->pSrc = sqlite3DbMallocZero(pParse->db, sizeof(*p->pSrc)); | |
| 151793 | + p->pSrc = sqlite3DbMallocZero(pParse->db, SZ_SRCLIST_1); | |
| 151139 | 151794 | while( pSub ){ |
| 151140 | 151795 | Expr *pTerm; |
| 151141 | 151796 | pPrior = pSub->pPrior; |
| 151142 | 151797 | pSub->pPrior = 0; |
| 151143 | 151798 | pSub->pNext = 0; |
| 151144 | 151799 | pSub->selFlags |= SF_Aggregate; |
| 151145 | - pSub->selFlags &= ~SF_Compound; | |
| 151800 | + pSub->selFlags &= ~(u32)SF_Compound; | |
| 151146 | 151801 | pSub->nSelectRow = 0; |
| 151147 | 151802 | sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, pSub->pEList); |
| 151148 | 151803 | pTerm = pPrior ? sqlite3ExprDup(db, pCount, 0) : pCount; |
| 151149 | 151804 | pSub->pEList = sqlite3ExprListAppend(pParse, 0, pTerm); |
| 151150 | 151805 | pTerm = sqlite3PExpr(pParse, TK_SELECT, 0, 0); |
| @@ -151155,11 +151810,11 @@ | ||
| 151155 | 151810 | pExpr = sqlite3PExpr(pParse, TK_PLUS, pTerm, pExpr); |
| 151156 | 151811 | } |
| 151157 | 151812 | pSub = pPrior; |
| 151158 | 151813 | } |
| 151159 | 151814 | p->pEList->a[0].pExpr = pExpr; |
| 151160 | - p->selFlags &= ~SF_Aggregate; | |
| 151815 | + p->selFlags &= ~(u32)SF_Aggregate; | |
| 151161 | 151816 | |
| 151162 | 151817 | #if TREETRACE_ENABLED |
| 151163 | 151818 | if( sqlite3TreeTrace & 0x200 ){ |
| 151164 | 151819 | TREETRACE(0x200,pParse,p,("After count-of-view optimization:\n")); |
| 151165 | 151820 | sqlite3TreeViewSelect(0, p, 0); |
| @@ -151362,11 +152017,11 @@ | ||
| 151362 | 152017 | sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, |
| 151363 | 152018 | p->pOrderBy); |
| 151364 | 152019 | testcase( pParse->earlyCleanup ); |
| 151365 | 152020 | p->pOrderBy = 0; |
| 151366 | 152021 | } |
| 151367 | - p->selFlags &= ~SF_Distinct; | |
| 152022 | + p->selFlags &= ~(u32)SF_Distinct; | |
| 151368 | 152023 | p->selFlags |= SF_NoopOrderBy; |
| 151369 | 152024 | } |
| 151370 | 152025 | sqlite3SelectPrep(pParse, p, 0); |
| 151371 | 152026 | if( pParse->nErr ){ |
| 151372 | 152027 | goto select_end; |
| @@ -151401,11 +152056,11 @@ | ||
| 151401 | 152056 | |
| 151402 | 152057 | /* Clear the SF_UFSrcCheck flag. The check has already been performed, |
| 151403 | 152058 | ** and leaving this flag set can cause errors if a compound sub-query |
| 151404 | 152059 | ** in p->pSrc is flattened into this query and this function called |
| 151405 | 152060 | ** again as part of compound SELECT processing. */ |
| 151406 | - p->selFlags &= ~SF_UFSrcCheck; | |
| 152061 | + p->selFlags &= ~(u32)SF_UFSrcCheck; | |
| 151407 | 152062 | } |
| 151408 | 152063 | |
| 151409 | 152064 | if( pDest->eDest==SRT_Output ){ |
| 151410 | 152065 | sqlite3GenerateColumnNames(pParse, p); |
| 151411 | 152066 | } |
| @@ -151890,11 +152545,11 @@ | ||
| 151890 | 152545 | && OptimizationEnabled(db, SQLITE_GroupByOrder) |
| 151891 | 152546 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| 151892 | 152547 | && p->pWin==0 |
| 151893 | 152548 | #endif |
| 151894 | 152549 | ){ |
| 151895 | - p->selFlags &= ~SF_Distinct; | |
| 152550 | + p->selFlags &= ~(u32)SF_Distinct; | |
| 151896 | 152551 | pGroupBy = p->pGroupBy = sqlite3ExprListDup(db, pEList, 0); |
| 151897 | 152552 | if( pGroupBy ){ |
| 151898 | 152553 | for(i=0; i<pGroupBy->nExpr; i++){ |
| 151899 | 152554 | pGroupBy->a[i].u.x.iOrderByCol = i+1; |
| 151900 | 152555 | } |
| @@ -153924,11 +154579,12 @@ | ||
| 153924 | 154579 | Vdbe *v = pParse->pVdbe; |
| 153925 | 154580 | sqlite3 *db = pParse->db; |
| 153926 | 154581 | ExprList *pNew; |
| 153927 | 154582 | Returning *pReturning; |
| 153928 | 154583 | Select sSelect; |
| 153929 | - SrcList sFrom; | |
| 154584 | + SrcList *pFrom; | |
| 154585 | + u8 fromSpace[SZ_SRCLIST_1]; | |
| 153930 | 154586 | |
| 153931 | 154587 | assert( v!=0 ); |
| 153932 | 154588 | if( !pParse->bReturning ){ |
| 153933 | 154589 | /* This RETURNING trigger must be for a different statement as |
| 153934 | 154590 | ** this statement lacks a RETURNING clause. */ |
| @@ -153940,17 +154596,18 @@ | ||
| 153940 | 154596 | if( pTrigger != &(pReturning->retTrig) ){ |
| 153941 | 154597 | /* This RETURNING trigger is for a different statement */ |
| 153942 | 154598 | return; |
| 153943 | 154599 | } |
| 153944 | 154600 | memset(&sSelect, 0, sizeof(sSelect)); |
| 153945 | - memset(&sFrom, 0, sizeof(sFrom)); | |
| 154601 | + pFrom = (SrcList*)fromSpace; | |
| 154602 | + memset(pFrom, 0, SZ_SRCLIST_1); | |
| 153946 | 154603 | sSelect.pEList = sqlite3ExprListDup(db, pReturning->pReturnEL, 0); |
| 153947 | - sSelect.pSrc = &sFrom; | |
| 153948 | - sFrom.nSrc = 1; | |
| 153949 | - sFrom.a[0].pSTab = pTab; | |
| 153950 | - sFrom.a[0].zName = pTab->zName; /* tag-20240424-1 */ | |
| 153951 | - sFrom.a[0].iCursor = -1; | |
| 154604 | + sSelect.pSrc = pFrom; | |
| 154605 | + pFrom->nSrc = 1; | |
| 154606 | + pFrom->a[0].pSTab = pTab; | |
| 154607 | + pFrom->a[0].zName = pTab->zName; /* tag-20240424-1 */ | |
| 154608 | + pFrom->a[0].iCursor = -1; | |
| 153952 | 154609 | sqlite3SelectPrep(pParse, &sSelect, 0); |
| 153953 | 154610 | if( pParse->nErr==0 ){ |
| 153954 | 154611 | assert( db->mallocFailed==0 ); |
| 153955 | 154612 | sqlite3GenerateColumnNames(pParse, &sSelect); |
| 153956 | 154613 | } |
| @@ -156347,11 +157004,11 @@ | ||
| 156347 | 157004 | saved_flags = db->flags; |
| 156348 | 157005 | saved_mDbFlags = db->mDbFlags; |
| 156349 | 157006 | saved_nChange = db->nChange; |
| 156350 | 157007 | saved_nTotalChange = db->nTotalChange; |
| 156351 | 157008 | saved_mTrace = db->mTrace; |
| 156352 | - db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks; | |
| 157009 | + db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks | SQLITE_Comments; | |
| 156353 | 157010 | db->mDbFlags |= DBFLAG_PreferBuiltin | DBFLAG_Vacuum; |
| 156354 | 157011 | db->flags &= ~(u64)(SQLITE_ForeignKeys | SQLITE_ReverseOrder |
| 156355 | 157012 | | SQLITE_Defensive | SQLITE_CountRows); |
| 156356 | 157013 | db->mTrace = 0; |
| 156357 | 157014 | |
| @@ -158476,13 +159133,18 @@ | ||
| 158476 | 159133 | WhereLoop *pLoops; /* List of all WhereLoop objects */ |
| 158477 | 159134 | WhereMemBlock *pMemToFree;/* Memory to free when this object destroyed */ |
| 158478 | 159135 | Bitmask revMask; /* Mask of ORDER BY terms that need reversing */ |
| 158479 | 159136 | WhereClause sWC; /* Decomposition of the WHERE clause */ |
| 158480 | 159137 | WhereMaskSet sMaskSet; /* Map cursor numbers to bitmasks */ |
| 158481 | - WhereLevel a[1]; /* Information about each nest loop in WHERE */ | |
| 159138 | + WhereLevel a[FLEXARRAY]; /* Information about each nest loop in WHERE */ | |
| 158482 | 159139 | }; |
| 158483 | 159140 | |
| 159141 | +/* | |
| 159142 | +** The size (in bytes) of a WhereInfo object that holds N WhereLevels. | |
| 159143 | +*/ | |
| 159144 | +#define SZ_WHEREINFO(N) ROUND8(offsetof(WhereInfo,a)+(N)*sizeof(WhereLevel)) | |
| 159145 | + | |
| 158484 | 159146 | /* |
| 158485 | 159147 | ** Private interfaces - callable only by other where.c routines. |
| 158486 | 159148 | ** |
| 158487 | 159149 | ** where.c: |
| 158488 | 159150 | */ |
| @@ -160929,12 +161591,11 @@ | ||
| 160929 | 161591 | */ |
| 160930 | 161592 | if( pWInfo->nLevel>1 ){ |
| 160931 | 161593 | int nNotReady; /* The number of notReady tables */ |
| 160932 | 161594 | SrcItem *origSrc; /* Original list of tables */ |
| 160933 | 161595 | nNotReady = pWInfo->nLevel - iLevel - 1; |
| 160934 | - pOrTab = sqlite3DbMallocRawNN(db, | |
| 160935 | - sizeof(*pOrTab)+ nNotReady*sizeof(pOrTab->a[0])); | |
| 161596 | + pOrTab = sqlite3DbMallocRawNN(db, SZ_SRCLIST(nNotReady+1)); | |
| 160936 | 161597 | if( pOrTab==0 ) return notReady; |
| 160937 | 161598 | pOrTab->nAlloc = (u8)(nNotReady + 1); |
| 160938 | 161599 | pOrTab->nSrc = pOrTab->nAlloc; |
| 160939 | 161600 | memcpy(pOrTab->a, pTabItem, sizeof(*pTabItem)); |
| 160940 | 161601 | origSrc = pWInfo->pTabList->a; |
| @@ -161473,11 +162134,12 @@ | ||
| 161473 | 162134 | Expr *pSubWhere = 0; |
| 161474 | 162135 | WhereClause *pWC = &pWInfo->sWC; |
| 161475 | 162136 | WhereInfo *pSubWInfo; |
| 161476 | 162137 | WhereLoop *pLoop = pLevel->pWLoop; |
| 161477 | 162138 | SrcItem *pTabItem = &pWInfo->pTabList->a[pLevel->iFrom]; |
| 161478 | - SrcList sFrom; | |
| 162139 | + SrcList *pFrom; | |
| 162140 | + u8 fromSpace[SZ_SRCLIST_1]; | |
| 161479 | 162141 | Bitmask mAll = 0; |
| 161480 | 162142 | int k; |
| 161481 | 162143 | |
| 161482 | 162144 | ExplainQueryPlan((pParse, 1, "RIGHT-JOIN %s", pTabItem->pSTab->zName)); |
| 161483 | 162145 | sqlite3VdbeNoJumpsOutsideSubrtn(v, pRJ->addrSubrtn, pRJ->endSubrtn, |
| @@ -161517,17 +162179,18 @@ | ||
| 161517 | 162179 | if( ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) ) continue; |
| 161518 | 162180 | pSubWhere = sqlite3ExprAnd(pParse, pSubWhere, |
| 161519 | 162181 | sqlite3ExprDup(pParse->db, pTerm->pExpr, 0)); |
| 161520 | 162182 | } |
| 161521 | 162183 | } |
| 161522 | - sFrom.nSrc = 1; | |
| 161523 | - sFrom.nAlloc = 1; | |
| 161524 | - memcpy(&sFrom.a[0], pTabItem, sizeof(SrcItem)); | |
| 161525 | - sFrom.a[0].fg.jointype = 0; | |
| 162184 | + pFrom = (SrcList*)fromSpace; | |
| 162185 | + pFrom->nSrc = 1; | |
| 162186 | + pFrom->nAlloc = 1; | |
| 162187 | + memcpy(&pFrom->a[0], pTabItem, sizeof(SrcItem)); | |
| 162188 | + pFrom->a[0].fg.jointype = 0; | |
| 161526 | 162189 | assert( pParse->withinRJSubrtn < 100 ); |
| 161527 | 162190 | pParse->withinRJSubrtn++; |
| 161528 | - pSubWInfo = sqlite3WhereBegin(pParse, &sFrom, pSubWhere, 0, 0, 0, | |
| 162191 | + pSubWInfo = sqlite3WhereBegin(pParse, pFrom, pSubWhere, 0, 0, 0, | |
| 161529 | 162192 | WHERE_RIGHT_JOIN, 0); |
| 161530 | 162193 | if( pSubWInfo ){ |
| 161531 | 162194 | int iCur = pLevel->iTabCur; |
| 161532 | 162195 | int r = ++pParse->nMem; |
| 161533 | 162196 | int nPk; |
| @@ -163511,15 +164174,20 @@ | ||
| 163511 | 164174 | WhereClause *pWC; /* The Where clause being analyzed */ |
| 163512 | 164175 | Parse *pParse; /* The parsing context */ |
| 163513 | 164176 | int eDistinct; /* Value to return from sqlite3_vtab_distinct() */ |
| 163514 | 164177 | u32 mIn; /* Mask of terms that are <col> IN (...) */ |
| 163515 | 164178 | u32 mHandleIn; /* Terms that vtab will handle as <col> IN (...) */ |
| 163516 | - sqlite3_value *aRhs[1]; /* RHS values for constraints. MUST BE LAST | |
| 163517 | - ** because extra space is allocated to hold up | |
| 163518 | - ** to nTerm such values */ | |
| 164179 | + sqlite3_value *aRhs[FLEXARRAY]; /* RHS values for constraints. MUST BE LAST | |
| 164180 | + ** Extra space is allocated to hold up | |
| 164181 | + ** to nTerm such values */ | |
| 163519 | 164182 | }; |
| 163520 | 164183 | |
| 164184 | +/* Size (in bytes) of a HiddenIndeInfo object sufficient to hold as | |
| 164185 | +** many as N constraints */ | |
| 164186 | +#define SZ_HIDDENINDEXINFO(N) \ | |
| 164187 | + (offsetof(HiddenIndexInfo,aRhs) + (N)*sizeof(sqlite3_value*)) | |
| 164188 | + | |
| 163521 | 164189 | /* Forward declaration of methods */ |
| 163522 | 164190 | static int whereLoopResize(sqlite3*, WhereLoop*, int); |
| 163523 | 164191 | |
| 163524 | 164192 | /* |
| 163525 | 164193 | ** Return the estimated number of output rows from a WHERE clause |
| @@ -164580,10 +165248,12 @@ | ||
| 164580 | 165248 | if( pSrc->colUsed & MASKBIT(BMS-1) ){ |
| 164581 | 165249 | nKeyCol += pTable->nCol - BMS + 1; |
| 164582 | 165250 | } |
| 164583 | 165251 | |
| 164584 | 165252 | /* Construct the Index object to describe this index */ |
| 165253 | + assert( nKeyCol <= pTable->nCol + MAX(0, pTable->nCol - BMS + 1) ); | |
| 165254 | + /* ^-- This guarantees that the number of index columns will fit in the u16 */ | |
| 164585 | 165255 | pIdx = sqlite3AllocateIndexObject(pParse->db, nKeyCol+HasRowid(pTable), |
| 164586 | 165256 | 0, &zNotUsed); |
| 164587 | 165257 | if( pIdx==0 ) goto end_auto_index_create; |
| 164588 | 165258 | pLoop->u.btree.pIndex = pIdx; |
| 164589 | 165259 | pIdx->zName = "auto-index"; |
| @@ -164991,12 +165661,12 @@ | ||
| 164991 | 165661 | |
| 164992 | 165662 | /* Allocate the sqlite3_index_info structure |
| 164993 | 165663 | */ |
| 164994 | 165664 | pIdxInfo = sqlite3DbMallocZero(pParse->db, sizeof(*pIdxInfo) |
| 164995 | 165665 | + (sizeof(*pIdxCons) + sizeof(*pUsage))*nTerm |
| 164996 | - + sizeof(*pIdxOrderBy)*nOrderBy + sizeof(*pHidden) | |
| 164997 | - + sizeof(sqlite3_value*)*nTerm ); | |
| 165666 | + + sizeof(*pIdxOrderBy)*nOrderBy | |
| 165667 | + + SZ_HIDDENINDEXINFO(nTerm) ); | |
| 164998 | 165668 | if( pIdxInfo==0 ){ |
| 164999 | 165669 | sqlite3ErrorMsg(pParse, "out of memory"); |
| 165000 | 165670 | return 0; |
| 165001 | 165671 | } |
| 165002 | 165672 | pHidden = (struct HiddenIndexInfo*)&pIdxInfo[1]; |
| @@ -170186,14 +170856,11 @@ | ||
| 170186 | 170856 | ** struct, the contents of WhereInfo.a[], the WhereClause structure |
| 170187 | 170857 | ** and the WhereMaskSet structure. Since WhereClause contains an 8-byte |
| 170188 | 170858 | ** field (type Bitmask) it must be aligned on an 8-byte boundary on |
| 170189 | 170859 | ** some architectures. Hence the ROUND8() below. |
| 170190 | 170860 | */ |
| 170191 | - nByteWInfo = ROUND8P(sizeof(WhereInfo)); | |
| 170192 | - if( nTabList>1 ){ | |
| 170193 | - nByteWInfo = ROUND8P(nByteWInfo + (nTabList-1)*sizeof(WhereLevel)); | |
| 170194 | - } | |
| 170861 | + nByteWInfo = SZ_WHEREINFO(nTabList); | |
| 170195 | 170862 | pWInfo = sqlite3DbMallocRawNN(db, nByteWInfo + sizeof(WhereLoop)); |
| 170196 | 170863 | if( db->mallocFailed ){ |
| 170197 | 170864 | sqlite3DbFree(db, pWInfo); |
| 170198 | 170865 | pWInfo = 0; |
| 170199 | 170866 | goto whereBeginError; |
| @@ -172141,11 +172808,11 @@ | ||
| 172141 | 172808 | |
| 172142 | 172809 | p->pSrc = 0; |
| 172143 | 172810 | p->pWhere = 0; |
| 172144 | 172811 | p->pGroupBy = 0; |
| 172145 | 172812 | p->pHaving = 0; |
| 172146 | - p->selFlags &= ~SF_Aggregate; | |
| 172813 | + p->selFlags &= ~(u32)SF_Aggregate; | |
| 172147 | 172814 | p->selFlags |= SF_WinRewrite; |
| 172148 | 172815 | |
| 172149 | 172816 | /* Create the ORDER BY clause for the sub-select. This is the concatenation |
| 172150 | 172817 | ** of the window PARTITION and ORDER BY clauses. Then, if this makes it |
| 172151 | 172818 | ** redundant, remove the ORDER BY from the parent SELECT. */ |
| @@ -178280,12 +178947,12 @@ | ||
| 178280 | 178947 | pRhs = sqlite3SelectNew(pParse,0,pFrom,0,0,0,0,0,0); |
| 178281 | 178948 | } |
| 178282 | 178949 | if( pRhs ){ |
| 178283 | 178950 | pRhs->op = (u8)yymsp[-1].minor.yy502; |
| 178284 | 178951 | pRhs->pPrior = pLhs; |
| 178285 | - if( ALWAYS(pLhs) ) pLhs->selFlags &= ~SF_MultiValue; | |
| 178286 | - pRhs->selFlags &= ~SF_MultiValue; | |
| 178952 | + if( ALWAYS(pLhs) ) pLhs->selFlags &= ~(u32)SF_MultiValue; | |
| 178953 | + pRhs->selFlags &= ~(u32)SF_MultiValue; | |
| 178287 | 178954 | if( yymsp[-1].minor.yy502!=TK_ALL ) pParse->hasCompound = 1; |
| 178288 | 178955 | }else{ |
| 178289 | 178956 | sqlite3SelectDelete(pParse->db, pLhs); |
| 178290 | 178957 | } |
| 178291 | 178958 | yymsp[-2].minor.yy637 = pRhs; |
| @@ -181025,11 +181692,15 @@ | ||
| 181025 | 181692 | tokenType = analyzeOverKeyword((const u8*)&zSql[4], lastTokenParsed); |
| 181026 | 181693 | }else if( tokenType==TK_FILTER ){ |
| 181027 | 181694 | assert( n==6 ); |
| 181028 | 181695 | tokenType = analyzeFilterKeyword((const u8*)&zSql[6], lastTokenParsed); |
| 181029 | 181696 | #endif /* SQLITE_OMIT_WINDOWFUNC */ |
| 181030 | - }else if( tokenType==TK_COMMENT && (db->flags & SQLITE_Comments)!=0 ){ | |
| 181697 | + }else if( tokenType==TK_COMMENT | |
| 181698 | + && (db->init.busy || (db->flags & SQLITE_Comments)!=0) | |
| 181699 | + ){ | |
| 181700 | + /* Ignore SQL comments if either (1) we are reparsing the schema or | |
| 181701 | + ** (2) SQLITE_DBCONFIG_ENABLE_COMMENTS is turned on (the default). */ | |
| 181031 | 181702 | zSql += n; |
| 181032 | 181703 | continue; |
| 181033 | 181704 | }else if( tokenType!=TK_QNUMBER ){ |
| 181034 | 181705 | Token x; |
| 181035 | 181706 | x.z = zSql; |
| @@ -181920,10 +182591,18 @@ | ||
| 181920 | 182591 | } |
| 181921 | 182592 | #endif |
| 181922 | 182593 | if( rc==SQLITE_OK ){ |
| 181923 | 182594 | sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage, |
| 181924 | 182595 | sqlite3GlobalConfig.szPage, sqlite3GlobalConfig.nPage); |
| 182596 | +#ifdef SQLITE_EXTRA_INIT_MUTEXED | |
| 182597 | + { | |
| 182598 | + int SQLITE_EXTRA_INIT_MUTEXED(const char*); | |
| 182599 | + rc = SQLITE_EXTRA_INIT_MUTEXED(0); | |
| 182600 | + } | |
| 182601 | +#endif | |
| 182602 | + } | |
| 182603 | + if( rc==SQLITE_OK ){ | |
| 181925 | 182604 | sqlite3MemoryBarrier(); |
| 181926 | 182605 | sqlite3GlobalConfig.isInit = 1; |
| 181927 | 182606 | #ifdef SQLITE_EXTRA_INIT |
| 181928 | 182607 | bRunExtraInit = 1; |
| 181929 | 182608 | #endif |
| @@ -183396,10 +184075,13 @@ | ||
| 183396 | 184075 | sqlite3_mutex_enter(db->mutex); |
| 183397 | 184076 | db->busyHandler.xBusyHandler = xBusy; |
| 183398 | 184077 | db->busyHandler.pBusyArg = pArg; |
| 183399 | 184078 | db->busyHandler.nBusy = 0; |
| 183400 | 184079 | db->busyTimeout = 0; |
| 184080 | +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT | |
| 184081 | + db->setlkTimeout = 0; | |
| 184082 | +#endif | |
| 183401 | 184083 | sqlite3_mutex_leave(db->mutex); |
| 183402 | 184084 | return SQLITE_OK; |
| 183403 | 184085 | } |
| 183404 | 184086 | |
| 183405 | 184087 | #ifndef SQLITE_OMIT_PROGRESS_CALLBACK |
| @@ -183445,15 +184127,50 @@ | ||
| 183445 | 184127 | #endif |
| 183446 | 184128 | if( ms>0 ){ |
| 183447 | 184129 | sqlite3_busy_handler(db, (int(*)(void*,int))sqliteDefaultBusyCallback, |
| 183448 | 184130 | (void*)db); |
| 183449 | 184131 | db->busyTimeout = ms; |
| 184132 | +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT | |
| 184133 | + db->setlkTimeout = ms; | |
| 184134 | +#endif | |
| 183450 | 184135 | }else{ |
| 183451 | 184136 | sqlite3_busy_handler(db, 0, 0); |
| 183452 | 184137 | } |
| 183453 | 184138 | return SQLITE_OK; |
| 183454 | 184139 | } |
| 184140 | + | |
| 184141 | +/* | |
| 184142 | +** Set the setlk timeout value. | |
| 184143 | +*/ | |
| 184144 | +SQLITE_API int sqlite3_setlk_timeout(sqlite3 *db, int ms, int flags){ | |
| 184145 | +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT | |
| 184146 | + int iDb; | |
| 184147 | + int bBOC = ((flags & SQLITE_SETLK_BLOCK_ON_CONNECT) ? 1 : 0); | |
| 184148 | +#endif | |
| 184149 | +#ifdef SQLITE_ENABLE_API_ARMOR | |
| 184150 | + if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; | |
| 184151 | +#endif | |
| 184152 | + if( ms<-1 ) return SQLITE_RANGE; | |
| 184153 | +#ifdef SQLITE_ENABLE_SETLK_TIMEOUT | |
| 184154 | + db->setlkTimeout = ms; | |
| 184155 | + db->setlkFlags = flags; | |
| 184156 | + sqlite3BtreeEnterAll(db); | |
| 184157 | + for(iDb=0; iDb<db->nDb; iDb++){ | |
| 184158 | + Btree *pBt = db->aDb[iDb].pBt; | |
| 184159 | + if( pBt ){ | |
| 184160 | + sqlite3_file *fd = sqlite3PagerFile(sqlite3BtreePager(pBt)); | |
| 184161 | + sqlite3OsFileControlHint(fd, SQLITE_FCNTL_BLOCK_ON_CONNECT, (void*)&bBOC); | |
| 184162 | + } | |
| 184163 | + } | |
| 184164 | + sqlite3BtreeLeaveAll(db); | |
| 184165 | +#endif | |
| 184166 | +#if !defined(SQLITE_ENABLE_API_ARMOR) && !defined(SQLITE_ENABLE_SETLK_TIMEOUT) | |
| 184167 | + UNUSED_PARAMETER(db); | |
| 184168 | + UNUSED_PARAMETER(flags); | |
| 184169 | +#endif | |
| 184170 | + return SQLITE_OK; | |
| 184171 | +} | |
| 183455 | 184172 | |
| 183456 | 184173 | /* |
| 183457 | 184174 | ** Cause any pending operation to stop at its earliest opportunity. |
| 183458 | 184175 | */ |
| 183459 | 184176 | SQLITE_API void sqlite3_interrupt(sqlite3 *db){ |
| @@ -185416,11 +186133,11 @@ | ||
| 185416 | 186133 | }else if( pData==0 ){ |
| 185417 | 186134 | sqlite3_mutex_leave(db->mutex); |
| 185418 | 186135 | return SQLITE_OK; |
| 185419 | 186136 | }else{ |
| 185420 | 186137 | size_t n = strlen(zName); |
| 185421 | - p = sqlite3_malloc64( sizeof(DbClientData)+n+1 ); | |
| 186138 | + p = sqlite3_malloc64( SZ_DBCLIENTDATA(n+1) ); | |
| 185422 | 186139 | if( p==0 ){ |
| 185423 | 186140 | if( xDestructor ) xDestructor(pData); |
| 185424 | 186141 | sqlite3_mutex_leave(db->mutex); |
| 185425 | 186142 | return SQLITE_NOMEM; |
| 185426 | 186143 | } |
| @@ -185782,12 +186499,12 @@ | ||
| 185782 | 186499 | #endif |
| 185783 | 186500 | |
| 185784 | 186501 | /* sqlite3_test_control(SQLITE_TESTCTRL_FK_NO_ACTION, sqlite3 *db, int b); |
| 185785 | 186502 | ** |
| 185786 | 186503 | ** If b is true, then activate the SQLITE_FkNoAction setting. If b is |
| 185787 | - ** false then clearn that setting. If the SQLITE_FkNoAction setting is | |
| 185788 | - ** abled, all foreign key ON DELETE and ON UPDATE actions behave as if | |
| 186504 | + ** false then clear that setting. If the SQLITE_FkNoAction setting is | |
| 186505 | + ** enabled, all foreign key ON DELETE and ON UPDATE actions behave as if | |
| 185789 | 186506 | ** they were NO ACTION, regardless of how they are defined. |
| 185790 | 186507 | ** |
| 185791 | 186508 | ** NB: One must usually run "PRAGMA writable_schema=RESET" after |
| 185792 | 186509 | ** using this test-control, before it will take full effect. failing |
| 185793 | 186510 | ** to reset the schema can result in some unexpected behavior. |
| @@ -187130,11 +187847,11 @@ | ||
| 187130 | 187847 | ** } |
| 187131 | 187848 | ** |
| 187132 | 187849 | ** Here, array { X } means zero or more occurrences of X, adjacent in |
| 187133 | 187850 | ** memory. A "position" is an index of a token in the token stream |
| 187134 | 187851 | ** generated by the tokenizer. Note that POS_END and POS_COLUMN occur |
| 187135 | -** in the same logical place as the position element, and act as sentinals | |
| 187852 | +** in the same logical place as the position element, and act as sentinels | |
| 187136 | 187853 | ** ending a position list array. POS_END is 0. POS_COLUMN is 1. |
| 187137 | 187854 | ** The positions numbers are not stored literally but rather as two more |
| 187138 | 187855 | ** than the difference from the prior position, or the just the position plus |
| 187139 | 187856 | ** 2 for the first position. Example: |
| 187140 | 187857 | ** |
| @@ -187817,10 +188534,23 @@ | ||
| 187817 | 188534 | |
| 187818 | 188535 | #define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32)) |
| 187819 | 188536 | #define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64) |
| 187820 | 188537 | |
| 187821 | 188538 | #define deliberate_fall_through |
| 188539 | + | |
| 188540 | +/* | |
| 188541 | +** Macros needed to provide flexible arrays in a portable way | |
| 188542 | +*/ | |
| 188543 | +#ifndef offsetof | |
| 188544 | +# define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD)) | |
| 188545 | +#endif | |
| 188546 | +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) | |
| 188547 | +# define FLEXARRAY | |
| 188548 | +#else | |
| 188549 | +# define FLEXARRAY 1 | |
| 188550 | +#endif | |
| 188551 | + | |
| 187822 | 188552 | |
| 187823 | 188553 | #endif /* SQLITE_AMALGAMATION */ |
| 187824 | 188554 | |
| 187825 | 188555 | #ifdef SQLITE_DEBUG |
| 187826 | 188556 | SQLITE_PRIVATE int sqlite3Fts3Corrupt(void); |
| @@ -187922,11 +188652,11 @@ | ||
| 187922 | 188652 | int inTransaction; /* True after xBegin but before xCommit/xRollback */ |
| 187923 | 188653 | int mxSavepoint; /* Largest valid xSavepoint integer */ |
| 187924 | 188654 | #endif |
| 187925 | 188655 | |
| 187926 | 188656 | #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) |
| 187927 | - /* True to disable the incremental doclist optimization. This is controled | |
| 188657 | + /* True to disable the incremental doclist optimization. This is controlled | |
| 187928 | 188658 | ** by special insert command 'test-no-incr-doclist'. */ |
| 187929 | 188659 | int bNoIncrDoclist; |
| 187930 | 188660 | |
| 187931 | 188661 | /* Number of segments in a level */ |
| 187932 | 188662 | int nMergeCount; |
| @@ -187974,11 +188704,11 @@ | ||
| 187974 | 188704 | #define FTS3_EVAL_NEXT 1 |
| 187975 | 188705 | #define FTS3_EVAL_MATCHINFO 2 |
| 187976 | 188706 | |
| 187977 | 188707 | /* |
| 187978 | 188708 | ** The Fts3Cursor.eSearch member is always set to one of the following. |
| 187979 | -** Actualy, Fts3Cursor.eSearch can be greater than or equal to | |
| 188709 | +** Actually, Fts3Cursor.eSearch can be greater than or equal to | |
| 187980 | 188710 | ** FTS3_FULLTEXT_SEARCH. If so, then Fts3Cursor.eSearch - 2 is the index |
| 187981 | 188711 | ** of the column to be searched. For example, in |
| 187982 | 188712 | ** |
| 187983 | 188713 | ** CREATE VIRTUAL TABLE ex1 USING fts3(a,b,c,d); |
| 187984 | 188714 | ** SELECT docid FROM ex1 WHERE b MATCH 'one two three'; |
| @@ -188047,12 +188777,16 @@ | ||
| 188047 | 188777 | /* Variables below this point are populated by fts3_expr.c when parsing |
| 188048 | 188778 | ** a MATCH expression. Everything above is part of the evaluation phase. |
| 188049 | 188779 | */ |
| 188050 | 188780 | int nToken; /* Number of tokens in the phrase */ |
| 188051 | 188781 | int iColumn; /* Index of column this phrase must match */ |
| 188052 | - Fts3PhraseToken aToken[1]; /* One entry for each token in the phrase */ | |
| 188782 | + Fts3PhraseToken aToken[FLEXARRAY]; /* One for each token in the phrase */ | |
| 188053 | 188783 | }; |
| 188784 | + | |
| 188785 | +/* Size (in bytes) of an Fts3Phrase object large enough to hold N tokens */ | |
| 188786 | +#define SZ_FTS3PHRASE(N) \ | |
| 188787 | + (offsetof(Fts3Phrase,aToken)+(N)*sizeof(Fts3PhraseToken)) | |
| 188054 | 188788 | |
| 188055 | 188789 | /* |
| 188056 | 188790 | ** A tree of these objects forms the RHS of a MATCH operator. |
| 188057 | 188791 | ** |
| 188058 | 188792 | ** If Fts3Expr.eType is FTSQUERY_PHRASE and isLoaded is true, then aDoclist |
| @@ -190627,11 +191361,11 @@ | ||
| 190627 | 191361 | ** |
| 190628 | 191362 | ** The space required to store the output is therefore the sum of the |
| 190629 | 191363 | ** sizes of the two inputs, plus enough space for exactly one of the input |
| 190630 | 191364 | ** docids to grow. |
| 190631 | 191365 | ** |
| 190632 | - ** A symetric argument may be made if the doclists are in descending | |
| 191366 | + ** A symmetric argument may be made if the doclists are in descending | |
| 190633 | 191367 | ** order. |
| 190634 | 191368 | */ |
| 190635 | 191369 | aOut = sqlite3_malloc64((i64)n1+n2+FTS3_VARINT_MAX-1+FTS3_BUFFER_PADDING); |
| 190636 | 191370 | if( !aOut ) return SQLITE_NOMEM; |
| 190637 | 191371 | |
| @@ -192725,11 +193459,11 @@ | ||
| 192725 | 193459 | ** |
| 192726 | 193460 | ** * features at least one token that uses an incremental doclist, and |
| 192727 | 193461 | ** |
| 192728 | 193462 | ** * does not contain any deferred tokens. |
| 192729 | 193463 | ** |
| 192730 | -** Advance it to the next matching documnent in the database and populate | |
| 193464 | +** Advance it to the next matching document in the database and populate | |
| 192731 | 193465 | ** the Fts3Doclist.pList and nList fields. |
| 192732 | 193466 | ** |
| 192733 | 193467 | ** If there is no "next" entry and no error occurs, then *pbEof is set to |
| 192734 | 193468 | ** 1 before returning. Otherwise, if no error occurs and the iterator is |
| 192735 | 193469 | ** successfully advanced, *pbEof is set to 0. |
| @@ -193732,11 +194466,11 @@ | ||
| 193732 | 194466 | |
| 193733 | 194467 | return rc; |
| 193734 | 194468 | } |
| 193735 | 194469 | |
| 193736 | 194470 | /* |
| 193737 | -** Restart interation for expression pExpr so that the next call to | |
| 194471 | +** Restart iteration for expression pExpr so that the next call to | |
| 193738 | 194472 | ** fts3EvalNext() visits the first row. Do not allow incremental |
| 193739 | 194473 | ** loading or merging of phrase doclists for this iteration. |
| 193740 | 194474 | ** |
| 193741 | 194475 | ** If *pRc is other than SQLITE_OK when this function is called, it is |
| 193742 | 194476 | ** a no-op. If an error occurs within this function, *pRc is set to an |
| @@ -194923,10 +195657,27 @@ | ||
| 194923 | 195657 | /* |
| 194924 | 195658 | ** Function getNextNode(), which is called by fts3ExprParse(), may itself |
| 194925 | 195659 | ** call fts3ExprParse(). So this forward declaration is required. |
| 194926 | 195660 | */ |
| 194927 | 195661 | static int fts3ExprParse(ParseContext *, const char *, int, Fts3Expr **, int *); |
| 195662 | + | |
| 195663 | +/* | |
| 195664 | +** Search buffer z[], size n, for a '"' character. Or, if enable_parenthesis | |
| 195665 | +** is defined, search for '(' and ')' as well. Return the index of the first | |
| 195666 | +** such character in the buffer. If there is no such character, return -1. | |
| 195667 | +*/ | |
| 195668 | +static int findBarredChar(const char *z, int n){ | |
| 195669 | + int ii; | |
| 195670 | + for(ii=0; ii<n; ii++){ | |
| 195671 | + if( (z[ii]=='"') | |
| 195672 | + || (sqlite3_fts3_enable_parentheses && (z[ii]=='(' || z[ii]==')')) | |
| 195673 | + ){ | |
| 195674 | + return ii; | |
| 195675 | + } | |
| 195676 | + } | |
| 195677 | + return -1; | |
| 195678 | +} | |
| 194928 | 195679 | |
| 194929 | 195680 | /* |
| 194930 | 195681 | ** Extract the next token from buffer z (length n) using the tokenizer |
| 194931 | 195682 | ** and other information (column names etc.) in pParse. Create an Fts3Expr |
| 194932 | 195683 | ** structure of type FTSQUERY_PHRASE containing a phrase consisting of this |
| @@ -194948,38 +195699,42 @@ | ||
| 194948 | 195699 | sqlite3_tokenizer *pTokenizer = pParse->pTokenizer; |
| 194949 | 195700 | sqlite3_tokenizer_module const *pModule = pTokenizer->pModule; |
| 194950 | 195701 | int rc; |
| 194951 | 195702 | sqlite3_tokenizer_cursor *pCursor; |
| 194952 | 195703 | Fts3Expr *pRet = 0; |
| 194953 | - int i = 0; | |
| 194954 | - | |
| 194955 | - /* Set variable i to the maximum number of bytes of input to tokenize. */ | |
| 194956 | - for(i=0; i<n; i++){ | |
| 194957 | - if( sqlite3_fts3_enable_parentheses && (z[i]=='(' || z[i]==')') ) break; | |
| 194958 | - if( z[i]=='"' ) break; | |
| 194959 | - } | |
| 194960 | - | |
| 194961 | - *pnConsumed = i; | |
| 194962 | - rc = sqlite3Fts3OpenTokenizer(pTokenizer, pParse->iLangid, z, i, &pCursor); | |
| 195704 | + | |
| 195705 | + *pnConsumed = n; | |
| 195706 | + rc = sqlite3Fts3OpenTokenizer(pTokenizer, pParse->iLangid, z, n, &pCursor); | |
| 194963 | 195707 | if( rc==SQLITE_OK ){ |
| 194964 | 195708 | const char *zToken; |
| 194965 | 195709 | int nToken = 0, iStart = 0, iEnd = 0, iPosition = 0; |
| 194966 | 195710 | sqlite3_int64 nByte; /* total space to allocate */ |
| 194967 | 195711 | |
| 194968 | 195712 | rc = pModule->xNext(pCursor, &zToken, &nToken, &iStart, &iEnd, &iPosition); |
| 194969 | 195713 | if( rc==SQLITE_OK ){ |
| 194970 | - nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase) + nToken; | |
| 195714 | + /* Check that this tokenization did not gobble up any " characters. Or, | |
| 195715 | + ** if enable_parenthesis is true, that it did not gobble up any | |
| 195716 | + ** open or close parenthesis characters either. If it did, call | |
| 195717 | + ** getNextToken() again, but pass only that part of the input buffer | |
| 195718 | + ** up to the first such character. */ | |
| 195719 | + int iBarred = findBarredChar(z, iEnd); | |
| 195720 | + if( iBarred>=0 ){ | |
| 195721 | + pModule->xClose(pCursor); | |
| 195722 | + return getNextToken(pParse, iCol, z, iBarred, ppExpr, pnConsumed); | |
| 195723 | + } | |
| 195724 | + | |
| 195725 | + nByte = sizeof(Fts3Expr) + SZ_FTS3PHRASE(1) + nToken; | |
| 194971 | 195726 | pRet = (Fts3Expr *)sqlite3Fts3MallocZero(nByte); |
| 194972 | 195727 | if( !pRet ){ |
| 194973 | 195728 | rc = SQLITE_NOMEM; |
| 194974 | 195729 | }else{ |
| 194975 | 195730 | pRet->eType = FTSQUERY_PHRASE; |
| 194976 | 195731 | pRet->pPhrase = (Fts3Phrase *)&pRet[1]; |
| 194977 | 195732 | pRet->pPhrase->nToken = 1; |
| 194978 | 195733 | pRet->pPhrase->iColumn = iCol; |
| 194979 | 195734 | pRet->pPhrase->aToken[0].n = nToken; |
| 194980 | - pRet->pPhrase->aToken[0].z = (char *)&pRet->pPhrase[1]; | |
| 195735 | + pRet->pPhrase->aToken[0].z = (char*)&pRet->pPhrase->aToken[1]; | |
| 194981 | 195736 | memcpy(pRet->pPhrase->aToken[0].z, zToken, nToken); |
| 194982 | 195737 | |
| 194983 | 195738 | if( iEnd<n && z[iEnd]=='*' ){ |
| 194984 | 195739 | pRet->pPhrase->aToken[0].isPrefix = 1; |
| 194985 | 195740 | iEnd++; |
| @@ -194999,11 +195754,15 @@ | ||
| 194999 | 195754 | } |
| 195000 | 195755 | } |
| 195001 | 195756 | |
| 195002 | 195757 | } |
| 195003 | 195758 | *pnConsumed = iEnd; |
| 195004 | - }else if( i && rc==SQLITE_DONE ){ | |
| 195759 | + }else if( n && rc==SQLITE_DONE ){ | |
| 195760 | + int iBarred = findBarredChar(z, n); | |
| 195761 | + if( iBarred>=0 ){ | |
| 195762 | + *pnConsumed = iBarred; | |
| 195763 | + } | |
| 195005 | 195764 | rc = SQLITE_OK; |
| 195006 | 195765 | } |
| 195007 | 195766 | |
| 195008 | 195767 | pModule->xClose(pCursor); |
| 195009 | 195768 | } |
| @@ -195048,11 +195807,11 @@ | ||
| 195048 | 195807 | Fts3Expr *p = 0; |
| 195049 | 195808 | sqlite3_tokenizer_cursor *pCursor = 0; |
| 195050 | 195809 | char *zTemp = 0; |
| 195051 | 195810 | i64 nTemp = 0; |
| 195052 | 195811 | |
| 195053 | - const int nSpace = sizeof(Fts3Expr) + sizeof(Fts3Phrase); | |
| 195812 | + const int nSpace = sizeof(Fts3Expr) + SZ_FTS3PHRASE(1); | |
| 195054 | 195813 | int nToken = 0; |
| 195055 | 195814 | |
| 195056 | 195815 | /* The final Fts3Expr data structure, including the Fts3Phrase, |
| 195057 | 195816 | ** Fts3PhraseToken structures token buffers are all stored as a single |
| 195058 | 195817 | ** allocation so that the expression can be freed with a single call to |
| @@ -195420,11 +196179,11 @@ | ||
| 195420 | 196179 | int eType = p->eType; |
| 195421 | 196180 | isPhrase = (eType==FTSQUERY_PHRASE || p->pLeft); |
| 195422 | 196181 | |
| 195423 | 196182 | /* The isRequirePhrase variable is set to true if a phrase or |
| 195424 | 196183 | ** an expression contained in parenthesis is required. If a |
| 195425 | - ** binary operator (AND, OR, NOT or NEAR) is encounted when | |
| 196184 | + ** binary operator (AND, OR, NOT or NEAR) is encountered when | |
| 195426 | 196185 | ** isRequirePhrase is set, this is a syntax error. |
| 195427 | 196186 | */ |
| 195428 | 196187 | if( !isPhrase && isRequirePhrase ){ |
| 195429 | 196188 | sqlite3Fts3ExprFree(p); |
| 195430 | 196189 | rc = SQLITE_ERROR; |
| @@ -196002,11 +196761,10 @@ | ||
| 196002 | 196761 | pTokenizer, 0, azCol, 0, nCol, nCol, zExpr, nExpr, &pExpr |
| 196003 | 196762 | ); |
| 196004 | 196763 | } |
| 196005 | 196764 | |
| 196006 | 196765 | if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM ){ |
| 196007 | - sqlite3Fts3ExprFree(pExpr); | |
| 196008 | 196766 | sqlite3_result_error(context, "Error parsing expression", -1); |
| 196009 | 196767 | }else if( rc==SQLITE_NOMEM || !(zBuf = exprToString(pExpr, 0)) ){ |
| 196010 | 196768 | sqlite3_result_error_nomem(context); |
| 196011 | 196769 | }else{ |
| 196012 | 196770 | sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); |
| @@ -196245,11 +197003,11 @@ | ||
| 196245 | 197003 | pEntry->count++; |
| 196246 | 197004 | pEntry->chain = pNew; |
| 196247 | 197005 | } |
| 196248 | 197006 | |
| 196249 | 197007 | |
| 196250 | -/* Resize the hash table so that it cantains "new_size" buckets. | |
| 197008 | +/* Resize the hash table so that it contains "new_size" buckets. | |
| 196251 | 197009 | ** "new_size" must be a power of 2. The hash table might fail |
| 196252 | 197010 | ** to resize if sqliteMalloc() fails. |
| 196253 | 197011 | ** |
| 196254 | 197012 | ** Return non-zero if a memory allocation error occurs. |
| 196255 | 197013 | */ |
| @@ -196700,11 +197458,11 @@ | ||
| 196700 | 197458 | isConsonant(z+2); |
| 196701 | 197459 | } |
| 196702 | 197460 | |
| 196703 | 197461 | /* |
| 196704 | 197462 | ** If the word ends with zFrom and xCond() is true for the stem |
| 196705 | -** of the word that preceeds the zFrom ending, then change the | |
| 197463 | +** of the word that precedes the zFrom ending, then change the | |
| 196706 | 197464 | ** ending to zTo. |
| 196707 | 197465 | ** |
| 196708 | 197466 | ** The input word *pz and zFrom are both in reverse order. zTo |
| 196709 | 197467 | ** is in normal order. |
| 196710 | 197468 | ** |
| @@ -202283,11 +203041,11 @@ | ||
| 202283 | 203041 | ** previous term. Before this function returns, it is updated to contain a |
| 202284 | 203042 | ** copy of zTerm/nTerm. |
| 202285 | 203043 | ** |
| 202286 | 203044 | ** It is assumed that the buffer associated with pNode is already large |
| 202287 | 203045 | ** enough to accommodate the new entry. The buffer associated with pPrev |
| 202288 | -** is extended by this function if requrired. | |
| 203046 | +** is extended by this function if required. | |
| 202289 | 203047 | ** |
| 202290 | 203048 | ** If an error (i.e. OOM condition) occurs, an SQLite error code is |
| 202291 | 203049 | ** returned. Otherwise, SQLITE_OK. |
| 202292 | 203050 | */ |
| 202293 | 203051 | static int fts3AppendToNode( |
| @@ -203946,11 +204704,11 @@ | ||
| 203946 | 204704 | #endif |
| 203947 | 204705 | |
| 203948 | 204706 | /* |
| 203949 | 204707 | ** SQLite value pRowid contains the rowid of a row that may or may not be |
| 203950 | 204708 | ** present in the FTS3 table. If it is, delete it and adjust the contents |
| 203951 | -** of subsiduary data structures accordingly. | |
| 204709 | +** of subsidiary data structures accordingly. | |
| 203952 | 204710 | */ |
| 203953 | 204711 | static int fts3DeleteByRowid( |
| 203954 | 204712 | Fts3Table *p, |
| 203955 | 204713 | sqlite3_value *pRowid, |
| 203956 | 204714 | int *pnChng, /* IN/OUT: Decrement if row is deleted */ |
| @@ -204272,12 +205030,16 @@ | ||
| 204272 | 205030 | struct MatchinfoBuffer { |
| 204273 | 205031 | u8 aRef[3]; |
| 204274 | 205032 | int nElem; |
| 204275 | 205033 | int bGlobal; /* Set if global data is loaded */ |
| 204276 | 205034 | char *zMatchinfo; |
| 204277 | - u32 aMatchinfo[1]; | |
| 205035 | + u32 aMI[FLEXARRAY]; | |
| 204278 | 205036 | }; |
| 205037 | + | |
| 205038 | +/* Size (in bytes) of a MatchinfoBuffer sufficient for N elements */ | |
| 205039 | +#define SZ_MATCHINFOBUFFER(N) \ | |
| 205040 | + (offsetof(MatchinfoBuffer,aMI)+(((N)+1)/2)*sizeof(u64)) | |
| 204279 | 205041 | |
| 204280 | 205042 | |
| 204281 | 205043 | /* |
| 204282 | 205044 | ** The snippet() and offsets() functions both return text values. An instance |
| 204283 | 205045 | ** of the following structure is used to accumulate those values while the |
| @@ -204299,17 +205061,17 @@ | ||
| 204299 | 205061 | ** Allocate a two-slot MatchinfoBuffer object. |
| 204300 | 205062 | */ |
| 204301 | 205063 | static MatchinfoBuffer *fts3MIBufferNew(size_t nElem, const char *zMatchinfo){ |
| 204302 | 205064 | MatchinfoBuffer *pRet; |
| 204303 | 205065 | sqlite3_int64 nByte = sizeof(u32) * (2*(sqlite3_int64)nElem + 1) |
| 204304 | - + sizeof(MatchinfoBuffer); | |
| 205066 | + + SZ_MATCHINFOBUFFER(1); | |
| 204305 | 205067 | sqlite3_int64 nStr = strlen(zMatchinfo); |
| 204306 | 205068 | |
| 204307 | 205069 | pRet = sqlite3Fts3MallocZero(nByte + nStr+1); |
| 204308 | 205070 | if( pRet ){ |
| 204309 | - pRet->aMatchinfo[0] = (u8*)(&pRet->aMatchinfo[1]) - (u8*)pRet; | |
| 204310 | - pRet->aMatchinfo[1+nElem] = pRet->aMatchinfo[0] | |
| 205071 | + pRet->aMI[0] = (u8*)(&pRet->aMI[1]) - (u8*)pRet; | |
| 205072 | + pRet->aMI[1+nElem] = pRet->aMI[0] | |
| 204311 | 205073 | + sizeof(u32)*((int)nElem+1); |
| 204312 | 205074 | pRet->nElem = (int)nElem; |
| 204313 | 205075 | pRet->zMatchinfo = ((char*)pRet) + nByte; |
| 204314 | 205076 | memcpy(pRet->zMatchinfo, zMatchinfo, nStr+1); |
| 204315 | 205077 | pRet->aRef[0] = 1; |
| @@ -204319,14 +205081,14 @@ | ||
| 204319 | 205081 | } |
| 204320 | 205082 | |
| 204321 | 205083 | static void fts3MIBufferFree(void *p){ |
| 204322 | 205084 | MatchinfoBuffer *pBuf = (MatchinfoBuffer*)((u8*)p - ((u32*)p)[-1]); |
| 204323 | 205085 | |
| 204324 | - assert( (u32*)p==&pBuf->aMatchinfo[1] | |
| 204325 | - || (u32*)p==&pBuf->aMatchinfo[pBuf->nElem+2] | |
| 205086 | + assert( (u32*)p==&pBuf->aMI[1] | |
| 205087 | + || (u32*)p==&pBuf->aMI[pBuf->nElem+2] | |
| 204326 | 205088 | ); |
| 204327 | - if( (u32*)p==&pBuf->aMatchinfo[1] ){ | |
| 205089 | + if( (u32*)p==&pBuf->aMI[1] ){ | |
| 204328 | 205090 | pBuf->aRef[1] = 0; |
| 204329 | 205091 | }else{ |
| 204330 | 205092 | pBuf->aRef[2] = 0; |
| 204331 | 205093 | } |
| 204332 | 205094 | |
| @@ -204339,32 +205101,32 @@ | ||
| 204339 | 205101 | void (*xRet)(void*) = 0; |
| 204340 | 205102 | u32 *aOut = 0; |
| 204341 | 205103 | |
| 204342 | 205104 | if( p->aRef[1]==0 ){ |
| 204343 | 205105 | p->aRef[1] = 1; |
| 204344 | - aOut = &p->aMatchinfo[1]; | |
| 205106 | + aOut = &p->aMI[1]; | |
| 204345 | 205107 | xRet = fts3MIBufferFree; |
| 204346 | 205108 | } |
| 204347 | 205109 | else if( p->aRef[2]==0 ){ |
| 204348 | 205110 | p->aRef[2] = 1; |
| 204349 | - aOut = &p->aMatchinfo[p->nElem+2]; | |
| 205111 | + aOut = &p->aMI[p->nElem+2]; | |
| 204350 | 205112 | xRet = fts3MIBufferFree; |
| 204351 | 205113 | }else{ |
| 204352 | 205114 | aOut = (u32*)sqlite3_malloc64(p->nElem * sizeof(u32)); |
| 204353 | 205115 | if( aOut ){ |
| 204354 | 205116 | xRet = sqlite3_free; |
| 204355 | - if( p->bGlobal ) memcpy(aOut, &p->aMatchinfo[1], p->nElem*sizeof(u32)); | |
| 205117 | + if( p->bGlobal ) memcpy(aOut, &p->aMI[1], p->nElem*sizeof(u32)); | |
| 204356 | 205118 | } |
| 204357 | 205119 | } |
| 204358 | 205120 | |
| 204359 | 205121 | *paOut = aOut; |
| 204360 | 205122 | return xRet; |
| 204361 | 205123 | } |
| 204362 | 205124 | |
| 204363 | 205125 | static void fts3MIBufferSetGlobal(MatchinfoBuffer *p){ |
| 204364 | 205126 | p->bGlobal = 1; |
| 204365 | - memcpy(&p->aMatchinfo[2+p->nElem], &p->aMatchinfo[1], p->nElem*sizeof(u32)); | |
| 205127 | + memcpy(&p->aMI[2+p->nElem], &p->aMI[1], p->nElem*sizeof(u32)); | |
| 204366 | 205128 | } |
| 204367 | 205129 | |
| 204368 | 205130 | /* |
| 204369 | 205131 | ** Free a MatchinfoBuffer object allocated using fts3MIBufferNew() |
| 204370 | 205132 | */ |
| @@ -204775,11 +205537,11 @@ | ||
| 204775 | 205537 | if( nAppend<0 ){ |
| 204776 | 205538 | nAppend = (int)strlen(zAppend); |
| 204777 | 205539 | } |
| 204778 | 205540 | |
| 204779 | 205541 | /* If there is insufficient space allocated at StrBuffer.z, use realloc() |
| 204780 | - ** to grow the buffer until so that it is big enough to accomadate the | |
| 205542 | + ** to grow the buffer until so that it is big enough to accommodate the | |
| 204781 | 205543 | ** appended data. |
| 204782 | 205544 | */ |
| 204783 | 205545 | if( pStr->n+nAppend+1>=pStr->nAlloc ){ |
| 204784 | 205546 | sqlite3_int64 nAlloc = pStr->nAlloc+(sqlite3_int64)nAppend+100; |
| 204785 | 205547 | char *zNew = sqlite3_realloc64(pStr->z, nAlloc); |
| @@ -207150,11 +207912,11 @@ | ||
| 207150 | 207912 | ** |
| 207151 | 207913 | ** When a match if found, the matching entry is moved to become the |
| 207152 | 207914 | ** most-recently used entry if it isn't so already. |
| 207153 | 207915 | ** |
| 207154 | 207916 | ** The JsonParse object returned still belongs to the Cache and might |
| 207155 | -** be deleted at any moment. If the caller whants the JsonParse to | |
| 207917 | +** be deleted at any moment. If the caller wants the JsonParse to | |
| 207156 | 207918 | ** linger, it needs to increment the nPJRef reference counter. |
| 207157 | 207919 | */ |
| 207158 | 207920 | static JsonParse *jsonCacheSearch( |
| 207159 | 207921 | sqlite3_context *ctx, /* The SQL statement context holding the cache */ |
| 207160 | 207922 | sqlite3_value *pArg /* Function argument containing SQL text */ |
| @@ -210195,11 +210957,11 @@ | ||
| 210195 | 210957 | ** |
| 210196 | 210958 | ** This goes against all historical documentation about how the SQLite |
| 210197 | 210959 | ** JSON functions were suppose to work. From the beginning, blob was |
| 210198 | 210960 | ** reserved for expansion and a blob value should have raised an error. |
| 210199 | 210961 | ** But it did not, due to a bug. And many applications came to depend |
| 210200 | - ** upon this buggy behavior, espeically when using the CLI and reading | |
| 210962 | + ** upon this buggy behavior, especially when using the CLI and reading | |
| 210201 | 210963 | ** JSON text using readfile(), which returns a blob. For this reason |
| 210202 | 210964 | ** we will continue to support the bug moving forward. |
| 210203 | 210965 | ** See for example https://sqlite.org/forum/forumpost/012136abd5292b8d |
| 210204 | 210966 | */ |
| 210205 | 210967 | } |
| @@ -212295,10 +213057,18 @@ | ||
| 212295 | 213057 | # define NEVER(X) ((X)?(assert(0),1):0) |
| 212296 | 213058 | #else |
| 212297 | 213059 | # define ALWAYS(X) (X) |
| 212298 | 213060 | # define NEVER(X) (X) |
| 212299 | 213061 | #endif |
| 213062 | +#ifndef offsetof | |
| 213063 | +#define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD)) | |
| 213064 | +#endif | |
| 213065 | +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) | |
| 213066 | +# define FLEXARRAY | |
| 213067 | +#else | |
| 213068 | +# define FLEXARRAY 1 | |
| 213069 | +#endif | |
| 212300 | 213070 | #endif /* !defined(SQLITE_AMALGAMATION) */ |
| 212301 | 213071 | |
| 212302 | 213072 | /* Macro to check for 4-byte alignment. Only used inside of assert() */ |
| 212303 | 213073 | #ifdef SQLITE_DEBUG |
| 212304 | 213074 | # define FOUR_BYTE_ALIGNED(X) ((((char*)(X) - (char*)0) & 3)==0) |
| @@ -212615,13 +213385,17 @@ | ||
| 212615 | 213385 | struct RtreeMatchArg { |
| 212616 | 213386 | u32 iSize; /* Size of this object */ |
| 212617 | 213387 | RtreeGeomCallback cb; /* Info about the callback functions */ |
| 212618 | 213388 | int nParam; /* Number of parameters to the SQL function */ |
| 212619 | 213389 | sqlite3_value **apSqlParam; /* Original SQL parameter values */ |
| 212620 | - RtreeDValue aParam[1]; /* Values for parameters to the SQL function */ | |
| 213390 | + RtreeDValue aParam[FLEXARRAY]; /* Values for parameters to the SQL function */ | |
| 212621 | 213391 | }; |
| 212622 | 213392 | |
| 213393 | +/* Size of an RtreeMatchArg object with N parameters */ | |
| 213394 | +#define SZ_RTREEMATCHARG(N) \ | |
| 213395 | + (offsetof(RtreeMatchArg,aParam)+(N)*sizeof(RtreeDValue)) | |
| 213396 | + | |
| 212623 | 213397 | #ifndef MAX |
| 212624 | 213398 | # define MAX(x,y) ((x) < (y) ? (y) : (x)) |
| 212625 | 213399 | #endif |
| 212626 | 213400 | #ifndef MIN |
| 212627 | 213401 | # define MIN(x,y) ((x) > (y) ? (y) : (x)) |
| @@ -214306,11 +215080,11 @@ | ||
| 214306 | 215080 | |
| 214307 | 215081 | return rc; |
| 214308 | 215082 | } |
| 214309 | 215083 | |
| 214310 | 215084 | /* |
| 214311 | -** Return the N-dimensional volumn of the cell stored in *p. | |
| 215085 | +** Return the N-dimensional volume of the cell stored in *p. | |
| 214312 | 215086 | */ |
| 214313 | 215087 | static RtreeDValue cellArea(Rtree *pRtree, RtreeCell *p){ |
| 214314 | 215088 | RtreeDValue area = (RtreeDValue)1; |
| 214315 | 215089 | assert( pRtree->nDim>=1 && pRtree->nDim<=5 ); |
| 214316 | 215090 | #ifndef SQLITE_RTREE_INT_ONLY |
| @@ -216072,11 +216846,11 @@ | ||
| 216072 | 216846 | } |
| 216073 | 216847 | |
| 216074 | 216848 | /* |
| 216075 | 216849 | ** The second and subsequent arguments to this function are a printf() |
| 216076 | 216850 | ** style format string and arguments. This function formats the string and |
| 216077 | -** appends it to the report being accumuated in pCheck. | |
| 216851 | +** appends it to the report being accumulated in pCheck. | |
| 216078 | 216852 | */ |
| 216079 | 216853 | static void rtreeCheckAppendMsg(RtreeCheck *pCheck, const char *zFmt, ...){ |
| 216080 | 216854 | va_list ap; |
| 216081 | 216855 | va_start(ap, zFmt); |
| 216082 | 216856 | if( pCheck->rc==SQLITE_OK && pCheck->nErr<RTREE_CHECK_MAX_ERROR ){ |
| @@ -217260,11 +218034,11 @@ | ||
| 217260 | 218034 | |
| 217261 | 218035 | /* |
| 217262 | 218036 | ** Determine if point (x0,y0) is beneath line segment (x1,y1)->(x2,y2). |
| 217263 | 218037 | ** Returns: |
| 217264 | 218038 | ** |
| 217265 | -** +2 x0,y0 is on the line segement | |
| 218039 | +** +2 x0,y0 is on the line segment | |
| 217266 | 218040 | ** |
| 217267 | 218041 | ** +1 x0,y0 is beneath line segment |
| 217268 | 218042 | ** |
| 217269 | 218043 | ** 0 x0,y0 is not on or beneath the line segment or the line segment |
| 217270 | 218044 | ** is vertical and x0,y0 is not on the line segment |
| @@ -217366,11 +218140,11 @@ | ||
| 217366 | 218140 | } |
| 217367 | 218141 | sqlite3_free(p1); |
| 217368 | 218142 | sqlite3_free(p2); |
| 217369 | 218143 | } |
| 217370 | 218144 | |
| 217371 | -/* Objects used by the overlap algorihm. */ | |
| 218145 | +/* Objects used by the overlap algorithm. */ | |
| 217372 | 218146 | typedef struct GeoEvent GeoEvent; |
| 217373 | 218147 | typedef struct GeoSegment GeoSegment; |
| 217374 | 218148 | typedef struct GeoOverlap GeoOverlap; |
| 217375 | 218149 | struct GeoEvent { |
| 217376 | 218150 | double x; /* X coordinate at which event occurs */ |
| @@ -218413,12 +219187,11 @@ | ||
| 218413 | 219187 | RtreeGeomCallback *pGeomCtx = (RtreeGeomCallback *)sqlite3_user_data(ctx); |
| 218414 | 219188 | RtreeMatchArg *pBlob; |
| 218415 | 219189 | sqlite3_int64 nBlob; |
| 218416 | 219190 | int memErr = 0; |
| 218417 | 219191 | |
| 218418 | - nBlob = sizeof(RtreeMatchArg) + (nArg-1)*sizeof(RtreeDValue) | |
| 218419 | - + nArg*sizeof(sqlite3_value*); | |
| 219192 | + nBlob = SZ_RTREEMATCHARG(nArg) + nArg*sizeof(sqlite3_value*); | |
| 218420 | 219193 | pBlob = (RtreeMatchArg *)sqlite3_malloc64(nBlob); |
| 218421 | 219194 | if( !pBlob ){ |
| 218422 | 219195 | sqlite3_result_error_nomem(ctx); |
| 218423 | 219196 | }else{ |
| 218424 | 219197 | int i; |
| @@ -219509,11 +220282,11 @@ | ||
| 219509 | 220282 | ** to read from the original database snapshot. In other words, partially |
| 219510 | 220283 | ** applied transactions are not visible to other clients. |
| 219511 | 220284 | ** |
| 219512 | 220285 | ** "RBU" stands for "Resumable Bulk Update". As in a large database update |
| 219513 | 220286 | ** transmitted via a wireless network to a mobile device. A transaction |
| 219514 | -** applied using this extension is hence refered to as an "RBU update". | |
| 220287 | +** applied using this extension is hence referred to as an "RBU update". | |
| 219515 | 220288 | ** |
| 219516 | 220289 | ** |
| 219517 | 220290 | ** LIMITATIONS |
| 219518 | 220291 | ** |
| 219519 | 220292 | ** An "RBU update" transaction is subject to the following limitations: |
| @@ -219806,11 +220579,11 @@ | ||
| 219806 | 220579 | ** sqlite3rbu_close() returns any value other than SQLITE_OK, the contents |
| 219807 | 220580 | ** of the state tables within the state database are zeroed. This way, |
| 219808 | 220581 | ** the next call to sqlite3rbu_vacuum() opens a handle that starts a |
| 219809 | 220582 | ** new RBU vacuum operation. |
| 219810 | 220583 | ** |
| 219811 | -** As with sqlite3rbu_open(), Zipvfs users should rever to the comment | |
| 220584 | +** As with sqlite3rbu_open(), Zipvfs users should refer to the comment | |
| 219812 | 220585 | ** describing the sqlite3rbu_create_vfs() API function below for |
| 219813 | 220586 | ** a description of the complications associated with using RBU with |
| 219814 | 220587 | ** zipvfs databases. |
| 219815 | 220588 | */ |
| 219816 | 220589 | SQLITE_API sqlite3rbu *sqlite3rbu_vacuum( |
| @@ -219902,11 +220675,11 @@ | ||
| 219902 | 220675 | /* |
| 219903 | 220676 | ** Close an RBU handle. |
| 219904 | 220677 | ** |
| 219905 | 220678 | ** If the RBU update has been completely applied, mark the RBU database |
| 219906 | 220679 | ** as fully applied. Otherwise, assuming no error has occurred, save the |
| 219907 | -** current state of the RBU update appliation to the RBU database. | |
| 220680 | +** current state of the RBU update application to the RBU database. | |
| 219908 | 220681 | ** |
| 219909 | 220682 | ** If an error has already occurred as part of an sqlite3rbu_step() |
| 219910 | 220683 | ** or sqlite3rbu_open() call, or if one occurs within this function, an |
| 219911 | 220684 | ** SQLite error code is returned. Additionally, if pzErrmsg is not NULL, |
| 219912 | 220685 | ** *pzErrmsg may be set to point to a buffer containing a utf-8 formatted |
| @@ -224828,11 +225601,11 @@ | ||
| 224828 | 225601 | int rc; |
| 224829 | 225602 | rc = p->pReal->pMethods->xFileSize(p->pReal, pSize); |
| 224830 | 225603 | |
| 224831 | 225604 | /* If this is an RBU vacuum operation and this is the target database, |
| 224832 | 225605 | ** pretend that it has at least one page. Otherwise, SQLite will not |
| 224833 | - ** check for the existance of a *-wal file. rbuVfsRead() contains | |
| 225606 | + ** check for the existence of a *-wal file. rbuVfsRead() contains | |
| 224834 | 225607 | ** similar logic. */ |
| 224835 | 225608 | if( rc==SQLITE_OK && *pSize==0 |
| 224836 | 225609 | && p->pRbu && rbuIsVacuum(p->pRbu) |
| 224837 | 225610 | && (p->openFlags & SQLITE_OPEN_MAIN_DB) |
| 224838 | 225611 | ){ |
| @@ -228058,11 +228831,11 @@ | ||
| 228058 | 228831 | } |
| 228059 | 228832 | |
| 228060 | 228833 | /* |
| 228061 | 228834 | ** This function is called to initialize the SessionTable.nCol, azCol[] |
| 228062 | 228835 | ** abPK[] and azDflt[] members of SessionTable object pTab. If these |
| 228063 | -** fields are already initilialized, this function is a no-op. | |
| 228836 | +** fields are already initialized, this function is a no-op. | |
| 228064 | 228837 | ** |
| 228065 | 228838 | ** If an error occurs, an error code is stored in sqlite3_session.rc and |
| 228066 | 228839 | ** non-zero returned. Or, if no error occurs but the table has no primary |
| 228067 | 228840 | ** key, sqlite3_session.rc is left set to SQLITE_OK and non-zero returned to |
| 228068 | 228841 | ** indicate that updates on this table should be ignored. SessionTable.abPK |
| @@ -229881,11 +230654,11 @@ | ||
| 229881 | 230654 | int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */ |
| 229882 | 230655 | void **ppChangeset /* OUT: Buffer containing changeset */ |
| 229883 | 230656 | ){ |
| 229884 | 230657 | sqlite3 *db = pSession->db; /* Source database handle */ |
| 229885 | 230658 | SessionTable *pTab; /* Used to iterate through attached tables */ |
| 229886 | - SessionBuffer buf = {0,0,0}; /* Buffer in which to accumlate changeset */ | |
| 230659 | + SessionBuffer buf = {0,0,0}; /* Buffer in which to accumulate changeset */ | |
| 229887 | 230660 | int rc; /* Return code */ |
| 229888 | 230661 | |
| 229889 | 230662 | assert( xOutput==0 || (pnChangeset==0 && ppChangeset==0) ); |
| 229890 | 230663 | assert( xOutput!=0 || (pnChangeset!=0 && ppChangeset!=0) ); |
| 229891 | 230664 | |
| @@ -234315,10 +235088,22 @@ | ||
| 234315 | 235088 | # define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&3)==0) |
| 234316 | 235089 | #else |
| 234317 | 235090 | # define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&7)==0) |
| 234318 | 235091 | #endif |
| 234319 | 235092 | |
| 235093 | +/* | |
| 235094 | +** Macros needed to provide flexible arrays in a portable way | |
| 235095 | +*/ | |
| 235096 | +#ifndef offsetof | |
| 235097 | +# define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD)) | |
| 235098 | +#endif | |
| 235099 | +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) | |
| 235100 | +# define FLEXARRAY | |
| 235101 | +#else | |
| 235102 | +# define FLEXARRAY 1 | |
| 235103 | +#endif | |
| 235104 | + | |
| 234320 | 235105 | #endif |
| 234321 | 235106 | |
| 234322 | 235107 | /* Truncate very long tokens to this many bytes. Hard limit is |
| 234323 | 235108 | ** (65536-1-1-4-9)==65521 bytes. The limiting factor is the 16-bit offset |
| 234324 | 235109 | ** field that occurs at the start of each leaf page (see fts5_index.c). */ |
| @@ -234387,14 +235172,15 @@ | ||
| 234387 | 235172 | ** |
| 234388 | 235173 | ** This object is used by fts5_expr.c and fts5_index.c. |
| 234389 | 235174 | */ |
| 234390 | 235175 | struct Fts5Colset { |
| 234391 | 235176 | int nCol; |
| 234392 | - int aiCol[1]; | |
| 235177 | + int aiCol[FLEXARRAY]; | |
| 234393 | 235178 | }; |
| 234394 | 235179 | |
| 234395 | - | |
| 235180 | +/* Size (int bytes) of a complete Fts5Colset object with N columns. */ | |
| 235181 | +#define SZ_FTS5COLSET(N) (sizeof(i64)*((N+2)/2)) | |
| 234396 | 235182 | |
| 234397 | 235183 | /************************************************************************** |
| 234398 | 235184 | ** Interface to code in fts5_config.c. fts5_config.c contains contains code |
| 234399 | 235185 | ** to parse the arguments passed to the CREATE VIRTUAL TABLE statement. |
| 234400 | 235186 | */ |
| @@ -235219,11 +236005,11 @@ | ||
| 235219 | 236005 | ************************************************************************* |
| 235220 | 236006 | ** Driver template for the LEMON parser generator. |
| 235221 | 236007 | ** |
| 235222 | 236008 | ** The "lemon" program processes an LALR(1) input grammar file, then uses |
| 235223 | 236009 | ** this template to construct a parser. The "lemon" program inserts text |
| 235224 | -** at each "%%" line. Also, any "P-a-r-s-e" identifer prefix (without the | |
| 236010 | +** at each "%%" line. Also, any "P-a-r-s-e" identifier prefix (without the | |
| 235225 | 236011 | ** interstitial "-" characters) contained in this template is changed into |
| 235226 | 236012 | ** the value of the %name directive from the grammar. Otherwise, the content |
| 235227 | 236013 | ** of this template is copied straight through into the generate parser |
| 235228 | 236014 | ** source file. |
| 235229 | 236015 | ** |
| @@ -237373,11 +238159,11 @@ | ||
| 237373 | 238159 | ** where "N" is the total number of documents in the set and nHit |
| 237374 | 238160 | ** is the number that contain at least one instance of the phrase |
| 237375 | 238161 | ** under consideration. |
| 237376 | 238162 | ** |
| 237377 | 238163 | ** The problem with this is that if (N < 2*nHit), the IDF is |
| 237378 | - ** negative. Which is undesirable. So the mimimum allowable IDF is | |
| 238164 | + ** negative. Which is undesirable. So the minimum allowable IDF is | |
| 237379 | 238165 | ** (1e-6) - roughly the same as a term that appears in just over |
| 237380 | 238166 | ** half of set of 5,000,000 documents. */ |
| 237381 | 238167 | double idf = log( (nRow - nHit + 0.5) / (nHit + 0.5) ); |
| 237382 | 238168 | if( idf<=0.0 ) idf = 1e-6; |
| 237383 | 238169 | p->aIDF[i] = idf; |
| @@ -237836,11 +238622,11 @@ | ||
| 237836 | 238622 | ** |
| 237837 | 238623 | ** * All non-ASCII characters, |
| 237838 | 238624 | ** * The 52 upper and lower case ASCII characters, and |
| 237839 | 238625 | ** * The 10 integer ASCII characters. |
| 237840 | 238626 | ** * The underscore character "_" (0x5F). |
| 237841 | -** * The unicode "subsitute" character (0x1A). | |
| 238627 | +** * The unicode "substitute" character (0x1A). | |
| 237842 | 238628 | */ |
| 237843 | 238629 | static int sqlite3Fts5IsBareword(char t){ |
| 237844 | 238630 | u8 aBareword[128] = { |
| 237845 | 238631 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 .. 0x0F */ |
| 237846 | 238632 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* 0x10 .. 0x1F */ |
| @@ -239154,12 +239940,16 @@ | ||
| 239154 | 239940 | Fts5ExprNearset *pNear; /* For FTS5_STRING - cluster of phrases */ |
| 239155 | 239941 | |
| 239156 | 239942 | /* Child nodes. For a NOT node, this array always contains 2 entries. For |
| 239157 | 239943 | ** AND or OR nodes, it contains 2 or more entries. */ |
| 239158 | 239944 | int nChild; /* Number of child nodes */ |
| 239159 | - Fts5ExprNode *apChild[1]; /* Array of child nodes */ | |
| 239945 | + Fts5ExprNode *apChild[FLEXARRAY]; /* Array of child nodes */ | |
| 239160 | 239946 | }; |
| 239947 | + | |
| 239948 | +/* Size (in bytes) of an Fts5ExprNode object that holds up to N children */ | |
| 239949 | +#define SZ_FTS5EXPRNODE(N) \ | |
| 239950 | + (offsetof(Fts5ExprNode,apChild) + (N)*sizeof(Fts5ExprNode*)) | |
| 239161 | 239951 | |
| 239162 | 239952 | #define Fts5NodeIsString(p) ((p)->eType==FTS5_TERM || (p)->eType==FTS5_STRING) |
| 239163 | 239953 | |
| 239164 | 239954 | /* |
| 239165 | 239955 | ** Invoke the xNext method of an Fts5ExprNode object. This macro should be |
| @@ -239187,24 +239977,31 @@ | ||
| 239187 | 239977 | */ |
| 239188 | 239978 | struct Fts5ExprPhrase { |
| 239189 | 239979 | Fts5ExprNode *pNode; /* FTS5_STRING node this phrase is part of */ |
| 239190 | 239980 | Fts5Buffer poslist; /* Current position list */ |
| 239191 | 239981 | int nTerm; /* Number of entries in aTerm[] */ |
| 239192 | - Fts5ExprTerm aTerm[1]; /* Terms that make up this phrase */ | |
| 239982 | + Fts5ExprTerm aTerm[FLEXARRAY]; /* Terms that make up this phrase */ | |
| 239193 | 239983 | }; |
| 239984 | + | |
| 239985 | +/* Size (in bytes) of an Fts5ExprPhrase object that holds up to N terms */ | |
| 239986 | +#define SZ_FTS5EXPRPHRASE(N) \ | |
| 239987 | + (offsetof(Fts5ExprPhrase,aTerm) + (N)*sizeof(Fts5ExprTerm)) | |
| 239194 | 239988 | |
| 239195 | 239989 | /* |
| 239196 | 239990 | ** One or more phrases that must appear within a certain token distance of |
| 239197 | 239991 | ** each other within each matching document. |
| 239198 | 239992 | */ |
| 239199 | 239993 | struct Fts5ExprNearset { |
| 239200 | 239994 | int nNear; /* NEAR parameter */ |
| 239201 | 239995 | Fts5Colset *pColset; /* Columns to search (NULL -> all columns) */ |
| 239202 | 239996 | int nPhrase; /* Number of entries in aPhrase[] array */ |
| 239203 | - Fts5ExprPhrase *apPhrase[1]; /* Array of phrase pointers */ | |
| 239997 | + Fts5ExprPhrase *apPhrase[FLEXARRAY]; /* Array of phrase pointers */ | |
| 239204 | 239998 | }; |
| 239205 | 239999 | |
| 240000 | +/* Size (in bytes) of an Fts5ExprNearset object covering up to N phrases */ | |
| 240001 | +#define SZ_FTS5EXPRNEARSET(N) \ | |
| 240002 | + (offsetof(Fts5ExprNearset,apPhrase)+(N)*sizeof(Fts5ExprPhrase*)) | |
| 239206 | 240003 | |
| 239207 | 240004 | /* |
| 239208 | 240005 | ** Parse context. |
| 239209 | 240006 | */ |
| 239210 | 240007 | struct Fts5Parse { |
| @@ -239360,11 +240157,11 @@ | ||
| 239360 | 240157 | assert_expr_depth_ok(sParse.rc, sParse.pExpr); |
| 239361 | 240158 | |
| 239362 | 240159 | /* If the LHS of the MATCH expression was a user column, apply the |
| 239363 | 240160 | ** implicit column-filter. */ |
| 239364 | 240161 | if( sParse.rc==SQLITE_OK && iCol<pConfig->nCol ){ |
| 239365 | - int n = sizeof(Fts5Colset); | |
| 240162 | + int n = SZ_FTS5COLSET(1); | |
| 239366 | 240163 | Fts5Colset *pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&sParse.rc, n); |
| 239367 | 240164 | if( pColset ){ |
| 239368 | 240165 | pColset->nCol = 1; |
| 239369 | 240166 | pColset->aiCol[0] = iCol; |
| 239370 | 240167 | sqlite3Fts5ParseSetColset(&sParse, sParse.pExpr, pColset); |
| @@ -240718,11 +241515,11 @@ | ||
| 240718 | 241515 | Fts5ExprNearset *pRet = 0; |
| 240719 | 241516 | |
| 240720 | 241517 | if( pParse->rc==SQLITE_OK ){ |
| 240721 | 241518 | if( pNear==0 ){ |
| 240722 | 241519 | sqlite3_int64 nByte; |
| 240723 | - nByte = sizeof(Fts5ExprNearset) + SZALLOC * sizeof(Fts5ExprPhrase*); | |
| 241520 | + nByte = SZ_FTS5EXPRNEARSET(SZALLOC+1); | |
| 240724 | 241521 | pRet = sqlite3_malloc64(nByte); |
| 240725 | 241522 | if( pRet==0 ){ |
| 240726 | 241523 | pParse->rc = SQLITE_NOMEM; |
| 240727 | 241524 | }else{ |
| 240728 | 241525 | memset(pRet, 0, (size_t)nByte); |
| @@ -240729,11 +241526,11 @@ | ||
| 240729 | 241526 | } |
| 240730 | 241527 | }else if( (pNear->nPhrase % SZALLOC)==0 ){ |
| 240731 | 241528 | int nNew = pNear->nPhrase + SZALLOC; |
| 240732 | 241529 | sqlite3_int64 nByte; |
| 240733 | 241530 | |
| 240734 | - nByte = sizeof(Fts5ExprNearset) + nNew * sizeof(Fts5ExprPhrase*); | |
| 241531 | + nByte = SZ_FTS5EXPRNEARSET(nNew+1); | |
| 240735 | 241532 | pRet = (Fts5ExprNearset*)sqlite3_realloc64(pNear, nByte); |
| 240736 | 241533 | if( pRet==0 ){ |
| 240737 | 241534 | pParse->rc = SQLITE_NOMEM; |
| 240738 | 241535 | } |
| 240739 | 241536 | }else{ |
| @@ -240820,16 +241617,16 @@ | ||
| 240820 | 241617 | if( pPhrase==0 || (pPhrase->nTerm % SZALLOC)==0 ){ |
| 240821 | 241618 | Fts5ExprPhrase *pNew; |
| 240822 | 241619 | int nNew = SZALLOC + (pPhrase ? pPhrase->nTerm : 0); |
| 240823 | 241620 | |
| 240824 | 241621 | pNew = (Fts5ExprPhrase*)sqlite3_realloc64(pPhrase, |
| 240825 | - sizeof(Fts5ExprPhrase) + sizeof(Fts5ExprTerm) * nNew | |
| 241622 | + SZ_FTS5EXPRPHRASE(nNew+1) | |
| 240826 | 241623 | ); |
| 240827 | 241624 | if( pNew==0 ){ |
| 240828 | 241625 | rc = SQLITE_NOMEM; |
| 240829 | 241626 | }else{ |
| 240830 | - if( pPhrase==0 ) memset(pNew, 0, sizeof(Fts5ExprPhrase)); | |
| 241627 | + if( pPhrase==0 ) memset(pNew, 0, SZ_FTS5EXPRPHRASE(1)); | |
| 240831 | 241628 | pCtx->pPhrase = pPhrase = pNew; |
| 240832 | 241629 | pNew->nTerm = nNew - SZALLOC; |
| 240833 | 241630 | } |
| 240834 | 241631 | } |
| 240835 | 241632 | |
| @@ -240933,11 +241730,11 @@ | ||
| 240933 | 241730 | } |
| 240934 | 241731 | |
| 240935 | 241732 | if( sCtx.pPhrase==0 ){ |
| 240936 | 241733 | /* This happens when parsing a token or quoted phrase that contains |
| 240937 | 241734 | ** no token characters at all. (e.g ... MATCH '""'). */ |
| 240938 | - sCtx.pPhrase = sqlite3Fts5MallocZero(&pParse->rc, sizeof(Fts5ExprPhrase)); | |
| 241735 | + sCtx.pPhrase = sqlite3Fts5MallocZero(&pParse->rc, SZ_FTS5EXPRPHRASE(1)); | |
| 240939 | 241736 | }else if( sCtx.pPhrase->nTerm ){ |
| 240940 | 241737 | sCtx.pPhrase->aTerm[sCtx.pPhrase->nTerm-1].bPrefix = (u8)bPrefix; |
| 240941 | 241738 | } |
| 240942 | 241739 | assert( pParse->apPhrase!=0 ); |
| 240943 | 241740 | pParse->apPhrase[pParse->nPhrase-1] = sCtx.pPhrase; |
| @@ -240968,23 +241765,22 @@ | ||
| 240968 | 241765 | if( rc==SQLITE_OK ){ |
| 240969 | 241766 | pNew->apExprPhrase = (Fts5ExprPhrase**)sqlite3Fts5MallocZero(&rc, |
| 240970 | 241767 | sizeof(Fts5ExprPhrase*)); |
| 240971 | 241768 | } |
| 240972 | 241769 | if( rc==SQLITE_OK ){ |
| 240973 | - pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&rc, | |
| 240974 | - sizeof(Fts5ExprNode)); | |
| 241770 | + pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&rc, SZ_FTS5EXPRNODE(1)); | |
| 240975 | 241771 | } |
| 240976 | 241772 | if( rc==SQLITE_OK ){ |
| 240977 | 241773 | pNew->pRoot->pNear = (Fts5ExprNearset*)sqlite3Fts5MallocZero(&rc, |
| 240978 | - sizeof(Fts5ExprNearset) + sizeof(Fts5ExprPhrase*)); | |
| 241774 | + SZ_FTS5EXPRNEARSET(2)); | |
| 240979 | 241775 | } |
| 240980 | 241776 | if( rc==SQLITE_OK && ALWAYS(pOrig!=0) ){ |
| 240981 | 241777 | Fts5Colset *pColsetOrig = pOrig->pNode->pNear->pColset; |
| 240982 | 241778 | if( pColsetOrig ){ |
| 240983 | 241779 | sqlite3_int64 nByte; |
| 240984 | 241780 | Fts5Colset *pColset; |
| 240985 | - nByte = sizeof(Fts5Colset) + (pColsetOrig->nCol-1) * sizeof(int); | |
| 241781 | + nByte = SZ_FTS5COLSET(pColsetOrig->nCol); | |
| 240986 | 241782 | pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&rc, nByte); |
| 240987 | 241783 | if( pColset ){ |
| 240988 | 241784 | memcpy(pColset, pColsetOrig, (size_t)nByte); |
| 240989 | 241785 | } |
| 240990 | 241786 | pNew->pRoot->pNear->pColset = pColset; |
| @@ -241008,11 +241804,11 @@ | ||
| 241008 | 241804 | } |
| 241009 | 241805 | } |
| 241010 | 241806 | }else{ |
| 241011 | 241807 | /* This happens when parsing a token or quoted phrase that contains |
| 241012 | 241808 | ** no token characters at all. (e.g ... MATCH '""'). */ |
| 241013 | - sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprPhrase)); | |
| 241809 | + sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, SZ_FTS5EXPRPHRASE(1)); | |
| 241014 | 241810 | } |
| 241015 | 241811 | } |
| 241016 | 241812 | |
| 241017 | 241813 | if( rc==SQLITE_OK && ALWAYS(sCtx.pPhrase) ){ |
| 241018 | 241814 | /* All the allocations succeeded. Put the expression object together. */ |
| @@ -241102,11 +241898,11 @@ | ||
| 241102 | 241898 | Fts5Colset *pNew; /* New colset object to return */ |
| 241103 | 241899 | |
| 241104 | 241900 | assert( pParse->rc==SQLITE_OK ); |
| 241105 | 241901 | assert( iCol>=0 && iCol<pParse->pConfig->nCol ); |
| 241106 | 241902 | |
| 241107 | - pNew = sqlite3_realloc64(p, sizeof(Fts5Colset) + sizeof(int)*nCol); | |
| 241903 | + pNew = sqlite3_realloc64(p, SZ_FTS5COLSET(nCol+1)); | |
| 241108 | 241904 | if( pNew==0 ){ |
| 241109 | 241905 | pParse->rc = SQLITE_NOMEM; |
| 241110 | 241906 | }else{ |
| 241111 | 241907 | int *aiCol = pNew->aiCol; |
| 241112 | 241908 | int i, j; |
| @@ -241137,11 +241933,11 @@ | ||
| 241137 | 241933 | static Fts5Colset *sqlite3Fts5ParseColsetInvert(Fts5Parse *pParse, Fts5Colset *p){ |
| 241138 | 241934 | Fts5Colset *pRet; |
| 241139 | 241935 | int nCol = pParse->pConfig->nCol; |
| 241140 | 241936 | |
| 241141 | 241937 | pRet = (Fts5Colset*)sqlite3Fts5MallocZero(&pParse->rc, |
| 241142 | - sizeof(Fts5Colset) + sizeof(int)*nCol | |
| 241938 | + SZ_FTS5COLSET(nCol+1) | |
| 241143 | 241939 | ); |
| 241144 | 241940 | if( pRet ){ |
| 241145 | 241941 | int i; |
| 241146 | 241942 | int iOld = 0; |
| 241147 | 241943 | for(i=0; i<nCol; i++){ |
| @@ -241198,11 +241994,11 @@ | ||
| 241198 | 241994 | ** fails, (*pRc) is set to SQLITE_NOMEM and NULL is returned. |
| 241199 | 241995 | */ |
| 241200 | 241996 | static Fts5Colset *fts5CloneColset(int *pRc, Fts5Colset *pOrig){ |
| 241201 | 241997 | Fts5Colset *pRet; |
| 241202 | 241998 | if( pOrig ){ |
| 241203 | - sqlite3_int64 nByte = sizeof(Fts5Colset) + (pOrig->nCol-1) * sizeof(int); | |
| 241999 | + sqlite3_int64 nByte = SZ_FTS5COLSET(pOrig->nCol); | |
| 241204 | 242000 | pRet = (Fts5Colset*)sqlite3Fts5MallocZero(pRc, nByte); |
| 241205 | 242001 | if( pRet ){ |
| 241206 | 242002 | memcpy(pRet, pOrig, (size_t)nByte); |
| 241207 | 242003 | } |
| 241208 | 242004 | }else{ |
| @@ -241366,21 +242162,21 @@ | ||
| 241366 | 242162 | Fts5ExprNode *pRet; |
| 241367 | 242163 | |
| 241368 | 242164 | assert( pNear->nPhrase==1 ); |
| 241369 | 242165 | assert( pParse->bPhraseToAnd ); |
| 241370 | 242166 | |
| 241371 | - nByte = sizeof(Fts5ExprNode) + nTerm*sizeof(Fts5ExprNode*); | |
| 242167 | + nByte = SZ_FTS5EXPRNODE(nTerm+1); | |
| 241372 | 242168 | pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte); |
| 241373 | 242169 | if( pRet ){ |
| 241374 | 242170 | pRet->eType = FTS5_AND; |
| 241375 | 242171 | pRet->nChild = nTerm; |
| 241376 | 242172 | pRet->iHeight = 1; |
| 241377 | 242173 | fts5ExprAssignXNext(pRet); |
| 241378 | 242174 | pParse->nPhrase--; |
| 241379 | 242175 | for(ii=0; ii<nTerm; ii++){ |
| 241380 | 242176 | Fts5ExprPhrase *pPhrase = (Fts5ExprPhrase*)sqlite3Fts5MallocZero( |
| 241381 | - &pParse->rc, sizeof(Fts5ExprPhrase) | |
| 242177 | + &pParse->rc, SZ_FTS5EXPRPHRASE(1) | |
| 241382 | 242178 | ); |
| 241383 | 242179 | if( pPhrase ){ |
| 241384 | 242180 | if( parseGrowPhraseArray(pParse) ){ |
| 241385 | 242181 | fts5ExprPhraseFree(pPhrase); |
| 241386 | 242182 | }else{ |
| @@ -241445,11 +242241,11 @@ | ||
| 241445 | 242241 | nChild = 2; |
| 241446 | 242242 | if( pLeft->eType==eType ) nChild += pLeft->nChild-1; |
| 241447 | 242243 | if( pRight->eType==eType ) nChild += pRight->nChild-1; |
| 241448 | 242244 | } |
| 241449 | 242245 | |
| 241450 | - nByte = sizeof(Fts5ExprNode) + sizeof(Fts5ExprNode*)*(nChild-1); | |
| 242246 | + nByte = SZ_FTS5EXPRNODE(nChild); | |
| 241451 | 242247 | pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte); |
| 241452 | 242248 | |
| 241453 | 242249 | if( pRet ){ |
| 241454 | 242250 | pRet->eType = eType; |
| 241455 | 242251 | pRet->pNear = pNear; |
| @@ -242320,11 +243116,11 @@ | ||
| 242320 | 243116 | } |
| 242321 | 243117 | return rc; |
| 242322 | 243118 | } |
| 242323 | 243119 | |
| 242324 | 243120 | /* |
| 242325 | -** Clear the token mappings for all Fts5IndexIter objects mannaged by | |
| 243121 | +** Clear the token mappings for all Fts5IndexIter objects managed by | |
| 242326 | 243122 | ** the expression passed as the only argument. |
| 242327 | 243123 | */ |
| 242328 | 243124 | static void sqlite3Fts5ExprClearTokens(Fts5Expr *pExpr){ |
| 242329 | 243125 | int ii; |
| 242330 | 243126 | for(ii=0; ii<pExpr->nPhrase; ii++){ |
| @@ -242355,11 +243151,11 @@ | ||
| 242355 | 243151 | |
| 242356 | 243152 | typedef struct Fts5HashEntry Fts5HashEntry; |
| 242357 | 243153 | |
| 242358 | 243154 | /* |
| 242359 | 243155 | ** This file contains the implementation of an in-memory hash table used |
| 242360 | -** to accumuluate "term -> doclist" content before it is flused to a level-0 | |
| 243156 | +** to accumulate "term -> doclist" content before it is flushed to a level-0 | |
| 242361 | 243157 | ** segment. |
| 242362 | 243158 | */ |
| 242363 | 243159 | |
| 242364 | 243160 | |
| 242365 | 243161 | struct Fts5Hash { |
| @@ -242412,11 +243208,11 @@ | ||
| 242412 | 243208 | int iPos; /* Position of last value written */ |
| 242413 | 243209 | i64 iRowid; /* Rowid of last value written */ |
| 242414 | 243210 | }; |
| 242415 | 243211 | |
| 242416 | 243212 | /* |
| 242417 | -** Eqivalent to: | |
| 243213 | +** Equivalent to: | |
| 242418 | 243214 | ** |
| 242419 | 243215 | ** char *fts5EntryKey(Fts5HashEntry *pEntry){ return zKey; } |
| 242420 | 243216 | */ |
| 242421 | 243217 | #define fts5EntryKey(p) ( ((char *)(&(p)[1])) ) |
| 242422 | 243218 | |
| @@ -243348,13 +244144,17 @@ | ||
| 243348 | 244144 | int nRef; /* Object reference count */ |
| 243349 | 244145 | u64 nWriteCounter; /* Total leaves written to level 0 */ |
| 243350 | 244146 | u64 nOriginCntr; /* Origin value for next top-level segment */ |
| 243351 | 244147 | int nSegment; /* Total segments in this structure */ |
| 243352 | 244148 | int nLevel; /* Number of levels in this index */ |
| 243353 | - Fts5StructureLevel aLevel[1]; /* Array of nLevel level objects */ | |
| 244149 | + Fts5StructureLevel aLevel[FLEXARRAY]; /* Array of nLevel level objects */ | |
| 243354 | 244150 | }; |
| 243355 | 244151 | |
| 244152 | +/* Size (in bytes) of an Fts5Structure object holding up to N levels */ | |
| 244153 | +#define SZ_FTS5STRUCTURE(N) \ | |
| 244154 | + (offsetof(Fts5Structure,aLevel) + (N)*sizeof(Fts5StructureLevel)) | |
| 244155 | + | |
| 243356 | 244156 | /* |
| 243357 | 244157 | ** An object of type Fts5SegWriter is used to write to segments. |
| 243358 | 244158 | */ |
| 243359 | 244159 | struct Fts5PageWriter { |
| 243360 | 244160 | int pgno; /* Page number for this page */ |
| @@ -243480,14 +244280,18 @@ | ||
| 243480 | 244280 | |
| 243481 | 244281 | /* |
| 243482 | 244282 | ** Array of tombstone pages. Reference counted. |
| 243483 | 244283 | */ |
| 243484 | 244284 | struct Fts5TombstoneArray { |
| 243485 | - int nRef; /* Number of pointers to this object */ | |
| 244285 | + int nRef; /* Number of pointers to this object */ | |
| 243486 | 244286 | int nTombstone; |
| 243487 | - Fts5Data *apTombstone[1]; /* Array of tombstone pages */ | |
| 244287 | + Fts5Data *apTombstone[FLEXARRAY]; /* Array of tombstone pages */ | |
| 243488 | 244288 | }; |
| 244289 | + | |
| 244290 | +/* Size (in bytes) of an Fts5TombstoneArray holding up to N tombstones */ | |
| 244291 | +#define SZ_FTS5TOMBSTONEARRAY(N) \ | |
| 244292 | + (offsetof(Fts5TombstoneArray,apTombstone)+(N)*sizeof(Fts5Data*)) | |
| 243489 | 244293 | |
| 243490 | 244294 | /* |
| 243491 | 244295 | ** Argument is a pointer to an Fts5Data structure that contains a |
| 243492 | 244296 | ** leaf page. |
| 243493 | 244297 | */ |
| @@ -243553,12 +244357,15 @@ | ||
| 243553 | 244357 | int bRev; /* True to iterate in reverse order */ |
| 243554 | 244358 | u8 bSkipEmpty; /* True to skip deleted entries */ |
| 243555 | 244359 | |
| 243556 | 244360 | i64 iSwitchRowid; /* Firstest rowid of other than aFirst[1] */ |
| 243557 | 244361 | Fts5CResult *aFirst; /* Current merge state (see above) */ |
| 243558 | - Fts5SegIter aSeg[1]; /* Array of segment iterators */ | |
| 244362 | + Fts5SegIter aSeg[FLEXARRAY]; /* Array of segment iterators */ | |
| 243559 | 244363 | }; |
| 244364 | + | |
| 244365 | +/* Size (in bytes) of an Fts5Iter object holding up to N segment iterators */ | |
| 244366 | +#define SZ_FTS5ITER(N) (offsetof(Fts5Iter,aSeg)+(N)*sizeof(Fts5SegIter)) | |
| 243560 | 244367 | |
| 243561 | 244368 | /* |
| 243562 | 244369 | ** An instance of the following type is used to iterate through the contents |
| 243563 | 244370 | ** of a doclist-index record. |
| 243564 | 244371 | ** |
| @@ -243582,12 +244389,16 @@ | ||
| 243582 | 244389 | i64 iRowid; /* First rowid on leaf iLeafPgno */ |
| 243583 | 244390 | }; |
| 243584 | 244391 | struct Fts5DlidxIter { |
| 243585 | 244392 | int nLvl; |
| 243586 | 244393 | int iSegid; |
| 243587 | - Fts5DlidxLvl aLvl[1]; | |
| 244394 | + Fts5DlidxLvl aLvl[FLEXARRAY]; | |
| 243588 | 244395 | }; |
| 244396 | + | |
| 244397 | +/* Size (in bytes) of an Fts5DlidxIter object with up to N levels */ | |
| 244398 | +#define SZ_FTS5DLIDXITER(N) \ | |
| 244399 | + (offsetof(Fts5DlidxIter,aLvl)+(N)*sizeof(Fts5DlidxLvl)) | |
| 243589 | 244400 | |
| 243590 | 244401 | static void fts5PutU16(u8 *aOut, u16 iVal){ |
| 243591 | 244402 | aOut[0] = (iVal>>8); |
| 243592 | 244403 | aOut[1] = (iVal&0xFF); |
| 243593 | 244404 | } |
| @@ -243952,11 +244763,11 @@ | ||
| 243952 | 244763 | ** an error occurs, (*pRc) is set to an SQLite error code before returning. |
| 243953 | 244764 | */ |
| 243954 | 244765 | static void fts5StructureMakeWritable(int *pRc, Fts5Structure **pp){ |
| 243955 | 244766 | Fts5Structure *p = *pp; |
| 243956 | 244767 | if( *pRc==SQLITE_OK && p->nRef>1 ){ |
| 243957 | - i64 nByte = sizeof(Fts5Structure)+(p->nLevel-1)*sizeof(Fts5StructureLevel); | |
| 244768 | + i64 nByte = SZ_FTS5STRUCTURE(p->nLevel); | |
| 243958 | 244769 | Fts5Structure *pNew; |
| 243959 | 244770 | pNew = (Fts5Structure*)sqlite3Fts5MallocZero(pRc, nByte); |
| 243960 | 244771 | if( pNew ){ |
| 243961 | 244772 | int i; |
| 243962 | 244773 | memcpy(pNew, p, nByte); |
| @@ -244026,14 +244837,11 @@ | ||
| 244026 | 244837 | if( nLevel>FTS5_MAX_SEGMENT || nLevel<0 |
| 244027 | 244838 | || nSegment>FTS5_MAX_SEGMENT || nSegment<0 |
| 244028 | 244839 | ){ |
| 244029 | 244840 | return FTS5_CORRUPT; |
| 244030 | 244841 | } |
| 244031 | - nByte = ( | |
| 244032 | - sizeof(Fts5Structure) + /* Main structure */ | |
| 244033 | - sizeof(Fts5StructureLevel) * (nLevel-1) /* aLevel[] array */ | |
| 244034 | - ); | |
| 244842 | + nByte = SZ_FTS5STRUCTURE(nLevel); | |
| 244035 | 244843 | pRet = (Fts5Structure*)sqlite3Fts5MallocZero(&rc, nByte); |
| 244036 | 244844 | |
| 244037 | 244845 | if( pRet ){ |
| 244038 | 244846 | pRet->nRef = 1; |
| 244039 | 244847 | pRet->nLevel = nLevel; |
| @@ -244109,14 +244917,11 @@ | ||
| 244109 | 244917 | fts5StructureMakeWritable(pRc, ppStruct); |
| 244110 | 244918 | assert( (ppStruct!=0 && (*ppStruct)!=0) || (*pRc)!=SQLITE_OK ); |
| 244111 | 244919 | if( *pRc==SQLITE_OK ){ |
| 244112 | 244920 | Fts5Structure *pStruct = *ppStruct; |
| 244113 | 244921 | int nLevel = pStruct->nLevel; |
| 244114 | - sqlite3_int64 nByte = ( | |
| 244115 | - sizeof(Fts5Structure) + /* Main structure */ | |
| 244116 | - sizeof(Fts5StructureLevel) * (nLevel+1) /* aLevel[] array */ | |
| 244117 | - ); | |
| 244922 | + sqlite3_int64 nByte = SZ_FTS5STRUCTURE(nLevel+2); | |
| 244118 | 244923 | |
| 244119 | 244924 | pStruct = sqlite3_realloc64(pStruct, nByte); |
| 244120 | 244925 | if( pStruct ){ |
| 244121 | 244926 | memset(&pStruct->aLevel[nLevel], 0, sizeof(Fts5StructureLevel)); |
| 244122 | 244927 | pStruct->nLevel++; |
| @@ -244651,11 +245456,11 @@ | ||
| 244651 | 245456 | Fts5DlidxIter *pIter = 0; |
| 244652 | 245457 | int i; |
| 244653 | 245458 | int bDone = 0; |
| 244654 | 245459 | |
| 244655 | 245460 | for(i=0; p->rc==SQLITE_OK && bDone==0; i++){ |
| 244656 | - sqlite3_int64 nByte = sizeof(Fts5DlidxIter) + i * sizeof(Fts5DlidxLvl); | |
| 245461 | + sqlite3_int64 nByte = SZ_FTS5DLIDXITER(i+1); | |
| 244657 | 245462 | Fts5DlidxIter *pNew; |
| 244658 | 245463 | |
| 244659 | 245464 | pNew = (Fts5DlidxIter*)sqlite3_realloc64(pIter, nByte); |
| 244660 | 245465 | if( pNew==0 ){ |
| 244661 | 245466 | p->rc = SQLITE_NOMEM; |
| @@ -244869,11 +245674,11 @@ | ||
| 244869 | 245674 | ** leave an error in the Fts5Index object. |
| 244870 | 245675 | */ |
| 244871 | 245676 | static void fts5SegIterAllocTombstone(Fts5Index *p, Fts5SegIter *pIter){ |
| 244872 | 245677 | const int nTomb = pIter->pSeg->nPgTombstone; |
| 244873 | 245678 | if( nTomb>0 ){ |
| 244874 | - int nByte = nTomb * sizeof(Fts5Data*) + sizeof(Fts5TombstoneArray); | |
| 245679 | + int nByte = SZ_FTS5TOMBSTONEARRAY(nTomb+1); | |
| 244875 | 245680 | Fts5TombstoneArray *pNew; |
| 244876 | 245681 | pNew = (Fts5TombstoneArray*)sqlite3Fts5MallocZero(&p->rc, nByte); |
| 244877 | 245682 | if( pNew ){ |
| 244878 | 245683 | pNew->nTombstone = nTomb; |
| 244879 | 245684 | pNew->nRef = 1; |
| @@ -246330,12 +247135,11 @@ | ||
| 246330 | 247135 | Fts5Iter *pNew; |
| 246331 | 247136 | i64 nSlot; /* Power of two >= nSeg */ |
| 246332 | 247137 | |
| 246333 | 247138 | for(nSlot=2; nSlot<nSeg; nSlot=nSlot*2); |
| 246334 | 247139 | pNew = fts5IdxMalloc(p, |
| 246335 | - sizeof(Fts5Iter) + /* pNew */ | |
| 246336 | - sizeof(Fts5SegIter) * (nSlot-1) + /* pNew->aSeg[] */ | |
| 247140 | + SZ_FTS5ITER(nSlot) + /* pNew + pNew->aSeg[] */ | |
| 246337 | 247141 | sizeof(Fts5CResult) * nSlot /* pNew->aFirst[] */ |
| 246338 | 247142 | ); |
| 246339 | 247143 | if( pNew ){ |
| 246340 | 247144 | pNew->nSeg = nSlot; |
| 246341 | 247145 | pNew->aFirst = (Fts5CResult*)&pNew->aSeg[nSlot]; |
| @@ -248697,11 +249501,11 @@ | ||
| 248697 | 249501 | static Fts5Structure *fts5IndexOptimizeStruct( |
| 248698 | 249502 | Fts5Index *p, |
| 248699 | 249503 | Fts5Structure *pStruct |
| 248700 | 249504 | ){ |
| 248701 | 249505 | Fts5Structure *pNew = 0; |
| 248702 | - sqlite3_int64 nByte = sizeof(Fts5Structure); | |
| 249506 | + sqlite3_int64 nByte = SZ_FTS5STRUCTURE(1); | |
| 248703 | 249507 | int nSeg = pStruct->nSegment; |
| 248704 | 249508 | int i; |
| 248705 | 249509 | |
| 248706 | 249510 | /* Figure out if this structure requires optimization. A structure does |
| 248707 | 249511 | ** not require optimization if either: |
| @@ -248727,10 +249531,11 @@ | ||
| 248727 | 249531 | } |
| 248728 | 249532 | assert( pStruct->aLevel[i].nMerge<=nThis ); |
| 248729 | 249533 | } |
| 248730 | 249534 | |
| 248731 | 249535 | nByte += (((i64)pStruct->nLevel)+1) * sizeof(Fts5StructureLevel); |
| 249536 | + assert( nByte==SZ_FTS5STRUCTURE(pStruct->nLevel+2) ); | |
| 248732 | 249537 | pNew = (Fts5Structure*)sqlite3Fts5MallocZero(&p->rc, nByte); |
| 248733 | 249538 | |
| 248734 | 249539 | if( pNew ){ |
| 248735 | 249540 | Fts5StructureLevel *pLvl; |
| 248736 | 249541 | nByte = nSeg * sizeof(Fts5StructureSegment); |
| @@ -249303,12 +250108,16 @@ | ||
| 249303 | 250108 | /* The following are used for other full-token tokendata queries only. */ |
| 249304 | 250109 | int nIter; |
| 249305 | 250110 | int nIterAlloc; |
| 249306 | 250111 | Fts5PoslistReader *aPoslistReader; |
| 249307 | 250112 | int *aPoslistToIter; |
| 249308 | - Fts5Iter *apIter[1]; | |
| 250113 | + Fts5Iter *apIter[FLEXARRAY]; | |
| 249309 | 250114 | }; |
| 250115 | + | |
| 250116 | +/* Size in bytes of an Fts5TokenDataIter object holding up to N iterators */ | |
| 250117 | +#define SZ_FTS5TOKENDATAITER(N) \ | |
| 250118 | + (offsetof(Fts5TokenDataIter,apIter) + (N)*sizeof(Fts5Iter)) | |
| 249310 | 250119 | |
| 249311 | 250120 | /* |
| 249312 | 250121 | ** The two input arrays - a1[] and a2[] - are in sorted order. This function |
| 249313 | 250122 | ** merges the two arrays together and writes the result to output array |
| 249314 | 250123 | ** aOut[]. aOut[] is guaranteed to be large enough to hold the result. |
| @@ -249377,11 +250186,11 @@ | ||
| 249377 | 250186 | } |
| 249378 | 250187 | |
| 249379 | 250188 | /* |
| 249380 | 250189 | ** Sort the contents of the pT->aMap[] array. |
| 249381 | 250190 | ** |
| 249382 | -** The sorting algorithm requries a malloc(). If this fails, an error code | |
| 250191 | +** The sorting algorithm requires a malloc(). If this fails, an error code | |
| 249383 | 250192 | ** is left in Fts5Index.rc before returning. |
| 249384 | 250193 | */ |
| 249385 | 250194 | static void fts5TokendataIterSortMap(Fts5Index *p, Fts5TokenDataIter *pT){ |
| 249386 | 250195 | Fts5TokenDataMap *aTmp = 0; |
| 249387 | 250196 | int nByte = pT->nMap * sizeof(Fts5TokenDataMap); |
| @@ -249568,11 +250377,11 @@ | ||
| 249568 | 250377 | if( iIdx==0 |
| 249569 | 250378 | && p->pConfig->eDetail==FTS5_DETAIL_FULL |
| 249570 | 250379 | && p->pConfig->bPrefixInsttoken |
| 249571 | 250380 | ){ |
| 249572 | 250381 | s.pTokendata = &s2; |
| 249573 | - s2.pT = (Fts5TokenDataIter*)fts5IdxMalloc(p, sizeof(*s2.pT)); | |
| 250382 | + s2.pT = (Fts5TokenDataIter*)fts5IdxMalloc(p, SZ_FTS5TOKENDATAITER(1)); | |
| 249574 | 250383 | } |
| 249575 | 250384 | |
| 249576 | 250385 | if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){ |
| 249577 | 250386 | s.xMerge = fts5MergeRowidLists; |
| 249578 | 250387 | s.xAppend = fts5AppendRowid; |
| @@ -249696,19 +250505,21 @@ | ||
| 249696 | 250505 | ** The %_data table is completely empty when this function is called. This |
| 249697 | 250506 | ** function populates it with the initial structure objects for each index, |
| 249698 | 250507 | ** and the initial version of the "averages" record (a zero-byte blob). |
| 249699 | 250508 | */ |
| 249700 | 250509 | static int sqlite3Fts5IndexReinit(Fts5Index *p){ |
| 249701 | - Fts5Structure s; | |
| 250510 | + Fts5Structure *pTmp; | |
| 250511 | + u8 tmpSpace[SZ_FTS5STRUCTURE(1)]; | |
| 249702 | 250512 | fts5StructureInvalidate(p); |
| 249703 | 250513 | fts5IndexDiscardData(p); |
| 249704 | - memset(&s, 0, sizeof(Fts5Structure)); | |
| 250514 | + pTmp = (Fts5Structure*)tmpSpace; | |
| 250515 | + memset(pTmp, 0, SZ_FTS5STRUCTURE(1)); | |
| 249705 | 250516 | if( p->pConfig->bContentlessDelete ){ |
| 249706 | - s.nOriginCntr = 1; | |
| 250517 | + pTmp->nOriginCntr = 1; | |
| 249707 | 250518 | } |
| 249708 | 250519 | fts5DataWrite(p, FTS5_AVERAGES_ROWID, (const u8*)"", 0); |
| 249709 | - fts5StructureWrite(p, &s); | |
| 250520 | + fts5StructureWrite(p, pTmp); | |
| 249710 | 250521 | return fts5IndexReturn(p); |
| 249711 | 250522 | } |
| 249712 | 250523 | |
| 249713 | 250524 | /* |
| 249714 | 250525 | ** Open a new Fts5Index handle. If the bCreate argument is true, create |
| @@ -249912,11 +250723,11 @@ | ||
| 249912 | 250723 | Fts5TokenDataIter *pRet = pIn; |
| 249913 | 250724 | |
| 249914 | 250725 | if( p->rc==SQLITE_OK ){ |
| 249915 | 250726 | if( pIn==0 || pIn->nIter==pIn->nIterAlloc ){ |
| 249916 | 250727 | int nAlloc = pIn ? pIn->nIterAlloc*2 : 16; |
| 249917 | - int nByte = nAlloc * sizeof(Fts5Iter*) + sizeof(Fts5TokenDataIter); | |
| 250728 | + int nByte = SZ_FTS5TOKENDATAITER(nAlloc+1); | |
| 249918 | 250729 | Fts5TokenDataIter *pNew = (Fts5TokenDataIter*)sqlite3_realloc(pIn, nByte); |
| 249919 | 250730 | |
| 249920 | 250731 | if( pNew==0 ){ |
| 249921 | 250732 | p->rc = SQLITE_NOMEM; |
| 249922 | 250733 | }else{ |
| @@ -250428,11 +251239,12 @@ | ||
| 250428 | 251239 | |
| 250429 | 251240 | memset(&ctx, 0, sizeof(ctx)); |
| 250430 | 251241 | |
| 250431 | 251242 | fts5BufferGrow(&p->rc, &token, nToken+1); |
| 250432 | 251243 | assert( token.p!=0 || p->rc!=SQLITE_OK ); |
| 250433 | - ctx.pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, sizeof(*ctx.pT)); | |
| 251244 | + ctx.pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, | |
| 251245 | + SZ_FTS5TOKENDATAITER(1)); | |
| 250434 | 251246 | |
| 250435 | 251247 | if( p->rc==SQLITE_OK ){ |
| 250436 | 251248 | |
| 250437 | 251249 | /* Fill in the token prefix to search for */ |
| 250438 | 251250 | token.p[0] = FTS5_MAIN_PREFIX; |
| @@ -250559,11 +251371,12 @@ | ||
| 250559 | 251371 | assert( p->pConfig->eDetail!=FTS5_DETAIL_FULL ); |
| 250560 | 251372 | assert( pIter->pTokenDataIter || pIter->nSeg>0 ); |
| 250561 | 251373 | if( pIter->nSeg>0 ){ |
| 250562 | 251374 | /* This is a prefix term iterator. */ |
| 250563 | 251375 | if( pT==0 ){ |
| 250564 | - pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, sizeof(*pT)); | |
| 251376 | + pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, | |
| 251377 | + SZ_FTS5TOKENDATAITER(1)); | |
| 250565 | 251378 | pIter->pTokenDataIter = pT; |
| 250566 | 251379 | } |
| 250567 | 251380 | if( pT ){ |
| 250568 | 251381 | fts5TokendataIterAppendMap(p, pT, pT->terms.n, nToken, iRowid, iPos); |
| 250569 | 251382 | fts5BufferAppendBlob(&p->rc, &pT->terms, nToken, (const u8*)pToken); |
| @@ -251593,11 +252406,11 @@ | ||
| 251593 | 252406 | } |
| 251594 | 252407 | #endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ |
| 251595 | 252408 | |
| 251596 | 252409 | #if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) |
| 251597 | 252410 | static void fts5DebugRowid(int *pRc, Fts5Buffer *pBuf, i64 iKey){ |
| 251598 | - int iSegid, iHeight, iPgno, bDlidx, bTomb; /* Rowid compenents */ | |
| 252411 | + int iSegid, iHeight, iPgno, bDlidx, bTomb; /* Rowid components */ | |
| 251599 | 252412 | fts5DecodeRowid(iKey, &bTomb, &iSegid, &bDlidx, &iHeight, &iPgno); |
| 251600 | 252413 | |
| 251601 | 252414 | if( iSegid==0 ){ |
| 251602 | 252415 | if( iKey==FTS5_AVERAGES_ROWID ){ |
| 251603 | 252416 | sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "{averages} "); |
| @@ -252554,13 +253367,15 @@ | ||
| 252554 | 253367 | struct Fts5Sorter { |
| 252555 | 253368 | sqlite3_stmt *pStmt; |
| 252556 | 253369 | i64 iRowid; /* Current rowid */ |
| 252557 | 253370 | const u8 *aPoslist; /* Position lists for current row */ |
| 252558 | 253371 | int nIdx; /* Number of entries in aIdx[] */ |
| 252559 | - int aIdx[1]; /* Offsets into aPoslist for current row */ | |
| 253372 | + int aIdx[FLEXARRAY]; /* Offsets into aPoslist for current row */ | |
| 252560 | 253373 | }; |
| 252561 | 253374 | |
| 253375 | +/* Size (int bytes) of an Fts5Sorter object with N indexes */ | |
| 253376 | +#define SZ_FTS5SORTER(N) (offsetof(Fts5Sorter,nIdx)+((N+2)/2)*sizeof(i64)) | |
| 252562 | 253377 | |
| 252563 | 253378 | /* |
| 252564 | 253379 | ** Virtual-table cursor object. |
| 252565 | 253380 | ** |
| 252566 | 253381 | ** iSpecial: |
| @@ -253434,11 +254249,11 @@ | ||
| 253434 | 254249 | int rc; |
| 253435 | 254250 | const char *zRank = pCsr->zRank; |
| 253436 | 254251 | const char *zRankArgs = pCsr->zRankArgs; |
| 253437 | 254252 | |
| 253438 | 254253 | nPhrase = sqlite3Fts5ExprPhraseCount(pCsr->pExpr); |
| 253439 | - nByte = sizeof(Fts5Sorter) + sizeof(int) * (nPhrase-1); | |
| 254254 | + nByte = SZ_FTS5SORTER(nPhrase); | |
| 253440 | 254255 | pSorter = (Fts5Sorter*)sqlite3_malloc64(nByte); |
| 253441 | 254256 | if( pSorter==0 ) return SQLITE_NOMEM; |
| 253442 | 254257 | memset(pSorter, 0, (size_t)nByte); |
| 253443 | 254258 | pSorter->nIdx = nPhrase; |
| 253444 | 254259 | |
| @@ -255960,11 +256775,11 @@ | ||
| 255960 | 256775 | int nArg, /* Number of args */ |
| 255961 | 256776 | sqlite3_value **apUnused /* Function arguments */ |
| 255962 | 256777 | ){ |
| 255963 | 256778 | assert( nArg==0 ); |
| 255964 | 256779 | UNUSED_PARAM2(nArg, apUnused); |
| 255965 | - sqlite3_result_text(pCtx, "fts5: 2025-02-18 01:16:26 57caa3136d1bfca06e4f2285734a4977b8d3fa1f75bf87453b975867e9de38fc", -1, SQLITE_TRANSIENT); | |
| 256780 | + sqlite3_result_text(pCtx, "fts5: 2025-03-16 00:13:29 18bda13e197e4b4ec7464b3e70012f71edc05f73d8b14bb48bad452f81c7e185", -1, SQLITE_TRANSIENT); | |
| 255966 | 256781 | } |
| 255967 | 256782 | |
| 255968 | 256783 | /* |
| 255969 | 256784 | ** Implementation of fts5_locale(LOCALE, TEXT) function. |
| 255970 | 256785 | ** |
| @@ -256185,12 +257000,12 @@ | ||
| 256185 | 257000 | /* If SQLITE_FTS5_ENABLE_TEST_MI is defined, assume that the file |
| 256186 | 257001 | ** fts5_test_mi.c is compiled and linked into the executable. And call |
| 256187 | 257002 | ** its entry point to enable the matchinfo() demo. */ |
| 256188 | 257003 | #ifdef SQLITE_FTS5_ENABLE_TEST_MI |
| 256189 | 257004 | if( rc==SQLITE_OK ){ |
| 256190 | - extern int sqlite3Fts5TestRegisterMatchinfo(sqlite3*); | |
| 256191 | - rc = sqlite3Fts5TestRegisterMatchinfo(db); | |
| 257005 | + extern int sqlite3Fts5TestRegisterMatchinfoAPI(fts5_api*); | |
| 257006 | + rc = sqlite3Fts5TestRegisterMatchinfoAPI(&pGlobal->api); | |
| 256192 | 257007 | } |
| 256193 | 257008 | #endif |
| 256194 | 257009 | |
| 256195 | 257010 | return rc; |
| 256196 | 257011 | } |
| 256197 | 257012 |
| --- extsrc/sqlite3.c | |
| +++ extsrc/sqlite3.c | |
| @@ -16,11 +16,11 @@ | |
| 16 | ** if you want a wrapper to interface SQLite with your choice of programming |
| 17 | ** language. The code for the "sqlite3" command-line shell is also in a |
| 18 | ** separate file. This file contains only code for the core SQLite library. |
| 19 | ** |
| 20 | ** The content in this amalgamation comes from Fossil check-in |
| 21 | ** 57caa3136d1bfca06e4f2285734a4977b8d3 with changes in files: |
| 22 | ** |
| 23 | ** |
| 24 | */ |
| 25 | #ifndef SQLITE_AMALGAMATION |
| 26 | #define SQLITE_CORE 1 |
| @@ -465,11 +465,11 @@ | |
| 465 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 466 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 467 | */ |
| 468 | #define SQLITE_VERSION "3.50.0" |
| 469 | #define SQLITE_VERSION_NUMBER 3050000 |
| 470 | #define SQLITE_SOURCE_ID "2025-02-18 01:16:26 57caa3136d1bfca06e4f2285734a4977b8d3fa1f75bf87453b975867e9de38fc" |
| 471 | |
| 472 | /* |
| 473 | ** CAPI3REF: Run-Time Library Version Numbers |
| 474 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 475 | ** |
| @@ -1479,10 +1479,16 @@ | |
| 1479 | ** to block for up to M milliseconds before failing when attempting to |
| 1480 | ** obtain a file lock using the xLock or xShmLock methods of the VFS. |
| 1481 | ** The parameter is a pointer to a 32-bit signed integer that contains |
| 1482 | ** the value that M is to be set to. Before returning, the 32-bit signed |
| 1483 | ** integer is overwritten with the previous value of M. |
| 1484 | ** |
| 1485 | ** <li>[[SQLITE_FCNTL_DATA_VERSION]] |
| 1486 | ** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to |
| 1487 | ** a database file. The argument is a pointer to a 32-bit unsigned integer. |
| 1488 | ** The "data version" for the pager is written into the pointer. The |
| @@ -1576,10 +1582,11 @@ | |
| 1576 | #define SQLITE_FCNTL_CKPT_START 39 |
| 1577 | #define SQLITE_FCNTL_EXTERNAL_READER 40 |
| 1578 | #define SQLITE_FCNTL_CKSM_FILE 41 |
| 1579 | #define SQLITE_FCNTL_RESET_CACHE 42 |
| 1580 | #define SQLITE_FCNTL_NULL_IO 43 |
| 1581 | |
| 1582 | /* deprecated names */ |
| 1583 | #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE |
| 1584 | #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE |
| 1585 | #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO |
| @@ -3332,10 +3339,48 @@ | |
| 3332 | ** |
| 3333 | ** See also: [PRAGMA busy_timeout] |
| 3334 | */ |
| 3335 | SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); |
| 3336 | |
| 3337 | /* |
| 3338 | ** CAPI3REF: Convenience Routines For Running Queries |
| 3339 | ** METHOD: sqlite3 |
| 3340 | ** |
| 3341 | ** This is a legacy interface that is preserved for backwards compatibility. |
| @@ -5447,11 +5492,11 @@ | |
| 5447 | ** For all versions of SQLite up to and including 3.6.23.1, a call to |
| 5448 | ** [sqlite3_reset()] was required after sqlite3_step() returned anything |
| 5449 | ** other than [SQLITE_ROW] before any subsequent invocation of |
| 5450 | ** sqlite3_step(). Failure to reset the prepared statement using |
| 5451 | ** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from |
| 5452 | ** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1], |
| 5453 | ** sqlite3_step() began |
| 5454 | ** calling [sqlite3_reset()] automatically in this circumstance rather |
| 5455 | ** than returning [SQLITE_MISUSE]. This is not considered a compatibility |
| 5456 | ** break because any application that ever receives an SQLITE_MISUSE error |
| 5457 | ** is broken by definition. The [SQLITE_OMIT_AUTORESET] compile-time option |
| @@ -7343,10 +7388,12 @@ | |
| 7343 | ** ^Any callback set by a previous call to this function |
| 7344 | ** for the same database connection is overridden. |
| 7345 | ** |
| 7346 | ** ^The second argument is a pointer to the function to invoke when a |
| 7347 | ** row is updated, inserted or deleted in a rowid table. |
| 7348 | ** ^The first argument to the callback is a copy of the third argument |
| 7349 | ** to sqlite3_update_hook(). |
| 7350 | ** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE], |
| 7351 | ** or [SQLITE_UPDATE], depending on the operation that caused the callback |
| 7352 | ** to be invoked. |
| @@ -14097,18 +14144,26 @@ | |
| 14097 | ** * Terms in the SET clause of an UPDATE statement |
| 14098 | ** * Terms in the result set of a SELECT statement |
| 14099 | ** * Terms in the GROUP BY or ORDER BY clauses of a SELECT statement. |
| 14100 | ** * Terms in the VALUES clause of an INSERT statement |
| 14101 | ** |
| 14102 | ** The hard upper limit here is 32676. Most database people will |
| 14103 | ** tell you that in a well-normalized database, you usually should |
| 14104 | ** not have more than a dozen or so columns in any table. And if |
| 14105 | ** that is the case, there is no point in having more than a few |
| 14106 | ** dozen values in any of the other situations described above. |
| 14107 | */ |
| 14108 | #ifndef SQLITE_MAX_COLUMN |
| 14109 | # define SQLITE_MAX_COLUMN 2000 |
| 14110 | #endif |
| 14111 | |
| 14112 | /* |
| 14113 | ** The maximum length of a single SQL statement in bytes. |
| 14114 | ** |
| @@ -15117,11 +15172,21 @@ | |
| 15117 | /* |
| 15118 | ** GCC does not define the offsetof() macro so we'll have to do it |
| 15119 | ** ourselves. |
| 15120 | */ |
| 15121 | #ifndef offsetof |
| 15122 | #define offsetof(STRUCTURE,FIELD) ((int)((char*)&((STRUCTURE*)0)->FIELD)) |
| 15123 | #endif |
| 15124 | |
| 15125 | /* |
| 15126 | ** Macros to compute minimum and maximum of two numbers. |
| 15127 | */ |
| @@ -17352,12 +17417,12 @@ | |
| 17352 | SQLITE_PRIVATE int sqlite3NotPureFunc(sqlite3_context*); |
| 17353 | #ifdef SQLITE_ENABLE_BYTECODE_VTAB |
| 17354 | SQLITE_PRIVATE int sqlite3VdbeBytecodeVtabInit(sqlite3*); |
| 17355 | #endif |
| 17356 | |
| 17357 | /* Use SQLITE_ENABLE_COMMENTS to enable generation of extra comments on |
| 17358 | ** each VDBE opcode. |
| 17359 | ** |
| 17360 | ** Use the SQLITE_ENABLE_MODULE_COMMENTS macro to see some extra no-op |
| 17361 | ** comments in VDBE programs that show key decision points in the code |
| 17362 | ** generator. |
| 17363 | */ |
| @@ -18076,10 +18141,14 @@ | |
| 18076 | BusyHandler busyHandler; /* Busy callback */ |
| 18077 | Db aDbStatic[2]; /* Static space for the 2 default backends */ |
| 18078 | Savepoint *pSavepoint; /* List of active savepoints */ |
| 18079 | int nAnalysisLimit; /* Number of index rows to ANALYZE */ |
| 18080 | int busyTimeout; /* Busy handler timeout, in msec */ |
| 18081 | int nSavepoint; /* Number of non-transaction savepoints */ |
| 18082 | int nStatement; /* Number of nested statement-transactions */ |
| 18083 | i64 nDeferredCons; /* Net deferred constraints this transaction. */ |
| 18084 | i64 nDeferredImmCons; /* Net deferred immediate constraints */ |
| 18085 | int *pnBytesFreed; /* If not NULL, increment this in DbFree() */ |
| @@ -18888,12 +18957,16 @@ | |
| 18888 | u8 aAction[2]; /* ON DELETE and ON UPDATE actions, respectively */ |
| 18889 | Trigger *apTrigger[2];/* Triggers for aAction[] actions */ |
| 18890 | struct sColMap { /* Mapping of columns in pFrom to columns in zTo */ |
| 18891 | int iFrom; /* Index of column in pFrom */ |
| 18892 | char *zCol; /* Name of column in zTo. If NULL use PRIMARY KEY */ |
| 18893 | } aCol[1]; /* One entry for each of nCol columns */ |
| 18894 | }; |
| 18895 | |
| 18896 | /* |
| 18897 | ** SQLite supports many different ways to resolve a constraint |
| 18898 | ** error. ROLLBACK processing means that a constraint violation |
| 18899 | ** causes the operation in process to fail and for the current transaction |
| @@ -18952,13 +19025,16 @@ | |
| 18952 | u8 enc; /* Text encoding - one of the SQLITE_UTF* values */ |
| 18953 | u16 nKeyField; /* Number of key columns in the index */ |
| 18954 | u16 nAllField; /* Total columns, including key plus others */ |
| 18955 | sqlite3 *db; /* The database connection */ |
| 18956 | u8 *aSortFlags; /* Sort order for each column. */ |
| 18957 | CollSeq *aColl[1]; /* Collating sequence for each term of the key */ |
| 18958 | }; |
| 18959 | |
| 18960 | /* |
| 18961 | ** Allowed bit values for entries in the KeyInfo.aSortFlags[] array. |
| 18962 | */ |
| 18963 | #define KEYINFO_ORDER_DESC 0x01 /* DESC sort order */ |
| 18964 | #define KEYINFO_ORDER_BIGNULL 0x02 /* NULL is larger than any other value */ |
| @@ -19074,11 +19150,11 @@ | |
| 19074 | Expr *pPartIdxWhere; /* WHERE clause for partial indices */ |
| 19075 | ExprList *aColExpr; /* Column expressions */ |
| 19076 | Pgno tnum; /* DB Page containing root of this index */ |
| 19077 | LogEst szIdxRow; /* Estimated average row size in bytes */ |
| 19078 | u16 nKeyCol; /* Number of columns forming the key */ |
| 19079 | u16 nColumn; /* Number of columns stored in the index */ |
| 19080 | u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ |
| 19081 | unsigned idxType:2; /* 0:Normal 1:UNIQUE, 2:PRIMARY KEY, 3:IPK */ |
| 19082 | unsigned bUnordered:1; /* Use this index for == or IN queries only */ |
| 19083 | unsigned uniqNotNull:1; /* True if UNIQUE and NOT NULL for all columns */ |
| 19084 | unsigned isResized:1; /* True if resizeIndexObject() has been called */ |
| @@ -19412,14 +19488,14 @@ | |
| 19412 | #define EP_Propagate (EP_Collate|EP_Subquery|EP_HasFunc) |
| 19413 | |
| 19414 | /* Macros can be used to test, set, or clear bits in the |
| 19415 | ** Expr.flags field. |
| 19416 | */ |
| 19417 | #define ExprHasProperty(E,P) (((E)->flags&(P))!=0) |
| 19418 | #define ExprHasAllProperty(E,P) (((E)->flags&(P))==(P)) |
| 19419 | #define ExprSetProperty(E,P) (E)->flags|=(P) |
| 19420 | #define ExprClearProperty(E,P) (E)->flags&=~(P) |
| 19421 | #define ExprAlwaysTrue(E) (((E)->flags&(EP_OuterON|EP_IsTrue))==EP_IsTrue) |
| 19422 | #define ExprAlwaysFalse(E) (((E)->flags&(EP_OuterON|EP_IsFalse))==EP_IsFalse) |
| 19423 | #define ExprIsFullSize(E) (((E)->flags&(EP_Reduced|EP_TokenOnly))==0) |
| 19424 | |
| 19425 | /* Macros used to ensure that the correct members of unions are accessed |
| @@ -19527,12 +19603,17 @@ | |
| 19527 | u16 iAlias; /* Index into Parse.aAlias[] for zName */ |
| 19528 | } x; |
| 19529 | int iConstExprReg; /* Register in which Expr value is cached. Used only |
| 19530 | ** by Parse.pConstExpr */ |
| 19531 | } u; |
| 19532 | } a[1]; /* One slot for each expression in the list */ |
| 19533 | }; |
| 19534 | |
| 19535 | /* |
| 19536 | ** Allowed values for Expr.a.eEName |
| 19537 | */ |
| 19538 | #define ENAME_NAME 0 /* The AS clause of a result set */ |
| @@ -19557,13 +19638,16 @@ | |
| 19557 | */ |
| 19558 | struct IdList { |
| 19559 | int nId; /* Number of identifiers on the list */ |
| 19560 | struct IdList_item { |
| 19561 | char *zName; /* Name of the identifier */ |
| 19562 | } a[1]; |
| 19563 | }; |
| 19564 | |
| 19565 | /* |
| 19566 | ** Allowed values for IdList.eType, which determines which value of the a.u4 |
| 19567 | ** is valid. |
| 19568 | */ |
| 19569 | #define EU4_NONE 0 /* Does not use IdList.a.u4 */ |
| @@ -19679,14 +19763,22 @@ | |
| 19679 | ** is used to hold the FROM clause of a SELECT statement. SrcList also |
| 19680 | ** represents the target tables for DELETE, INSERT, and UPDATE statements. |
| 19681 | ** |
| 19682 | */ |
| 19683 | struct SrcList { |
| 19684 | int nSrc; /* Number of tables or subqueries in the FROM clause */ |
| 19685 | u32 nAlloc; /* Number of entries allocated in a[] below */ |
| 19686 | SrcItem a[1]; /* One entry for each identifier on the list */ |
| 19687 | }; |
| 19688 | |
| 19689 | /* |
| 19690 | ** Permitted values of the SrcList.a.jointype field |
| 19691 | */ |
| 19692 | #define JT_INNER 0x01 /* Any kind of inner or cross join */ |
| @@ -20747,12 +20839,16 @@ | |
| 20747 | */ |
| 20748 | struct With { |
| 20749 | int nCte; /* Number of CTEs in the WITH clause */ |
| 20750 | int bView; /* Belongs to the outermost Select of a view */ |
| 20751 | With *pOuter; /* Containing WITH clause, or NULL */ |
| 20752 | Cte a[1]; /* For each CTE in the WITH clause.... */ |
| 20753 | }; |
| 20754 | |
| 20755 | /* |
| 20756 | ** The Cte object is not guaranteed to persist for the entire duration |
| 20757 | ** of code generation. (The query flattener or other parser tree |
| 20758 | ** edits might delete it.) The following object records information |
| @@ -20778,12 +20874,16 @@ | |
| 20778 | */ |
| 20779 | struct DbClientData { |
| 20780 | DbClientData *pNext; /* Next in a linked list */ |
| 20781 | void *pData; /* The data */ |
| 20782 | void (*xDestructor)(void*); /* Destructor. Might be NULL */ |
| 20783 | char zName[1]; /* Name of this client data. MUST BE LAST */ |
| 20784 | }; |
| 20785 | |
| 20786 | #ifdef SQLITE_DEBUG |
| 20787 | /* |
| 20788 | ** An instance of the TreeView object is used for printing the content of |
| 20789 | ** data structures on sqlite3DebugPrintf() using a tree-like view. |
| @@ -21223,11 +21323,11 @@ | |
| 21223 | SQLITE_PRIVATE int sqlite3ColumnsFromExprList(Parse*,ExprList*,i16*,Column**); |
| 21224 | SQLITE_PRIVATE void sqlite3SubqueryColumnTypes(Parse*,Table*,Select*,char); |
| 21225 | SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse*,Select*,char); |
| 21226 | SQLITE_PRIVATE void sqlite3OpenSchemaTable(Parse *, int); |
| 21227 | SQLITE_PRIVATE Index *sqlite3PrimaryKeyIndex(Table*); |
| 21228 | SQLITE_PRIVATE i16 sqlite3TableColumnToIndex(Index*, i16); |
| 21229 | #ifdef SQLITE_OMIT_GENERATED_COLUMNS |
| 21230 | # define sqlite3TableColumnToStorage(T,X) (X) /* No-op pass-through */ |
| 21231 | # define sqlite3StorageColumnToTable(T,X) (X) /* No-op pass-through */ |
| 21232 | #else |
| 21233 | SQLITE_PRIVATE i16 sqlite3TableColumnToStorage(Table*, i16); |
| @@ -21321,11 +21421,11 @@ | |
| 21321 | SQLITE_PRIVATE void sqlite3SrcListShiftJoinType(Parse*,SrcList*); |
| 21322 | SQLITE_PRIVATE void sqlite3SrcListAssignCursors(Parse*, SrcList*); |
| 21323 | SQLITE_PRIVATE void sqlite3IdListDelete(sqlite3*, IdList*); |
| 21324 | SQLITE_PRIVATE void sqlite3ClearOnOrUsing(sqlite3*, OnOrUsing*); |
| 21325 | SQLITE_PRIVATE void sqlite3SrcListDelete(sqlite3*, SrcList*); |
| 21326 | SQLITE_PRIVATE Index *sqlite3AllocateIndexObject(sqlite3*,i16,int,char**); |
| 21327 | SQLITE_PRIVATE void sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*, |
| 21328 | Expr*, int, int, u8); |
| 21329 | SQLITE_PRIVATE void sqlite3DropIndex(Parse*, SrcList*, int); |
| 21330 | SQLITE_PRIVATE int sqlite3Select(Parse*, Select*, SelectDest*); |
| 21331 | SQLITE_PRIVATE Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*, |
| @@ -21457,11 +21557,12 @@ | |
| 21457 | SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3*,const IdList*); |
| 21458 | SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3*,const Select*,int); |
| 21459 | SQLITE_PRIVATE FuncDef *sqlite3FunctionSearch(int,const char*); |
| 21460 | SQLITE_PRIVATE void sqlite3InsertBuiltinFuncs(FuncDef*,int); |
| 21461 | SQLITE_PRIVATE FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,u8,u8); |
| 21462 | SQLITE_PRIVATE void sqlite3QuoteValue(StrAccum*,sqlite3_value*); |
| 21463 | SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void); |
| 21464 | SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void); |
| 21465 | SQLITE_PRIVATE void sqlite3RegisterJsonFunctions(void); |
| 21466 | SQLITE_PRIVATE void sqlite3RegisterPerConnectionBuiltinFunctions(sqlite3*); |
| 21467 | #if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON) |
| @@ -22557,10 +22658,13 @@ | |
| 22557 | #ifdef SQLITE_ENABLE_RTREE |
| 22558 | "ENABLE_RTREE", |
| 22559 | #endif |
| 22560 | #ifdef SQLITE_ENABLE_SESSION |
| 22561 | "ENABLE_SESSION", |
| 22562 | #endif |
| 22563 | #ifdef SQLITE_ENABLE_SNAPSHOT |
| 22564 | "ENABLE_SNAPSHOT", |
| 22565 | #endif |
| 22566 | #ifdef SQLITE_ENABLE_SORTER_REFERENCES |
| @@ -22612,10 +22716,13 @@ | |
| 22612 | "EXTRA_IFNULLROW", |
| 22613 | #endif |
| 22614 | #ifdef SQLITE_EXTRA_INIT |
| 22615 | "EXTRA_INIT=" CTIMEOPT_VAL(SQLITE_EXTRA_INIT), |
| 22616 | #endif |
| 22617 | #ifdef SQLITE_EXTRA_SHUTDOWN |
| 22618 | "EXTRA_SHUTDOWN=" CTIMEOPT_VAL(SQLITE_EXTRA_SHUTDOWN), |
| 22619 | #endif |
| 22620 | #ifdef SQLITE_FTS3_MAX_EXPR_DEPTH |
| 22621 | "FTS3_MAX_EXPR_DEPTH=" CTIMEOPT_VAL(SQLITE_FTS3_MAX_EXPR_DEPTH), |
| @@ -23596,16 +23703,23 @@ | |
| 23596 | #ifdef SQLITE_ENABLE_COLUMN_USED_MASK |
| 23597 | u64 maskUsed; /* Mask of columns used by this cursor */ |
| 23598 | #endif |
| 23599 | VdbeTxtBlbCache *pCache; /* Cache of large TEXT or BLOB values */ |
| 23600 | |
| 23601 | /* 2*nField extra array elements allocated for aType[], beyond the one |
| 23602 | ** static element declared in the structure. nField total array slots for |
| 23603 | ** aType[] and nField+1 array slots for aOffset[] */ |
| 23604 | u32 aType[1]; /* Type values record decode. MUST BE LAST */ |
| 23605 | }; |
| 23606 | |
| 23607 | /* Return true if P is a null-only cursor |
| 23608 | */ |
| 23609 | #define IsNullCursor(P) \ |
| 23610 | ((P)->eCurType==CURTYPE_PSEUDO && (P)->nullRow && (P)->seekResult==0) |
| 23611 | |
| @@ -23858,13 +23972,20 @@ | |
| 23858 | int iOp; /* Instruction number of OP_Function */ |
| 23859 | int isError; /* Error code returned by the function. */ |
| 23860 | u8 enc; /* Encoding to use for results */ |
| 23861 | u8 skipFlag; /* Skip accumulator loading if true */ |
| 23862 | u16 argc; /* Number of arguments */ |
| 23863 | sqlite3_value *argv[1]; /* Argument set */ |
| 23864 | }; |
| 23865 | |
| 23866 | |
| 23867 | /* The ScanStatus object holds a single value for the |
| 23868 | ** sqlite3_stmt_scanstatus() interface. |
| 23869 | ** |
| 23870 | ** aAddrRange[]: |
| @@ -23994,11 +24115,11 @@ | |
| 23994 | struct PreUpdate { |
| 23995 | Vdbe *v; |
| 23996 | VdbeCursor *pCsr; /* Cursor to read old values from */ |
| 23997 | int op; /* One of SQLITE_INSERT, UPDATE, DELETE */ |
| 23998 | u8 *aRecord; /* old.* database record */ |
| 23999 | KeyInfo keyinfo; |
| 24000 | UnpackedRecord *pUnpacked; /* Unpacked version of aRecord[] */ |
| 24001 | UnpackedRecord *pNewUnpacked; /* Unpacked version of new.* record */ |
| 24002 | int iNewReg; /* Register for new.* values */ |
| 24003 | int iBlobWrite; /* Value returned by preupdate_blobwrite() */ |
| 24004 | i64 iKey1; /* First key value passed to hook */ |
| @@ -24006,10 +24127,11 @@ | |
| 24006 | Mem oldipk; /* Memory cell holding "old" IPK value */ |
| 24007 | Mem *aNew; /* Array of new.* values */ |
| 24008 | Table *pTab; /* Schema object being updated */ |
| 24009 | Index *pPk; /* PK index if pTab is WITHOUT ROWID */ |
| 24010 | sqlite3_value **apDflt; /* Array of default values, if required */ |
| 24011 | }; |
| 24012 | |
| 24013 | /* |
| 24014 | ** An instance of this object is used to pass an vector of values into |
| 24015 | ** OP_VFilter, the xFilter method of a virtual table. The vector is the |
| @@ -24372,12 +24494,13 @@ | |
| 24372 | u32 nFree = countLookasideSlots(db->lookaside.pFree); |
| 24373 | #ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE |
| 24374 | nInit += countLookasideSlots(db->lookaside.pSmallInit); |
| 24375 | nFree += countLookasideSlots(db->lookaside.pSmallFree); |
| 24376 | #endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */ |
| 24377 | if( pHighwater ) *pHighwater = db->lookaside.nSlot - nInit; |
| 24378 | return db->lookaside.nSlot - (nInit+nFree); |
| 24379 | } |
| 24380 | |
| 24381 | /* |
| 24382 | ** Query status information for a single database connection |
| 24383 | */ |
| @@ -24426,11 +24549,11 @@ | |
| 24426 | testcase( op==SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE ); |
| 24427 | testcase( op==SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL ); |
| 24428 | assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)>=0 ); |
| 24429 | assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)<3 ); |
| 24430 | *pCurrent = 0; |
| 24431 | *pHighwater = db->lookaside.anStat[op - SQLITE_DBSTATUS_LOOKASIDE_HIT]; |
| 24432 | if( resetFlag ){ |
| 24433 | db->lookaside.anStat[op - SQLITE_DBSTATUS_LOOKASIDE_HIT] = 0; |
| 24434 | } |
| 24435 | break; |
| 24436 | } |
| @@ -25938,11 +26061,11 @@ | |
| 25938 | ** Return the number of days after the most recent Sunday. |
| 25939 | ** |
| 25940 | ** In other words, return the day of the week according |
| 25941 | ** to this code: |
| 25942 | ** |
| 25943 | ** 0=Sunday, 1=Monday, 2=Tues, ..., 6=Saturday |
| 25944 | */ |
| 25945 | static int daysAfterSunday(DateTime *pDate){ |
| 25946 | assert( pDate->validJD ); |
| 25947 | return (int)((pDate->iJD+129600000)/86400000) % 7; |
| 25948 | } |
| @@ -31541,21 +31664,21 @@ | |
| 31541 | #define etSTRING 5 /* Strings. %s */ |
| 31542 | #define etDYNSTRING 6 /* Dynamically allocated strings. %z */ |
| 31543 | #define etPERCENT 7 /* Percent symbol. %% */ |
| 31544 | #define etCHARX 8 /* Characters. %c */ |
| 31545 | /* The rest are extensions, not normally found in printf() */ |
| 31546 | #define etSQLESCAPE 9 /* Strings with '\'' doubled. %q */ |
| 31547 | #define etSQLESCAPE2 10 /* Strings with '\'' doubled and enclosed in '', |
| 31548 | NULL pointers replaced by SQL NULL. %Q */ |
| 31549 | #define etTOKEN 11 /* a pointer to a Token structure */ |
| 31550 | #define etSRCITEM 12 /* a pointer to a SrcItem */ |
| 31551 | #define etPOINTER 13 /* The %p conversion */ |
| 31552 | #define etSQLESCAPE3 14 /* %w -> Strings with '\"' doubled */ |
| 31553 | #define etORDINAL 15 /* %r -> 1st, 2nd, 3rd, 4th, etc. English only */ |
| 31554 | #define etDECIMAL 16 /* %d or %u, but not %x, %o */ |
| 31555 | |
| 31556 | #define etINVALID 17 /* Any unrecognized conversion type */ |
| 31557 | |
| 31558 | |
| 31559 | /* |
| 31560 | ** An "etByte" is an 8-bit unsigned value. |
| 31561 | */ |
| @@ -31590,13 +31713,13 @@ | |
| 31590 | static const et_info fmtinfo[] = { |
| 31591 | { 'd', 10, 1, etDECIMAL, 0, 0 }, |
| 31592 | { 's', 0, 4, etSTRING, 0, 0 }, |
| 31593 | { 'g', 0, 1, etGENERIC, 30, 0 }, |
| 31594 | { 'z', 0, 4, etDYNSTRING, 0, 0 }, |
| 31595 | { 'q', 0, 4, etSQLESCAPE, 0, 0 }, |
| 31596 | { 'Q', 0, 4, etSQLESCAPE2, 0, 0 }, |
| 31597 | { 'w', 0, 4, etSQLESCAPE3, 0, 0 }, |
| 31598 | { 'c', 0, 0, etCHARX, 0, 0 }, |
| 31599 | { 'o', 8, 0, etRADIX, 0, 2 }, |
| 31600 | { 'u', 10, 0, etDECIMAL, 0, 0 }, |
| 31601 | { 'x', 16, 0, etRADIX, 16, 1 }, |
| 31602 | { 'X', 16, 0, etRADIX, 0, 4 }, |
| @@ -32189,29 +32312,11 @@ | |
| 32189 | }else{ |
| 32190 | buf[0] = 0; |
| 32191 | } |
| 32192 | }else{ |
| 32193 | unsigned int ch = va_arg(ap,unsigned int); |
| 32194 | if( ch<0x00080 ){ |
| 32195 | buf[0] = ch & 0xff; |
| 32196 | length = 1; |
| 32197 | }else if( ch<0x00800 ){ |
| 32198 | buf[0] = 0xc0 + (u8)((ch>>6)&0x1f); |
| 32199 | buf[1] = 0x80 + (u8)(ch & 0x3f); |
| 32200 | length = 2; |
| 32201 | }else if( ch<0x10000 ){ |
| 32202 | buf[0] = 0xe0 + (u8)((ch>>12)&0x0f); |
| 32203 | buf[1] = 0x80 + (u8)((ch>>6) & 0x3f); |
| 32204 | buf[2] = 0x80 + (u8)(ch & 0x3f); |
| 32205 | length = 3; |
| 32206 | }else{ |
| 32207 | buf[0] = 0xf0 + (u8)((ch>>18) & 0x07); |
| 32208 | buf[1] = 0x80 + (u8)((ch>>12) & 0x3f); |
| 32209 | buf[2] = 0x80 + (u8)((ch>>6) & 0x3f); |
| 32210 | buf[3] = 0x80 + (u8)(ch & 0x3f); |
| 32211 | length = 4; |
| 32212 | } |
| 32213 | } |
| 32214 | if( precision>1 ){ |
| 32215 | i64 nPrior = 1; |
| 32216 | width -= precision-1; |
| 32217 | if( width>1 && !flag_leftjustify ){ |
| @@ -32287,26 +32392,35 @@ | |
| 32287 | /* Adjust width to account for extra bytes in UTF-8 characters */ |
| 32288 | int ii = length - 1; |
| 32289 | while( ii>=0 ) if( (bufpt[ii--] & 0xc0)==0x80 ) width++; |
| 32290 | } |
| 32291 | break; |
| 32292 | case etSQLESCAPE: /* %q: Escape ' characters */ |
| 32293 | case etSQLESCAPE2: /* %Q: Escape ' and enclose in '...' */ |
| 32294 | case etSQLESCAPE3: { /* %w: Escape " characters */ |
| 32295 | i64 i, j, k, n; |
| 32296 | int needQuote, isnull; |
| 32297 | char ch; |
| 32298 | char q = ((xtype==etSQLESCAPE3)?'"':'\''); /* Quote character */ |
| 32299 | char *escarg; |
| 32300 | |
| 32301 | if( bArgList ){ |
| 32302 | escarg = getTextArg(pArgList); |
| 32303 | }else{ |
| 32304 | escarg = va_arg(ap,char*); |
| 32305 | } |
| 32306 | isnull = escarg==0; |
| 32307 | if( isnull ) escarg = (xtype==etSQLESCAPE2 ? "NULL" : "(NULL)"); |
| 32308 | /* For %q, %Q, and %w, the precision is the number of bytes (or |
| 32309 | ** characters if the ! flags is present) to use from the input. |
| 32310 | ** Because of the extra quoting characters inserted, the number |
| 32311 | ** of output characters may be larger than the precision. |
| 32312 | */ |
| @@ -32315,26 +32429,77 @@ | |
| 32315 | if( ch==q ) n++; |
| 32316 | if( flag_altform2 && (ch&0xc0)==0xc0 ){ |
| 32317 | while( (escarg[i+1]&0xc0)==0x80 ){ i++; } |
| 32318 | } |
| 32319 | } |
| 32320 | needQuote = !isnull && xtype==etSQLESCAPE2; |
| 32321 | n += i + 3; |
| 32322 | if( n>etBUFSIZE ){ |
| 32323 | bufpt = zExtra = printfTempBuf(pAccum, n); |
| 32324 | if( bufpt==0 ) return; |
| 32325 | }else{ |
| 32326 | bufpt = buf; |
| 32327 | } |
| 32328 | j = 0; |
| 32329 | if( needQuote ) bufpt[j++] = q; |
| 32330 | k = i; |
| 32331 | for(i=0; i<k; i++){ |
| 32332 | bufpt[j++] = ch = escarg[i]; |
| 32333 | if( ch==q ) bufpt[j++] = ch; |
| 32334 | } |
| 32335 | if( needQuote ) bufpt[j++] = q; |
| 32336 | bufpt[j] = 0; |
| 32337 | length = j; |
| 32338 | goto adjust_width_for_utf8; |
| 32339 | } |
| 32340 | case etTOKEN: { |
| @@ -34828,10 +34993,39 @@ | |
| 34828 | *zOut++ = (u8)(((c>>10)&0x003F) + (((c-0x10000)>>10)&0x00C0)); \ |
| 34829 | *zOut++ = (u8)(0x00DC + ((c>>8)&0x03)); \ |
| 34830 | *zOut++ = (u8)(c&0x00FF); \ |
| 34831 | } \ |
| 34832 | } |
| 34833 | |
| 34834 | /* |
| 34835 | ** Translate a single UTF-8 character. Return the unicode value. |
| 34836 | ** |
| 34837 | ** During translation, assume that the byte that zTerm points |
| @@ -36934,11 +37128,11 @@ | |
| 36934 | return 0; |
| 36935 | #endif |
| 36936 | } |
| 36937 | |
| 36938 | /* |
| 36939 | ** Compute the absolute value of a 32-bit signed integer, of possible. Or |
| 36940 | ** if the integer has a value of -2147483648, return +2147483647 |
| 36941 | */ |
| 36942 | SQLITE_PRIVATE int sqlite3AbsInt32(int x){ |
| 36943 | if( x>=0 ) return x; |
| 36944 | if( x==(int)0x80000000 ) return 0x7fffffff; |
| @@ -38911,10 +39105,11 @@ | |
| 38911 | #if SQLITE_ENABLE_LOCKING_STYLE || defined(__APPLE__) |
| 38912 | unsigned fsFlags; /* cached details from statfs() */ |
| 38913 | #endif |
| 38914 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 38915 | unsigned iBusyTimeout; /* Wait this many millisec on locks */ |
| 38916 | #endif |
| 38917 | #if OS_VXWORKS |
| 38918 | struct vxworksFileId *pId; /* Unique file ID */ |
| 38919 | #endif |
| 38920 | #ifdef SQLITE_DEBUG |
| @@ -40304,10 +40499,17 @@ | |
| 40304 | pInode->nLock++; |
| 40305 | }else{ |
| 40306 | rc = 0; |
| 40307 | } |
| 40308 | }else{ |
| 40309 | rc = osSetPosixAdvisoryLock(pFile->h, pLock, pFile); |
| 40310 | } |
| 40311 | return rc; |
| 40312 | } |
| 40313 | |
| @@ -42665,21 +42867,27 @@ | |
| 42665 | return SQLITE_OK; |
| 42666 | } |
| 42667 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 42668 | case SQLITE_FCNTL_LOCK_TIMEOUT: { |
| 42669 | int iOld = pFile->iBusyTimeout; |
| 42670 | #if SQLITE_ENABLE_SETLK_TIMEOUT==1 |
| 42671 | pFile->iBusyTimeout = *(int*)pArg; |
| 42672 | #elif SQLITE_ENABLE_SETLK_TIMEOUT==2 |
| 42673 | pFile->iBusyTimeout = !!(*(int*)pArg); |
| 42674 | #else |
| 42675 | # error "SQLITE_ENABLE_SETLK_TIMEOUT must be set to 1 or 2" |
| 42676 | #endif |
| 42677 | *(int*)pArg = iOld; |
| 42678 | return SQLITE_OK; |
| 42679 | } |
| 42680 | #endif |
| 42681 | #if SQLITE_MAX_MMAP_SIZE>0 |
| 42682 | case SQLITE_FCNTL_MMAP_SIZE: { |
| 42683 | i64 newLimit = *(i64*)pArg; |
| 42684 | int rc = SQLITE_OK; |
| 42685 | if( newLimit>sqlite3GlobalConfig.mxMmap ){ |
| @@ -43658,11 +43866,11 @@ | |
| 43658 | ** occur later in the above list than the lock being obtained may be |
| 43659 | ** held. |
| 43660 | ** |
| 43661 | ** It is not permitted to block on the RECOVER lock. |
| 43662 | */ |
| 43663 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 43664 | { |
| 43665 | u16 lockMask = (p->exclMask|p->sharedMask); |
| 43666 | assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || ( |
| 43667 | (ofst!=2) /* not RECOVER */ |
| 43668 | && (ofst!=1 || lockMask==0 || lockMask==2) |
| @@ -45467,11 +45675,11 @@ | |
| 45467 | sp.tv_sec = microseconds / 1000000; |
| 45468 | sp.tv_nsec = (microseconds % 1000000) * 1000; |
| 45469 | |
| 45470 | /* Almost all modern unix systems support nanosleep(). But if you are |
| 45471 | ** compiling for one of the rare exceptions, you can use |
| 45472 | ** -DHAVE_NANOSLEEP=0 (perhaps in conjuction with -DHAVE_USLEEP if |
| 45473 | ** usleep() is available) in order to bypass the use of nanosleep() */ |
| 45474 | nanosleep(&sp, NULL); |
| 45475 | |
| 45476 | UNUSED_PARAMETER(NotUsed); |
| 45477 | return microseconds; |
| @@ -47188,11 +47396,21 @@ | |
| 47188 | HANDLE hMap; /* Handle for accessing memory mapping */ |
| 47189 | void *pMapRegion; /* Area memory mapped */ |
| 47190 | sqlite3_int64 mmapSize; /* Size of mapped region */ |
| 47191 | sqlite3_int64 mmapSizeMax; /* Configured FCNTL_MMAP_SIZE value */ |
| 47192 | #endif |
| 47193 | }; |
| 47194 | |
| 47195 | /* |
| 47196 | ** The winVfsAppData structure is used for the pAppData member for all of the |
| 47197 | ** Win32 VFS variants. |
| 47198 | */ |
| @@ -47623,10 +47841,16 @@ | |
| 47623 | #endif |
| 47624 | |
| 47625 | #define osGetFullPathNameW ((DWORD(WINAPI*)(LPCWSTR,DWORD,LPWSTR, \ |
| 47626 | LPWSTR*))aSyscall[25].pCurrent) |
| 47627 | |
| 47628 | { "GetLastError", (SYSCALL)GetLastError, 0 }, |
| 47629 | |
| 47630 | #define osGetLastError ((DWORD(WINAPI*)(VOID))aSyscall[26].pCurrent) |
| 47631 | |
| 47632 | #if !defined(SQLITE_OMIT_LOAD_EXTENSION) |
| @@ -47905,15 +48129,17 @@ | |
| 47905 | #endif |
| 47906 | |
| 47907 | #define osCreateEventExW ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,LPCWSTR, \ |
| 47908 | DWORD,DWORD))aSyscall[62].pCurrent) |
| 47909 | |
| 47910 | #if !SQLITE_OS_WINRT |
| 47911 | { "WaitForSingleObject", (SYSCALL)WaitForSingleObject, 0 }, |
| 47912 | #else |
| 47913 | { "WaitForSingleObject", (SYSCALL)0, 0 }, |
| 47914 | #endif |
| 47915 | |
| 47916 | #define osWaitForSingleObject ((DWORD(WINAPI*)(HANDLE, \ |
| 47917 | DWORD))aSyscall[63].pCurrent) |
| 47918 | |
| 47919 | #if !SQLITE_OS_WINCE |
| @@ -48056,10 +48282,44 @@ | |
| 48056 | #endif |
| 48057 | |
| 48058 | #define osFlushViewOfFile \ |
| 48059 | ((BOOL(WINAPI*)(LPCVOID,SIZE_T))aSyscall[79].pCurrent) |
| 48060 | |
| 48061 | }; /* End of the overrideable system calls */ |
| 48062 | |
| 48063 | /* |
| 48064 | ** This is the xSetSystemCall() method of sqlite3_vfs for all of the |
| 48065 | ** "win32" VFSes. Return SQLITE_OK upon successfully updating the |
| @@ -48354,11 +48614,13 @@ | |
| 48354 | (sInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) ? 2 : 1, 0); |
| 48355 | #endif |
| 48356 | } |
| 48357 | return osInterlockedCompareExchange(&sqlite3_os_type, 2, 2)==2; |
| 48358 | #elif SQLITE_TEST |
| 48359 | return osInterlockedCompareExchange(&sqlite3_os_type, 2, 2)==2; |
| 48360 | #else |
| 48361 | /* |
| 48362 | ** NOTE: All sub-platforms where the GetVersionEx[AW] functions are |
| 48363 | ** deprecated are always assumed to be based on the NT kernel. |
| 48364 | */ |
| @@ -49440,10 +49702,89 @@ | |
| 49440 | return osLockFile(*phFile, offsetLow, offsetHigh, numBytesLow, |
| 49441 | numBytesHigh); |
| 49442 | } |
| 49443 | #endif |
| 49444 | } |
| 49445 | |
| 49446 | /* |
| 49447 | ** Unlock a file region. |
| 49448 | */ |
| 49449 | static BOOL winUnlockFile( |
| @@ -49471,10 +49812,18 @@ | |
| 49471 | return osUnlockFile(*phFile, offsetLow, offsetHigh, numBytesLow, |
| 49472 | numBytesHigh); |
| 49473 | } |
| 49474 | #endif |
| 49475 | } |
| 49476 | |
| 49477 | /***************************************************************************** |
| 49478 | ** The next group of routines implement the I/O methods specified |
| 49479 | ** by the sqlite3_io_methods object. |
| 49480 | ******************************************************************************/ |
| @@ -49485,69 +49834,73 @@ | |
| 49485 | #ifndef INVALID_SET_FILE_POINTER |
| 49486 | # define INVALID_SET_FILE_POINTER ((DWORD)-1) |
| 49487 | #endif |
| 49488 | |
| 49489 | /* |
| 49490 | ** Move the current position of the file handle passed as the first |
| 49491 | ** argument to offset iOffset within the file. If successful, return 0. |
| 49492 | ** Otherwise, set pFile->lastErrno and return non-zero. |
| 49493 | */ |
| 49494 | static int winSeekFile(winFile *pFile, sqlite3_int64 iOffset){ |
| 49495 | #if !SQLITE_OS_WINRT |
| 49496 | LONG upperBits; /* Most sig. 32 bits of new offset */ |
| 49497 | LONG lowerBits; /* Least sig. 32 bits of new offset */ |
| 49498 | DWORD dwRet; /* Value returned by SetFilePointer() */ |
| 49499 | DWORD lastErrno; /* Value returned by GetLastError() */ |
| 49500 | |
| 49501 | OSTRACE(("SEEK file=%p, offset=%lld\n", pFile->h, iOffset)); |
| 49502 | |
| 49503 | upperBits = (LONG)((iOffset>>32) & 0x7fffffff); |
| 49504 | lowerBits = (LONG)(iOffset & 0xffffffff); |
| 49505 | |
| 49506 | /* API oddity: If successful, SetFilePointer() returns a dword |
| 49507 | ** containing the lower 32-bits of the new file-offset. Or, if it fails, |
| 49508 | ** it returns INVALID_SET_FILE_POINTER. However according to MSDN, |
| 49509 | ** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine |
| 49510 | ** whether an error has actually occurred, it is also necessary to call |
| 49511 | ** GetLastError(). |
| 49512 | */ |
| 49513 | dwRet = osSetFilePointer(pFile->h, lowerBits, &upperBits, FILE_BEGIN); |
| 49514 | |
| 49515 | if( (dwRet==INVALID_SET_FILE_POINTER |
| 49516 | && ((lastErrno = osGetLastError())!=NO_ERROR)) ){ |
| 49517 | pFile->lastErrno = lastErrno; |
| 49518 | winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno, |
| 49519 | "winSeekFile", pFile->zPath); |
| 49520 | OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h)); |
| 49521 | return 1; |
| 49522 | } |
| 49523 | |
| 49524 | OSTRACE(("SEEK file=%p, rc=SQLITE_OK\n", pFile->h)); |
| 49525 | return 0; |
| 49526 | #else |
| 49527 | /* |
| 49528 | ** Same as above, except that this implementation works for WinRT. |
| 49529 | */ |
| 49530 | |
| 49531 | LARGE_INTEGER x; /* The new offset */ |
| 49532 | BOOL bRet; /* Value returned by SetFilePointerEx() */ |
| 49533 | |
| 49534 | x.QuadPart = iOffset; |
| 49535 | bRet = osSetFilePointerEx(pFile->h, x, 0, FILE_BEGIN); |
| 49536 | |
| 49537 | if(!bRet){ |
| 49538 | pFile->lastErrno = osGetLastError(); |
| 49539 | winLogError(SQLITE_IOERR_SEEK, pFile->lastErrno, |
| 49540 | "winSeekFile", pFile->zPath); |
| 49541 | OSTRACE(("SEEK file=%p, rc=SQLITE_IOERR_SEEK\n", pFile->h)); |
| 49542 | return 1; |
| 49543 | } |
| 49544 | |
| 49545 | OSTRACE(("SEEK file=%p, rc=SQLITE_OK\n", pFile->h)); |
| 49546 | return 0; |
| 49547 | #endif |
| 49548 | } |
| 49549 | |
| 49550 | #if SQLITE_MAX_MMAP_SIZE>0 |
| 49551 | /* Forward references to VFS helper methods used for memory mapped files */ |
| 49552 | static int winMapfile(winFile*, sqlite3_int64); |
| 49553 | static int winUnmapfile(winFile*); |
| @@ -49803,10 +50156,64 @@ | |
| 49803 | } |
| 49804 | OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n", |
| 49805 | osGetCurrentProcessId(), pFile, pFile->h)); |
| 49806 | return SQLITE_OK; |
| 49807 | } |
| 49808 | |
| 49809 | /* |
| 49810 | ** Truncate an open file to a specified size |
| 49811 | */ |
| 49812 | static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){ |
| @@ -50059,31 +50466,32 @@ | |
| 50059 | /* |
| 50060 | ** Acquire a reader lock. |
| 50061 | ** Different API routines are called depending on whether or not this |
| 50062 | ** is Win9x or WinNT. |
| 50063 | */ |
| 50064 | static int winGetReadLock(winFile *pFile){ |
| 50065 | int res; |
| 50066 | OSTRACE(("READ-LOCK file=%p, lock=%d\n", pFile->h, pFile->locktype)); |
| 50067 | if( osIsNT() ){ |
| 50068 | #if SQLITE_OS_WINCE |
| 50069 | /* |
| 50070 | ** NOTE: Windows CE is handled differently here due its lack of the Win32 |
| 50071 | ** API LockFileEx. |
| 50072 | */ |
| 50073 | res = winceLockFile(&pFile->h, SHARED_FIRST, 0, 1, 0); |
| 50074 | #else |
| 50075 | res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS, SHARED_FIRST, 0, |
| 50076 | SHARED_SIZE, 0); |
| 50077 | #endif |
| 50078 | } |
| 50079 | #ifdef SQLITE_WIN32_HAS_ANSI |
| 50080 | else{ |
| 50081 | int lk; |
| 50082 | sqlite3_randomness(sizeof(lk), &lk); |
| 50083 | pFile->sharedLockByte = (short)((lk & 0x7fffffff)%(SHARED_SIZE - 1)); |
| 50084 | res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, |
| 50085 | SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0); |
| 50086 | } |
| 50087 | #endif |
| 50088 | if( res == 0 ){ |
| 50089 | pFile->lastErrno = osGetLastError(); |
| @@ -50174,50 +50582,66 @@ | |
| 50174 | */ |
| 50175 | assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK ); |
| 50176 | assert( locktype!=PENDING_LOCK ); |
| 50177 | assert( locktype!=RESERVED_LOCK || pFile->locktype==SHARED_LOCK ); |
| 50178 | |
| 50179 | /* Lock the PENDING_LOCK byte if we need to acquire a PENDING lock or |
| 50180 | ** a SHARED lock. If we are acquiring a SHARED lock, the acquisition of |
| 50181 | ** the PENDING_LOCK byte is temporary. |
| 50182 | */ |
| 50183 | newLocktype = pFile->locktype; |
| 50184 | if( pFile->locktype==NO_LOCK |
| 50185 | || (locktype==EXCLUSIVE_LOCK && pFile->locktype<=RESERVED_LOCK) |
| 50186 | ){ |
| 50187 | int cnt = 3; |
| 50188 | while( cnt-->0 && (res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS, |
| 50189 | PENDING_BYTE, 0, 1, 0))==0 ){ |
| 50190 | /* Try 3 times to get the pending lock. This is needed to work |
| 50191 | ** around problems caused by indexing and/or anti-virus software on |
| 50192 | ** Windows systems. |
| 50193 | ** If you are using this code as a model for alternative VFSes, do not |
| 50194 | ** copy this retry logic. It is a hack intended for Windows only. |
| 50195 | */ |
| 50196 | lastErrno = osGetLastError(); |
| 50197 | OSTRACE(("LOCK-PENDING-FAIL file=%p, count=%d, result=%d\n", |
| 50198 | pFile->h, cnt, res)); |
| 50199 | if( lastErrno==ERROR_INVALID_HANDLE ){ |
| 50200 | pFile->lastErrno = lastErrno; |
| 50201 | rc = SQLITE_IOERR_LOCK; |
| 50202 | OSTRACE(("LOCK-FAIL file=%p, count=%d, rc=%s\n", |
| 50203 | pFile->h, cnt, sqlite3ErrName(rc))); |
| 50204 | return rc; |
| 50205 | } |
| 50206 | if( cnt ) sqlite3_win32_sleep(1); |
| 50207 | } |
| 50208 | gotPendingLock = res; |
| 50209 | if( !res ){ |
| 50210 | lastErrno = osGetLastError(); |
| 50211 | } |
| 50212 | } |
| 50213 | |
| 50214 | /* Acquire a shared lock |
| 50215 | */ |
| 50216 | if( locktype==SHARED_LOCK && res ){ |
| 50217 | assert( pFile->locktype==NO_LOCK ); |
| 50218 | res = winGetReadLock(pFile); |
| 50219 | if( res ){ |
| 50220 | newLocktype = SHARED_LOCK; |
| 50221 | }else{ |
| 50222 | lastErrno = osGetLastError(); |
| 50223 | } |
| @@ -50251,11 +50675,11 @@ | |
| 50251 | SHARED_SIZE, 0); |
| 50252 | if( res ){ |
| 50253 | newLocktype = EXCLUSIVE_LOCK; |
| 50254 | }else{ |
| 50255 | lastErrno = osGetLastError(); |
| 50256 | winGetReadLock(pFile); |
| 50257 | } |
| 50258 | } |
| 50259 | |
| 50260 | /* If we are holding a PENDING lock that ought to be released, then |
| 50261 | ** release it now. |
| @@ -50331,11 +50755,11 @@ | |
| 50331 | OSTRACE(("UNLOCK file=%p, oldLock=%d(%d), newLock=%d\n", |
| 50332 | pFile->h, pFile->locktype, pFile->sharedLockByte, locktype)); |
| 50333 | type = pFile->locktype; |
| 50334 | if( type>=EXCLUSIVE_LOCK ){ |
| 50335 | winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); |
| 50336 | if( locktype==SHARED_LOCK && !winGetReadLock(pFile) ){ |
| 50337 | /* This should never happen. We should always be able to |
| 50338 | ** reacquire the read lock */ |
| 50339 | rc = winLogError(SQLITE_IOERR_UNLOCK, osGetLastError(), |
| 50340 | "winUnlock", pFile->zPath); |
| 50341 | } |
| @@ -50541,10 +50965,32 @@ | |
| 50541 | } |
| 50542 | OSTRACE(("FCNTL file=%p, rc=%s\n", pFile->h, sqlite3ErrName(rc))); |
| 50543 | return rc; |
| 50544 | } |
| 50545 | #endif |
| 50546 | } |
| 50547 | OSTRACE(("FCNTL file=%p, rc=SQLITE_NOTFOUND\n", pFile->h)); |
| 50548 | return SQLITE_NOTFOUND; |
| 50549 | } |
| 50550 | |
| @@ -50621,36 +51067,39 @@ | |
| 50621 | ** nRef |
| 50622 | ** pNext |
| 50623 | ** |
| 50624 | ** The following fields are read-only after the object is created: |
| 50625 | ** |
| 50626 | ** fid |
| 50627 | ** zFilename |
| 50628 | ** |
| 50629 | ** Either winShmNode.mutex must be held or winShmNode.nRef==0 and |
| 50630 | ** winShmMutexHeld() is true when reading or writing any other field |
| 50631 | ** in this structure. |
| 50632 | ** |
| 50633 | */ |
| 50634 | struct winShmNode { |
| 50635 | sqlite3_mutex *mutex; /* Mutex to access this object */ |
| 50636 | char *zFilename; /* Name of the file */ |
| 50637 | winFile hFile; /* File handle from winOpen */ |
| 50638 | |
| 50639 | int szRegion; /* Size of shared-memory regions */ |
| 50640 | int nRegion; /* Size of array apRegion */ |
| 50641 | u8 isReadonly; /* True if read-only */ |
| 50642 | u8 isUnlocked; /* True if no DMS lock held */ |
| 50643 | |
| 50644 | struct ShmRegion { |
| 50645 | HANDLE hMap; /* File handle from CreateFileMapping */ |
| 50646 | void *pMap; |
| 50647 | } *aRegion; |
| 50648 | DWORD lastErrno; /* The Windows errno from the last I/O error */ |
| 50649 | |
| 50650 | int nRef; /* Number of winShm objects pointing to this */ |
| 50651 | winShm *pFirst; /* All winShm objects pointing to this */ |
| 50652 | winShmNode *pNext; /* Next in list of all winShmNode objects */ |
| 50653 | #if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) |
| 50654 | u8 nextShmId; /* Next available winShm.id value */ |
| 50655 | #endif |
| 50656 | }; |
| @@ -50662,27 +51111,19 @@ | |
| 50662 | */ |
| 50663 | static winShmNode *winShmNodeList = 0; |
| 50664 | |
| 50665 | /* |
| 50666 | ** Structure used internally by this VFS to record the state of an |
| 50667 | ** open shared memory connection. |
| 50668 | ** |
| 50669 | ** The following fields are initialized when this object is created and |
| 50670 | ** are read-only thereafter: |
| 50671 | ** |
| 50672 | ** winShm.pShmNode |
| 50673 | ** winShm.id |
| 50674 | ** |
| 50675 | ** All other fields are read/write. The winShm.pShmNode->mutex must be held |
| 50676 | ** while accessing any read/write fields. |
| 50677 | */ |
| 50678 | struct winShm { |
| 50679 | winShmNode *pShmNode; /* The underlying winShmNode object */ |
| 50680 | winShm *pNext; /* Next winShm with the same winShmNode */ |
| 50681 | u8 hasMutex; /* True if holding the winShmNode mutex */ |
| 50682 | u16 sharedMask; /* Mask of shared locks held */ |
| 50683 | u16 exclMask; /* Mask of exclusive locks held */ |
| 50684 | #if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) |
| 50685 | u8 id; /* Id of this connection with its winShmNode */ |
| 50686 | #endif |
| 50687 | }; |
| 50688 | |
| @@ -50690,54 +51131,10 @@ | |
| 50690 | ** Constants used for locking |
| 50691 | */ |
| 50692 | #define WIN_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */ |
| 50693 | #define WIN_SHM_DMS (WIN_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */ |
| 50694 | |
| 50695 | /* |
| 50696 | ** Apply advisory locks for all n bytes beginning at ofst. |
| 50697 | */ |
| 50698 | #define WINSHM_UNLCK 1 |
| 50699 | #define WINSHM_RDLCK 2 |
| 50700 | #define WINSHM_WRLCK 3 |
| 50701 | static int winShmSystemLock( |
| 50702 | winShmNode *pFile, /* Apply locks to this open shared-memory segment */ |
| 50703 | int lockType, /* WINSHM_UNLCK, WINSHM_RDLCK, or WINSHM_WRLCK */ |
| 50704 | int ofst, /* Offset to first byte to be locked/unlocked */ |
| 50705 | int nByte /* Number of bytes to lock or unlock */ |
| 50706 | ){ |
| 50707 | int rc = 0; /* Result code form Lock/UnlockFileEx() */ |
| 50708 | |
| 50709 | /* Access to the winShmNode object is serialized by the caller */ |
| 50710 | assert( pFile->nRef==0 || sqlite3_mutex_held(pFile->mutex) ); |
| 50711 | |
| 50712 | OSTRACE(("SHM-LOCK file=%p, lock=%d, offset=%d, size=%d\n", |
| 50713 | pFile->hFile.h, lockType, ofst, nByte)); |
| 50714 | |
| 50715 | /* Release/Acquire the system-level lock */ |
| 50716 | if( lockType==WINSHM_UNLCK ){ |
| 50717 | rc = winUnlockFile(&pFile->hFile.h, ofst, 0, nByte, 0); |
| 50718 | }else{ |
| 50719 | /* Initialize the locking parameters */ |
| 50720 | DWORD dwFlags = LOCKFILE_FAIL_IMMEDIATELY; |
| 50721 | if( lockType == WINSHM_WRLCK ) dwFlags |= LOCKFILE_EXCLUSIVE_LOCK; |
| 50722 | rc = winLockFile(&pFile->hFile.h, dwFlags, ofst, 0, nByte, 0); |
| 50723 | } |
| 50724 | |
| 50725 | if( rc!= 0 ){ |
| 50726 | rc = SQLITE_OK; |
| 50727 | }else{ |
| 50728 | pFile->lastErrno = osGetLastError(); |
| 50729 | rc = SQLITE_BUSY; |
| 50730 | } |
| 50731 | |
| 50732 | OSTRACE(("SHM-LOCK file=%p, func=%s, errno=%lu, rc=%s\n", |
| 50733 | pFile->hFile.h, (lockType == WINSHM_UNLCK) ? "winUnlockFile" : |
| 50734 | "winLockFile", pFile->lastErrno, sqlite3ErrName(rc))); |
| 50735 | |
| 50736 | return rc; |
| 50737 | } |
| 50738 | |
| 50739 | /* Forward references to VFS methods */ |
| 50740 | static int winOpen(sqlite3_vfs*,const char*,sqlite3_file*,int,int*); |
| 50741 | static int winDelete(sqlite3_vfs *,const char*,int); |
| 50742 | |
| 50743 | /* |
| @@ -50765,15 +51162,11 @@ | |
| 50765 | bRc = osCloseHandle(p->aRegion[i].hMap); |
| 50766 | OSTRACE(("SHM-PURGE-CLOSE pid=%lu, region=%d, rc=%s\n", |
| 50767 | osGetCurrentProcessId(), i, bRc ? "ok" : "failed")); |
| 50768 | UNUSED_VARIABLE_VALUE(bRc); |
| 50769 | } |
| 50770 | if( p->hFile.h!=NULL && p->hFile.h!=INVALID_HANDLE_VALUE ){ |
| 50771 | SimulateIOErrorBenign(1); |
| 50772 | winClose((sqlite3_file *)&p->hFile); |
| 50773 | SimulateIOErrorBenign(0); |
| 50774 | } |
| 50775 | if( deleteFlag ){ |
| 50776 | SimulateIOErrorBenign(1); |
| 50777 | sqlite3BeginBenignMalloc(); |
| 50778 | winDelete(pVfs, p->zFilename, 0); |
| 50779 | sqlite3EndBenignMalloc(); |
| @@ -50787,46 +51180,166 @@ | |
| 50787 | } |
| 50788 | } |
| 50789 | } |
| 50790 | |
| 50791 | /* |
| 50792 | ** The DMS lock has not yet been taken on shm file pShmNode. Attempt to |
| 50793 | ** take it now. Return SQLITE_OK if successful, or an SQLite error |
| 50794 | ** code otherwise. |
| 50795 | ** |
| 50796 | ** If the DMS cannot be locked because this is a readonly_shm=1 |
| 50797 | ** connection and no other process already holds a lock, return |
| 50798 | ** SQLITE_READONLY_CANTINIT and set pShmNode->isUnlocked=1. |
| 50799 | */ |
| 50800 | static int winLockSharedMemory(winShmNode *pShmNode){ |
| 50801 | int rc = winShmSystemLock(pShmNode, WINSHM_WRLCK, WIN_SHM_DMS, 1); |
| 50802 | |
| 50803 | if( rc==SQLITE_OK ){ |
| 50804 | if( pShmNode->isReadonly ){ |
| 50805 | pShmNode->isUnlocked = 1; |
| 50806 | winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1); |
| 50807 | return SQLITE_READONLY_CANTINIT; |
| 50808 | }else if( winTruncate((sqlite3_file*)&pShmNode->hFile, 0) ){ |
| 50809 | winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1); |
| 50810 | return winLogError(SQLITE_IOERR_SHMOPEN, osGetLastError(), |
| 50811 | "winLockSharedMemory", pShmNode->zFilename); |
| 50812 | } |
| 50813 | } |
| 50814 | |
| 50815 | if( rc==SQLITE_OK ){ |
| 50816 | winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1); |
| 50817 | } |
| 50818 | |
| 50819 | return winShmSystemLock(pShmNode, WINSHM_RDLCK, WIN_SHM_DMS, 1); |
| 50820 | } |
| 50821 | |
| 50822 | /* |
| 50823 | ** Open the shared-memory area associated with database file pDbFd. |
| 50824 | ** |
| 50825 | ** When opening a new shared-memory file, if no other instances of that |
| 50826 | ** file are currently open, in this process or in other processes, then |
| 50827 | ** the file must be truncated to zero length or have its header cleared. |
| 50828 | */ |
| 50829 | static int winOpenSharedMemory(winFile *pDbFd){ |
| 50830 | struct winShm *p; /* The connection to be opened */ |
| 50831 | winShmNode *pShmNode = 0; /* The underlying mmapped file */ |
| 50832 | int rc = SQLITE_OK; /* Result code */ |
| @@ -50834,102 +51347,87 @@ | |
| 50834 | int nName; /* Size of zName in bytes */ |
| 50835 | |
| 50836 | assert( pDbFd->pShm==0 ); /* Not previously opened */ |
| 50837 | |
| 50838 | /* Allocate space for the new sqlite3_shm object. Also speculatively |
| 50839 | ** allocate space for a new winShmNode and filename. |
| 50840 | */ |
| 50841 | p = sqlite3MallocZero( sizeof(*p) ); |
| 50842 | if( p==0 ) return SQLITE_IOERR_NOMEM_BKPT; |
| 50843 | nName = sqlite3Strlen30(pDbFd->zPath); |
| 50844 | pNew = sqlite3MallocZero( sizeof(*pShmNode) + (i64)nName + 17 ); |
| 50845 | if( pNew==0 ){ |
| 50846 | sqlite3_free(p); |
| 50847 | return SQLITE_IOERR_NOMEM_BKPT; |
| 50848 | } |
| 50849 | pNew->zFilename = (char*)&pNew[1]; |
| 50850 | sqlite3_snprintf(nName+15, pNew->zFilename, "%s-shm", pDbFd->zPath); |
| 50851 | sqlite3FileSuffix3(pDbFd->zPath, pNew->zFilename); |
| 50852 | |
| 50853 | /* Look to see if there is an existing winShmNode that can be used. |
| 50854 | ** If no matching winShmNode currently exists, create a new one. |
| 50855 | */ |
| 50856 | winShmEnterMutex(); |
| 50857 | for(pShmNode = winShmNodeList; pShmNode; pShmNode=pShmNode->pNext){ |
| 50858 | /* TBD need to come up with better match here. Perhaps |
| 50859 | ** use FILE_ID_BOTH_DIR_INFO Structure. |
| 50860 | */ |
| 50861 | if( sqlite3StrICmp(pShmNode->zFilename, pNew->zFilename)==0 ) break; |
| 50862 | } |
| 50863 | if( pShmNode ){ |
| 50864 | sqlite3_free(pNew); |
| 50865 | }else{ |
| 50866 | int inFlags = SQLITE_OPEN_WAL; |
| 50867 | int outFlags = 0; |
| 50868 | |
| 50869 | pShmNode = pNew; |
| 50870 | pNew = 0; |
| 50871 | ((winFile*)(&pShmNode->hFile))->h = INVALID_HANDLE_VALUE; |
| 50872 | pShmNode->pNext = winShmNodeList; |
| 50873 | winShmNodeList = pShmNode; |
| 50874 | |
| 50875 | if( sqlite3GlobalConfig.bCoreMutex ){ |
| 50876 | pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); |
| 50877 | if( pShmNode->mutex==0 ){ |
| 50878 | rc = SQLITE_IOERR_NOMEM_BKPT; |
| 50879 | goto shm_open_err; |
| 50880 | } |
| 50881 | } |
| 50882 | |
| 50883 | if( 0==sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){ |
| 50884 | inFlags |= SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; |
| 50885 | }else{ |
| 50886 | inFlags |= SQLITE_OPEN_READONLY; |
| 50887 | } |
| 50888 | rc = winOpen(pDbFd->pVfs, pShmNode->zFilename, |
| 50889 | (sqlite3_file*)&pShmNode->hFile, |
| 50890 | inFlags, &outFlags); |
| 50891 | if( rc!=SQLITE_OK ){ |
| 50892 | rc = winLogError(rc, osGetLastError(), "winOpenShm", |
| 50893 | pShmNode->zFilename); |
| 50894 | goto shm_open_err; |
| 50895 | } |
| 50896 | if( outFlags==SQLITE_OPEN_READONLY ) pShmNode->isReadonly = 1; |
| 50897 | |
| 50898 | rc = winLockSharedMemory(pShmNode); |
| 50899 | if( rc!=SQLITE_OK && rc!=SQLITE_READONLY_CANTINIT ) goto shm_open_err; |
| 50900 | } |
| 50901 | |
| 50902 | /* Make the new connection a child of the winShmNode */ |
| 50903 | p->pShmNode = pShmNode; |
| 50904 | #if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) |
| 50905 | p->id = pShmNode->nextShmId++; |
| 50906 | #endif |
| 50907 | pShmNode->nRef++; |
| 50908 | pDbFd->pShm = p; |
| 50909 | winShmLeaveMutex(); |
| 50910 | |
| 50911 | /* The reference count on pShmNode has already been incremented under |
| 50912 | ** the cover of the winShmEnterMutex() mutex and the pointer from the |
| 50913 | ** new (struct winShm) object to the pShmNode has been set. All that is |
| 50914 | ** left to do is to link the new object into the linked list starting |
| 50915 | ** at pShmNode->pFirst. This must be done while holding the pShmNode->mutex |
| 50916 | ** mutex. |
| 50917 | */ |
| 50918 | sqlite3_mutex_enter(pShmNode->mutex); |
| 50919 | p->pNext = pShmNode->pFirst; |
| 50920 | pShmNode->pFirst = p; |
| 50921 | sqlite3_mutex_leave(pShmNode->mutex); |
| 50922 | return rc; |
| 50923 | |
| 50924 | /* Jump here on any error */ |
| 50925 | shm_open_err: |
| 50926 | winShmSystemLock(pShmNode, WINSHM_UNLCK, WIN_SHM_DMS, 1); |
| 50927 | winShmPurge(pDbFd->pVfs, 0); /* This call frees pShmNode if required */ |
| 50928 | sqlite3_free(p); |
| 50929 | sqlite3_free(pNew); |
| 50930 | winShmLeaveMutex(); |
| 50931 | return rc; |
| 50932 | } |
| 50933 | |
| 50934 | /* |
| 50935 | ** Close a connection to shared-memory. Delete the underlying |
| @@ -50940,38 +51438,33 @@ | |
| 50940 | int deleteFlag /* Delete after closing if true */ |
| 50941 | ){ |
| 50942 | winFile *pDbFd; /* Database holding shared-memory */ |
| 50943 | winShm *p; /* The connection to be closed */ |
| 50944 | winShmNode *pShmNode; /* The underlying shared-memory file */ |
| 50945 | winShm **pp; /* For looping over sibling connections */ |
| 50946 | |
| 50947 | pDbFd = (winFile*)fd; |
| 50948 | p = pDbFd->pShm; |
| 50949 | if( p==0 ) return SQLITE_OK; |
| 50950 | pShmNode = p->pShmNode; |
| 50951 | |
| 50952 | /* Remove connection p from the set of connections associated |
| 50953 | ** with pShmNode */ |
| 50954 | sqlite3_mutex_enter(pShmNode->mutex); |
| 50955 | for(pp=&pShmNode->pFirst; (*pp)!=p; pp = &(*pp)->pNext){} |
| 50956 | *pp = p->pNext; |
| 50957 | |
| 50958 | /* Free the connection p */ |
| 50959 | sqlite3_free(p); |
| 50960 | pDbFd->pShm = 0; |
| 50961 | sqlite3_mutex_leave(pShmNode->mutex); |
| 50962 | |
| 50963 | /* If pShmNode->nRef has reached 0, then close the underlying |
| 50964 | ** shared-memory file, too */ |
| 50965 | winShmEnterMutex(); |
| 50966 | assert( pShmNode->nRef>0 ); |
| 50967 | pShmNode->nRef--; |
| 50968 | if( pShmNode->nRef==0 ){ |
| 50969 | winShmPurge(pDbFd->pVfs, deleteFlag); |
| 50970 | } |
| 50971 | winShmLeaveMutex(); |
| 50972 | |
| 50973 | return SQLITE_OK; |
| 50974 | } |
| 50975 | |
| 50976 | /* |
| 50977 | ** Change the lock state for a shared-memory segment. |
| @@ -50982,14 +51475,13 @@ | |
| 50982 | int n, /* Number of locks to acquire or release */ |
| 50983 | int flags /* What to do with the lock */ |
| 50984 | ){ |
| 50985 | winFile *pDbFd = (winFile*)fd; /* Connection holding shared memory */ |
| 50986 | winShm *p = pDbFd->pShm; /* The shared memory being locked */ |
| 50987 | winShm *pX; /* For looping over all siblings */ |
| 50988 | winShmNode *pShmNode; |
| 50989 | int rc = SQLITE_OK; /* Result code */ |
| 50990 | u16 mask; /* Mask of locks to take or release */ |
| 50991 | |
| 50992 | if( p==0 ) return SQLITE_IOERR_SHMLOCK; |
| 50993 | pShmNode = p->pShmNode; |
| 50994 | if( NEVER(pShmNode==0) ) return SQLITE_IOERR_SHMLOCK; |
| 50995 | |
| @@ -50999,89 +51491,86 @@ | |
| 50999 | || flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE) |
| 51000 | || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED) |
| 51001 | || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) ); |
| 51002 | assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 ); |
| 51003 | |
| 51004 | mask = (u16)((1U<<(ofst+n)) - (1U<<ofst)); |
| 51005 | assert( n>1 || mask==(1<<ofst) ); |
| 51006 | sqlite3_mutex_enter(pShmNode->mutex); |
| 51007 | if( flags & SQLITE_SHM_UNLOCK ){ |
| 51008 | u16 allMask = 0; /* Mask of locks held by siblings */ |
| 51009 | |
| 51010 | /* See if any siblings hold this same lock */ |
| 51011 | for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ |
| 51012 | if( pX==p ) continue; |
| 51013 | assert( (pX->exclMask & (p->exclMask|p->sharedMask))==0 ); |
| 51014 | allMask |= pX->sharedMask; |
| 51015 | } |
| 51016 | |
| 51017 | /* Unlock the system-level locks */ |
| 51018 | if( (mask & allMask)==0 ){ |
| 51019 | rc = winShmSystemLock(pShmNode, WINSHM_UNLCK, ofst+WIN_SHM_BASE, n); |
| 51020 | }else{ |
| 51021 | rc = SQLITE_OK; |
| 51022 | } |
| 51023 | |
| 51024 | /* Undo the local locks */ |
| 51025 | if( rc==SQLITE_OK ){ |
| 51026 | p->exclMask &= ~mask; |
| 51027 | p->sharedMask &= ~mask; |
| 51028 | } |
| 51029 | }else if( flags & SQLITE_SHM_SHARED ){ |
| 51030 | u16 allShared = 0; /* Union of locks held by connections other than "p" */ |
| 51031 | |
| 51032 | /* Find out which shared locks are already held by sibling connections. |
| 51033 | ** If any sibling already holds an exclusive lock, go ahead and return |
| 51034 | ** SQLITE_BUSY. |
| 51035 | */ |
| 51036 | for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ |
| 51037 | if( (pX->exclMask & mask)!=0 ){ |
| 51038 | rc = SQLITE_BUSY; |
| 51039 | break; |
| 51040 | } |
| 51041 | allShared |= pX->sharedMask; |
| 51042 | } |
| 51043 | |
| 51044 | /* Get shared locks at the system level, if necessary */ |
| 51045 | if( rc==SQLITE_OK ){ |
| 51046 | if( (allShared & mask)==0 ){ |
| 51047 | rc = winShmSystemLock(pShmNode, WINSHM_RDLCK, ofst+WIN_SHM_BASE, n); |
| 51048 | }else{ |
| 51049 | rc = SQLITE_OK; |
| 51050 | } |
| 51051 | } |
| 51052 | |
| 51053 | /* Get the local shared locks */ |
| 51054 | if( rc==SQLITE_OK ){ |
| 51055 | p->sharedMask |= mask; |
| 51056 | } |
| 51057 | }else{ |
| 51058 | /* Make sure no sibling connections hold locks that will block this |
| 51059 | ** lock. If any do, return SQLITE_BUSY right away. |
| 51060 | */ |
| 51061 | for(pX=pShmNode->pFirst; pX; pX=pX->pNext){ |
| 51062 | if( (pX->exclMask & mask)!=0 || (pX->sharedMask & mask)!=0 ){ |
| 51063 | rc = SQLITE_BUSY; |
| 51064 | break; |
| 51065 | } |
| 51066 | } |
| 51067 | |
| 51068 | /* Get the exclusive locks at the system level. Then if successful |
| 51069 | ** also mark the local connection as being locked. |
| 51070 | */ |
| 51071 | if( rc==SQLITE_OK ){ |
| 51072 | rc = winShmSystemLock(pShmNode, WINSHM_WRLCK, ofst+WIN_SHM_BASE, n); |
| 51073 | if( rc==SQLITE_OK ){ |
| 51074 | assert( (p->sharedMask & mask)==0 ); |
| 51075 | p->exclMask |= mask; |
| 51076 | } |
| 51077 | } |
| 51078 | } |
| 51079 | sqlite3_mutex_leave(pShmNode->mutex); |
| 51080 | OSTRACE(("SHM-LOCK pid=%lu, id=%d, sharedMask=%03x, exclMask=%03x, rc=%s\n", |
| 51081 | osGetCurrentProcessId(), p->id, p->sharedMask, p->exclMask, |
| 51082 | sqlite3ErrName(rc))); |
| 51083 | return rc; |
| 51084 | } |
| 51085 | |
| 51086 | /* |
| 51087 | ** Implement a memory barrier or memory fence on shared memory. |
| @@ -51139,17 +51628,19 @@ | |
| 51139 | } |
| 51140 | pShmNode = pShm->pShmNode; |
| 51141 | |
| 51142 | sqlite3_mutex_enter(pShmNode->mutex); |
| 51143 | if( pShmNode->isUnlocked ){ |
| 51144 | rc = winLockSharedMemory(pShmNode); |
| 51145 | if( rc!=SQLITE_OK ) goto shmpage_out; |
| 51146 | pShmNode->isUnlocked = 0; |
| 51147 | } |
| 51148 | assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 ); |
| 51149 | |
| 51150 | if( pShmNode->nRegion<=iRegion ){ |
| 51151 | struct ShmRegion *apNew; /* New aRegion[] array */ |
| 51152 | int nByte = (iRegion+1)*szRegion; /* Minimum required file size */ |
| 51153 | sqlite3_int64 sz; /* Current size of wal-index file */ |
| 51154 | |
| 51155 | pShmNode->szRegion = szRegion; |
| @@ -51156,35 +51647,32 @@ | |
| 51156 | |
| 51157 | /* The requested region is not mapped into this processes address space. |
| 51158 | ** Check to see if it has been allocated (i.e. if the wal-index file is |
| 51159 | ** large enough to contain the requested region). |
| 51160 | */ |
| 51161 | rc = winFileSize((sqlite3_file *)&pShmNode->hFile, &sz); |
| 51162 | if( rc!=SQLITE_OK ){ |
| 51163 | rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(), |
| 51164 | "winShmMap1", pDbFd->zPath); |
| 51165 | goto shmpage_out; |
| 51166 | } |
| 51167 | |
| 51168 | if( sz<nByte ){ |
| 51169 | /* The requested memory region does not exist. If isWrite is set to |
| 51170 | ** zero, exit early. *pp will be set to NULL and SQLITE_OK returned. |
| 51171 | ** |
| 51172 | ** Alternatively, if isWrite is non-zero, use ftruncate() to allocate |
| 51173 | ** the requested memory region. |
| 51174 | */ |
| 51175 | if( !isWrite ) goto shmpage_out; |
| 51176 | rc = winTruncate((sqlite3_file *)&pShmNode->hFile, nByte); |
| 51177 | if( rc!=SQLITE_OK ){ |
| 51178 | rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(), |
| 51179 | "winShmMap2", pDbFd->zPath); |
| 51180 | goto shmpage_out; |
| 51181 | } |
| 51182 | } |
| 51183 | |
| 51184 | /* Map the requested memory region into this processes address space. */ |
| 51185 | apNew = (struct ShmRegion *)sqlite3_realloc64( |
| 51186 | pShmNode->aRegion, (iRegion+1)*sizeof(apNew[0]) |
| 51187 | ); |
| 51188 | if( !apNew ){ |
| 51189 | rc = SQLITE_IOERR_NOMEM_BKPT; |
| 51190 | goto shmpage_out; |
| @@ -51199,22 +51687,17 @@ | |
| 51199 | while( pShmNode->nRegion<=iRegion ){ |
| 51200 | HANDLE hMap = NULL; /* file-mapping handle */ |
| 51201 | void *pMap = 0; /* Mapped memory region */ |
| 51202 | |
| 51203 | #if SQLITE_OS_WINRT |
| 51204 | hMap = osCreateFileMappingFromApp(pShmNode->hFile.h, |
| 51205 | NULL, protect, nByte, NULL |
| 51206 | ); |
| 51207 | #elif defined(SQLITE_WIN32_HAS_WIDE) |
| 51208 | hMap = osCreateFileMappingW(pShmNode->hFile.h, |
| 51209 | NULL, protect, 0, nByte, NULL |
| 51210 | ); |
| 51211 | #elif defined(SQLITE_WIN32_HAS_ANSI) && SQLITE_WIN32_CREATEFILEMAPPINGA |
| 51212 | hMap = osCreateFileMappingA(pShmNode->hFile.h, |
| 51213 | NULL, protect, 0, nByte, NULL |
| 51214 | ); |
| 51215 | #endif |
| 51216 | OSTRACE(("SHM-MAP-CREATE pid=%lu, region=%d, size=%d, rc=%s\n", |
| 51217 | osGetCurrentProcessId(), pShmNode->nRegion, nByte, |
| 51218 | hMap ? "ok" : "failed")); |
| 51219 | if( hMap ){ |
| 51220 | int iOffset = pShmNode->nRegion*szRegion; |
| @@ -51253,11 +51736,13 @@ | |
| 51253 | char *p = (char *)pShmNode->aRegion[iRegion].pMap; |
| 51254 | *pp = (void *)&p[iOffsetShift]; |
| 51255 | }else{ |
| 51256 | *pp = 0; |
| 51257 | } |
| 51258 | if( pShmNode->isReadonly && rc==SQLITE_OK ) rc = SQLITE_READONLY; |
| 51259 | sqlite3_mutex_leave(pShmNode->mutex); |
| 51260 | return rc; |
| 51261 | } |
| 51262 | |
| 51263 | #else |
| @@ -51594,30 +52079,10 @@ | |
| 51594 | /* caller will handle out of memory */ |
| 51595 | return zConverted; |
| 51596 | } |
| 51597 | #endif |
| 51598 | |
| 51599 | /* |
| 51600 | ** Convert a UTF-8 filename into whatever form the underlying |
| 51601 | ** operating system wants filenames in. Space to hold the result |
| 51602 | ** is obtained from malloc and must be freed by the calling |
| 51603 | ** function. |
| 51604 | */ |
| 51605 | static void *winConvertFromUtf8Filename(const char *zFilename){ |
| 51606 | void *zConverted = 0; |
| 51607 | if( osIsNT() ){ |
| 51608 | zConverted = winUtf8ToUnicode(zFilename); |
| 51609 | } |
| 51610 | #ifdef SQLITE_WIN32_HAS_ANSI |
| 51611 | else{ |
| 51612 | zConverted = winUtf8ToMbcs(zFilename, osAreFileApisANSI()); |
| 51613 | } |
| 51614 | #endif |
| 51615 | /* caller will handle out of memory */ |
| 51616 | return zConverted; |
| 51617 | } |
| 51618 | |
| 51619 | /* |
| 51620 | ** This function returns non-zero if the specified UTF-8 string buffer |
| 51621 | ** ends with a directory separator character or one was successfully |
| 51622 | ** added to it. |
| 51623 | */ |
| @@ -53067,11 +53532,11 @@ | |
| 53067 | }; |
| 53068 | #endif |
| 53069 | |
| 53070 | /* Double-check that the aSyscall[] array has been constructed |
| 53071 | ** correctly. See ticket [bb3a86e890c8e96ab] */ |
| 53072 | assert( ArraySize(aSyscall)==80 ); |
| 53073 | |
| 53074 | /* get memory map allocation granularity */ |
| 53075 | memset(&winSysInfo, 0, sizeof(SYSTEM_INFO)); |
| 53076 | #if SQLITE_OS_WINRT |
| 53077 | osGetNativeSystemInfo(&winSysInfo); |
| @@ -54125,11 +54590,11 @@ | |
| 54125 | ** Empirical testing showed that the *37 multiplier |
| 54126 | ** (an arbitrary prime)in the hash function provided |
| 54127 | ** no fewer collisions than the no-op *1. */ |
| 54128 | #define BITVEC_HASH(X) (((X)*1)%BITVEC_NINT) |
| 54129 | |
| 54130 | #define BITVEC_NPTR (BITVEC_USIZE/sizeof(Bitvec *)) |
| 54131 | |
| 54132 | |
| 54133 | /* |
| 54134 | ** A bitmap is an instance of the following structure. |
| 54135 | ** |
| @@ -54308,11 +54773,11 @@ | |
| 54308 | if (!p) { |
| 54309 | return; |
| 54310 | } |
| 54311 | } |
| 54312 | if( p->iSize<=BITVEC_NBIT ){ |
| 54313 | p->u.aBitmap[i/BITVEC_SZELEM] &= ~(1 << (i&(BITVEC_SZELEM-1))); |
| 54314 | }else{ |
| 54315 | unsigned int j; |
| 54316 | u32 *aiValues = pBuf; |
| 54317 | memcpy(aiValues, p->u.aHash, sizeof(p->u.aHash)); |
| 54318 | memset(p->u.aHash, 0, sizeof(p->u.aHash)); |
| @@ -54359,11 +54824,11 @@ | |
| 54359 | ** up to N bits. Let I be an integer between 0 and N. 0<=I<N. |
| 54360 | ** Then the following macros can be used to set, clear, or test |
| 54361 | ** individual bits within V. |
| 54362 | */ |
| 54363 | #define SETBIT(V,I) V[I>>3] |= (1<<(I&7)) |
| 54364 | #define CLEARBIT(V,I) V[I>>3] &= ~(1<<(I&7)) |
| 54365 | #define TESTBIT(V,I) (V[I>>3]&(1<<(I&7)))!=0 |
| 54366 | |
| 54367 | /* |
| 54368 | ** This routine runs an extensive test of the Bitvec code. |
| 54369 | ** |
| @@ -55643,14 +56108,10 @@ | |
| 55643 | void *pStart, *pEnd; /* Bounds of global page cache memory */ |
| 55644 | /* Above requires no mutex. Use mutex below for variable that follow. */ |
| 55645 | sqlite3_mutex *mutex; /* Mutex for accessing the following: */ |
| 55646 | PgFreeslot *pFree; /* Free page blocks */ |
| 55647 | int nFreeSlot; /* Number of unused pcache slots */ |
| 55648 | /* The following value requires a mutex to change. We skip the mutex on |
| 55649 | ** reading because (1) most platforms read a 32-bit integer atomically and |
| 55650 | ** (2) even if an incorrect value is read, no great harm is done since this |
| 55651 | ** is really just an optimization. */ |
| 55652 | int bUnderPressure; /* True if low on PAGECACHE memory */ |
| 55653 | } pcache1_g; |
| 55654 | |
| 55655 | /* |
| 55656 | ** All code in this file should access the global structure above via the |
| @@ -55694,11 +56155,11 @@ | |
| 55694 | pcache1.szSlot = sz; |
| 55695 | pcache1.nSlot = pcache1.nFreeSlot = n; |
| 55696 | pcache1.nReserve = n>90 ? 10 : (n/10 + 1); |
| 55697 | pcache1.pStart = pBuf; |
| 55698 | pcache1.pFree = 0; |
| 55699 | pcache1.bUnderPressure = 0; |
| 55700 | while( n-- ){ |
| 55701 | p = (PgFreeslot*)pBuf; |
| 55702 | p->pNext = pcache1.pFree; |
| 55703 | pcache1.pFree = p; |
| 55704 | pBuf = (void*)&((char*)pBuf)[sz]; |
| @@ -55762,11 +56223,11 @@ | |
| 55762 | sqlite3_mutex_enter(pcache1.mutex); |
| 55763 | p = (PgHdr1 *)pcache1.pFree; |
| 55764 | if( p ){ |
| 55765 | pcache1.pFree = pcache1.pFree->pNext; |
| 55766 | pcache1.nFreeSlot--; |
| 55767 | pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve; |
| 55768 | assert( pcache1.nFreeSlot>=0 ); |
| 55769 | sqlite3StatusHighwater(SQLITE_STATUS_PAGECACHE_SIZE, nByte); |
| 55770 | sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_USED, 1); |
| 55771 | } |
| 55772 | sqlite3_mutex_leave(pcache1.mutex); |
| @@ -55801,11 +56262,11 @@ | |
| 55801 | sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_USED, 1); |
| 55802 | pSlot = (PgFreeslot*)p; |
| 55803 | pSlot->pNext = pcache1.pFree; |
| 55804 | pcache1.pFree = pSlot; |
| 55805 | pcache1.nFreeSlot++; |
| 55806 | pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve; |
| 55807 | assert( pcache1.nFreeSlot<=pcache1.nSlot ); |
| 55808 | sqlite3_mutex_leave(pcache1.mutex); |
| 55809 | }else{ |
| 55810 | assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) ); |
| 55811 | sqlite3MemdebugSetType(p, MEMTYPE_HEAP); |
| @@ -55932,11 +56393,11 @@ | |
| 55932 | ** allocating a new page cache entry in order to avoid stressing |
| 55933 | ** the heap even further. |
| 55934 | */ |
| 55935 | static int pcache1UnderMemoryPressure(PCache1 *pCache){ |
| 55936 | if( pcache1.nSlot && (pCache->szPage+pCache->szExtra)<=pcache1.szSlot ){ |
| 55937 | return pcache1.bUnderPressure; |
| 55938 | }else{ |
| 55939 | return sqlite3HeapNearlyFull(); |
| 55940 | } |
| 55941 | } |
| 55942 | |
| @@ -59211,10 +59672,19 @@ | |
| 59211 | pPager->pInJournal = 0; |
| 59212 | releaseAllSavepoints(pPager); |
| 59213 | |
| 59214 | if( pagerUseWal(pPager) ){ |
| 59215 | assert( !isOpen(pPager->jfd) ); |
| 59216 | sqlite3WalEndReadTransaction(pPager->pWal); |
| 59217 | pPager->eState = PAGER_OPEN; |
| 59218 | }else if( !pPager->exclusiveMode ){ |
| 59219 | int rc; /* Error code returned by pagerUnlockDb() */ |
| 59220 | int iDc = isOpen(pPager->fd)?sqlite3OsDeviceCharacteristics(pPager->fd):0; |
| @@ -65669,10 +66139,15 @@ | |
| 65669 | ) |
| 65670 | |
| 65671 | /* |
| 65672 | ** An open write-ahead log file is represented by an instance of the |
| 65673 | ** following object. |
| 65674 | */ |
| 65675 | struct Wal { |
| 65676 | sqlite3_vfs *pVfs; /* The VFS used to create pDbFd */ |
| 65677 | sqlite3_file *pDbFd; /* File handle for the database file */ |
| 65678 | sqlite3_file *pWalFd; /* File handle for WAL file */ |
| @@ -65759,12 +66234,16 @@ | |
| 65759 | int iNext; /* Next slot in aIndex[] not yet returned */ |
| 65760 | ht_slot *aIndex; /* i0, i1, i2... such that aPgno[iN] ascend */ |
| 65761 | u32 *aPgno; /* Array of page numbers. */ |
| 65762 | int nEntry; /* Nr. of entries in aPgno[] and aIndex[] */ |
| 65763 | int iZero; /* Frame number associated with aPgno[0] */ |
| 65764 | } aSegment[1]; /* One for every 32KB page in the wal-index */ |
| 65765 | }; |
| 65766 | |
| 65767 | /* |
| 65768 | ** Define the parameters of the hash tables in the wal-index file. There |
| 65769 | ** is a hash-table following every HASHTABLE_NPAGE page numbers in the |
| 65770 | ** wal-index. |
| @@ -67122,12 +67601,11 @@ | |
| 67122 | assert( pWal->ckptLock && pWal->hdr.mxFrame>0 ); |
| 67123 | iLast = pWal->hdr.mxFrame; |
| 67124 | |
| 67125 | /* Allocate space for the WalIterator object. */ |
| 67126 | nSegment = walFramePage(iLast) + 1; |
| 67127 | nByte = sizeof(WalIterator) |
| 67128 | + (nSegment-1)*sizeof(struct WalSegment) |
| 67129 | + iLast*sizeof(ht_slot); |
| 67130 | p = (WalIterator *)sqlite3_malloc64(nByte |
| 67131 | + sizeof(ht_slot) * (iLast>HASHTABLE_NPAGE?HASHTABLE_NPAGE:iLast) |
| 67132 | ); |
| 67133 | if( !p ){ |
| @@ -67194,11 +67672,11 @@ | |
| 67194 | ** or 0 otherwise. |
| 67195 | */ |
| 67196 | static int walEnableBlocking(Wal *pWal){ |
| 67197 | int res = 0; |
| 67198 | if( pWal->db ){ |
| 67199 | int tmout = pWal->db->busyTimeout; |
| 67200 | if( tmout ){ |
| 67201 | res = walEnableBlockingMs(pWal, tmout); |
| 67202 | } |
| 67203 | } |
| 67204 | return res; |
| @@ -67580,11 +68058,13 @@ | |
| 67580 | static int walHandleException(Wal *pWal){ |
| 67581 | if( pWal->exclusiveMode==0 ){ |
| 67582 | static const int S = 1; |
| 67583 | static const int E = (1<<SQLITE_SHM_NLOCK); |
| 67584 | int ii; |
| 67585 | u32 mUnlock = pWal->lockMask & ~( |
| 67586 | (pWal->readLock<0 ? 0 : (S << WAL_READ_LOCK(pWal->readLock))) |
| 67587 | | (pWal->writeLock ? (E << WAL_WRITE_LOCK) : 0) |
| 67588 | | (pWal->ckptLock ? (E << WAL_CKPT_LOCK) : 0) |
| 67589 | ); |
| 67590 | for(ii=0; ii<SQLITE_SHM_NLOCK; ii++){ |
| @@ -67852,11 +68332,16 @@ | |
| 67852 | }else{ |
| 67853 | int bWriteLock = pWal->writeLock; |
| 67854 | if( bWriteLock |
| 67855 | || SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1)) |
| 67856 | ){ |
| 67857 | pWal->writeLock = 1; |
| 67858 | if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){ |
| 67859 | badHdr = walIndexTryHdr(pWal, pChanged); |
| 67860 | if( badHdr ){ |
| 67861 | /* If the wal-index header is still malformed even while holding |
| 67862 | ** a WRITE lock, it can only mean that the header is corrupted and |
| @@ -68637,12 +69122,15 @@ | |
| 68637 | /* |
| 68638 | ** Finish with a read transaction. All this does is release the |
| 68639 | ** read-lock. |
| 68640 | */ |
| 68641 | SQLITE_PRIVATE void sqlite3WalEndReadTransaction(Wal *pWal){ |
| 68642 | sqlite3WalEndWriteTransaction(pWal); |
| 68643 | if( pWal->readLock>=0 ){ |
| 68644 | walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock)); |
| 68645 | pWal->readLock = -1; |
| 68646 | } |
| 68647 | } |
| 68648 | |
| @@ -68831,11 +69319,11 @@ | |
| 68831 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 68832 | /* If the write-lock is already held, then it was obtained before the |
| 68833 | ** read-transaction was even opened, making this call a no-op. |
| 68834 | ** Return early. */ |
| 68835 | if( pWal->writeLock ){ |
| 68836 | assert( !memcmp(&pWal->hdr,(void *)walIndexHdr(pWal),sizeof(WalIndexHdr)) ); |
| 68837 | return SQLITE_OK; |
| 68838 | } |
| 68839 | #endif |
| 68840 | |
| 68841 | /* Cannot start a write transaction without first holding a read |
| @@ -70280,10 +70768,16 @@ | |
| 70280 | ** If a tree that appears to be taller than this is encountered, it is |
| 70281 | ** assumed that the database is corrupt. |
| 70282 | */ |
| 70283 | #define BTCURSOR_MAX_DEPTH 20 |
| 70284 | |
| 70285 | /* |
| 70286 | ** A cursor is a pointer to a particular entry within a particular |
| 70287 | ** b-tree within a database file. |
| 70288 | ** |
| 70289 | ** The entry is identified by its MemPage and the index in |
| @@ -70688,11 +71182,11 @@ | |
| 70688 | ** two or more btrees in common both try to lock all their btrees |
| 70689 | ** at the same instant. |
| 70690 | */ |
| 70691 | static void SQLITE_NOINLINE btreeEnterAll(sqlite3 *db){ |
| 70692 | int i; |
| 70693 | int skipOk = 1; |
| 70694 | Btree *p; |
| 70695 | assert( sqlite3_mutex_held(db->mutex) ); |
| 70696 | for(i=0; i<db->nDb; i++){ |
| 70697 | p = db->aDb[i].pBt; |
| 70698 | if( p && p->sharable ){ |
| @@ -71834,11 +72328,11 @@ | |
| 71834 | /* |
| 71835 | ** Provide flag hints to the cursor. |
| 71836 | */ |
| 71837 | SQLITE_PRIVATE void sqlite3BtreeCursorHintFlags(BtCursor *pCur, unsigned x){ |
| 71838 | assert( x==BTREE_SEEK_EQ || x==BTREE_BULKLOAD || x==0 ); |
| 71839 | pCur->hints = x; |
| 71840 | } |
| 71841 | |
| 71842 | |
| 71843 | #ifndef SQLITE_OMIT_AUTOVACUUM |
| 71844 | /* |
| @@ -72028,18 +72522,19 @@ | |
| 72028 | ** page pPage, return the number of bytes of payload stored locally. |
| 72029 | */ |
| 72030 | static int btreePayloadToLocal(MemPage *pPage, i64 nPayload){ |
| 72031 | int maxLocal; /* Maximum amount of payload held locally */ |
| 72032 | maxLocal = pPage->maxLocal; |
| 72033 | if( nPayload<=maxLocal ){ |
| 72034 | return nPayload; |
| 72035 | }else{ |
| 72036 | int minLocal; /* Minimum amount of payload held locally */ |
| 72037 | int surplus; /* Overflow payload available for local storage */ |
| 72038 | minLocal = pPage->minLocal; |
| 72039 | surplus = minLocal + (nPayload - minLocal)%(pPage->pBt->usableSize-4); |
| 72040 | return ( surplus <= maxLocal ) ? surplus : minLocal; |
| 72041 | } |
| 72042 | } |
| 72043 | |
| 72044 | /* |
| 72045 | ** The following routines are implementations of the MemPage.xParseCell() |
| @@ -72145,15 +72640,17 @@ | |
| 72145 | pInfo->nKey = *(i64*)&iKey; |
| 72146 | pInfo->nPayload = nPayload; |
| 72147 | pInfo->pPayload = pIter; |
| 72148 | testcase( nPayload==pPage->maxLocal ); |
| 72149 | testcase( nPayload==(u32)pPage->maxLocal+1 ); |
| 72150 | if( nPayload<=pPage->maxLocal ){ |
| 72151 | /* This is the (easy) common case where the entire payload fits |
| 72152 | ** on the local page. No overflow is required. |
| 72153 | */ |
| 72154 | pInfo->nSize = nPayload + (u16)(pIter - pCell); |
| 72155 | if( pInfo->nSize<4 ) pInfo->nSize = 4; |
| 72156 | pInfo->nLocal = (u16)nPayload; |
| 72157 | }else{ |
| 72158 | btreeParseCellAdjustSizeForOverflow(pPage, pCell, pInfo); |
| 72159 | } |
| @@ -72182,15 +72679,17 @@ | |
| 72182 | pInfo->nKey = nPayload; |
| 72183 | pInfo->nPayload = nPayload; |
| 72184 | pInfo->pPayload = pIter; |
| 72185 | testcase( nPayload==pPage->maxLocal ); |
| 72186 | testcase( nPayload==(u32)pPage->maxLocal+1 ); |
| 72187 | if( nPayload<=pPage->maxLocal ){ |
| 72188 | /* This is the (easy) common case where the entire payload fits |
| 72189 | ** on the local page. No overflow is required. |
| 72190 | */ |
| 72191 | pInfo->nSize = nPayload + (u16)(pIter - pCell); |
| 72192 | if( pInfo->nSize<4 ) pInfo->nSize = 4; |
| 72193 | pInfo->nLocal = (u16)nPayload; |
| 72194 | }else{ |
| 72195 | btreeParseCellAdjustSizeForOverflow(pPage, pCell, pInfo); |
| 72196 | } |
| @@ -72725,18 +73224,18 @@ | |
| 72725 | ** that routine will not detect overlap between cells or freeblocks. Nor |
| 72726 | ** does it detect cells or freeblocks that encroach into the reserved bytes |
| 72727 | ** at the end of the page. So do additional corruption checks inside this |
| 72728 | ** routine and return SQLITE_CORRUPT if any problems are found. |
| 72729 | */ |
| 72730 | static int freeSpace(MemPage *pPage, u16 iStart, u16 iSize){ |
| 72731 | u16 iPtr; /* Address of ptr to next freeblock */ |
| 72732 | u16 iFreeBlk; /* Address of the next freeblock */ |
| 72733 | u8 hdr; /* Page header size. 0 or 100 */ |
| 72734 | u8 nFrag = 0; /* Reduction in fragmentation */ |
| 72735 | u16 iOrigSize = iSize; /* Original value of iSize */ |
| 72736 | u16 x; /* Offset to cell content area */ |
| 72737 | u32 iEnd = iStart + iSize; /* First byte past the iStart buffer */ |
| 72738 | unsigned char *data = pPage->aData; /* Page content */ |
| 72739 | u8 *pTmp; /* Temporary ptr into data[] */ |
| 72740 | |
| 72741 | assert( pPage->pBt!=0 ); |
| 72742 | assert( sqlite3PagerIswriteable(pPage->pDbPage) ); |
| @@ -72759,11 +73258,11 @@ | |
| 72759 | if( iFreeBlk==0 ) break; /* TH3: corrupt082.100 */ |
| 72760 | return SQLITE_CORRUPT_PAGE(pPage); |
| 72761 | } |
| 72762 | iPtr = iFreeBlk; |
| 72763 | } |
| 72764 | if( iFreeBlk>pPage->pBt->usableSize-4 ){ /* TH3: corrupt081.100 */ |
| 72765 | return SQLITE_CORRUPT_PAGE(pPage); |
| 72766 | } |
| 72767 | assert( iFreeBlk>iPtr || iFreeBlk==0 || CORRUPT_DB ); |
| 72768 | |
| 72769 | /* At this point: |
| @@ -72774,11 +73273,11 @@ | |
| 72774 | */ |
| 72775 | if( iFreeBlk && iEnd+3>=iFreeBlk ){ |
| 72776 | nFrag = iFreeBlk - iEnd; |
| 72777 | if( iEnd>iFreeBlk ) return SQLITE_CORRUPT_PAGE(pPage); |
| 72778 | iEnd = iFreeBlk + get2byte(&data[iFreeBlk+2]); |
| 72779 | if( iEnd > pPage->pBt->usableSize ){ |
| 72780 | return SQLITE_CORRUPT_PAGE(pPage); |
| 72781 | } |
| 72782 | iSize = iEnd - iStart; |
| 72783 | iFreeBlk = get2byte(&data[iFreeBlk]); |
| 72784 | } |
| @@ -72795,11 +73294,11 @@ | |
| 72795 | iSize = iEnd - iPtr; |
| 72796 | iStart = iPtr; |
| 72797 | } |
| 72798 | } |
| 72799 | if( nFrag>data[hdr+7] ) return SQLITE_CORRUPT_PAGE(pPage); |
| 72800 | data[hdr+7] -= nFrag; |
| 72801 | } |
| 72802 | pTmp = &data[hdr+5]; |
| 72803 | x = get2byte(pTmp); |
| 72804 | if( pPage->pBt->btsFlags & BTS_FAST_SECURE ){ |
| 72805 | /* Overwrite deleted information with zeros when the secure_delete |
| @@ -72816,11 +73315,12 @@ | |
| 72816 | put2byte(&data[hdr+5], iEnd); |
| 72817 | }else{ |
| 72818 | /* Insert the new freeblock into the freelist */ |
| 72819 | put2byte(&data[iPtr], iStart); |
| 72820 | put2byte(&data[iStart], iFreeBlk); |
| 72821 | put2byte(&data[iStart+2], iSize); |
| 72822 | } |
| 72823 | pPage->nFree += iOrigSize; |
| 72824 | return SQLITE_OK; |
| 72825 | } |
| 72826 | |
| @@ -73042,11 +73542,11 @@ | |
| 73042 | return SQLITE_CORRUPT_PAGE(pPage); |
| 73043 | } |
| 73044 | assert( pBt->pageSize>=512 && pBt->pageSize<=65536 ); |
| 73045 | pPage->maskPage = (u16)(pBt->pageSize - 1); |
| 73046 | pPage->nOverflow = 0; |
| 73047 | pPage->cellOffset = pPage->hdrOffset + 8 + pPage->childPtrSize; |
| 73048 | pPage->aCellIdx = data + pPage->childPtrSize + 8; |
| 73049 | pPage->aDataEnd = pPage->aData + pBt->pageSize; |
| 73050 | pPage->aDataOfst = pPage->aData + pPage->childPtrSize; |
| 73051 | /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the |
| 73052 | ** number of cells on the page. */ |
| @@ -73076,12 +73576,12 @@ | |
| 73076 | ** no entries. |
| 73077 | */ |
| 73078 | static void zeroPage(MemPage *pPage, int flags){ |
| 73079 | unsigned char *data = pPage->aData; |
| 73080 | BtShared *pBt = pPage->pBt; |
| 73081 | u8 hdr = pPage->hdrOffset; |
| 73082 | u16 first; |
| 73083 | |
| 73084 | assert( sqlite3PagerPagenumber(pPage->pDbPage)==pPage->pgno || CORRUPT_DB ); |
| 73085 | assert( sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage ); |
| 73086 | assert( sqlite3PagerGetData(pPage->pDbPage) == data ); |
| 73087 | assert( sqlite3PagerIswriteable(pPage->pDbPage) ); |
| @@ -73094,11 +73594,11 @@ | |
| 73094 | memset(&data[hdr+1], 0, 4); |
| 73095 | data[hdr+7] = 0; |
| 73096 | put2byte(&data[hdr+5], pBt->usableSize); |
| 73097 | pPage->nFree = (u16)(pBt->usableSize - first); |
| 73098 | decodeFlags(pPage, flags); |
| 73099 | pPage->cellOffset = first; |
| 73100 | pPage->aDataEnd = &data[pBt->pageSize]; |
| 73101 | pPage->aCellIdx = &data[first]; |
| 73102 | pPage->aDataOfst = &data[pPage->childPtrSize]; |
| 73103 | pPage->nOverflow = 0; |
| 73104 | assert( pBt->pageSize>=512 && pBt->pageSize<=65536 ); |
| @@ -73880,11 +74380,11 @@ | |
| 73880 | int rc = SQLITE_OK; |
| 73881 | int x; |
| 73882 | BtShared *pBt = p->pBt; |
| 73883 | assert( nReserve>=0 && nReserve<=255 ); |
| 73884 | sqlite3BtreeEnter(p); |
| 73885 | pBt->nReserveWanted = nReserve; |
| 73886 | x = pBt->pageSize - pBt->usableSize; |
| 73887 | if( nReserve<x ) nReserve = x; |
| 73888 | if( pBt->btsFlags & BTS_PAGESIZE_FIXED ){ |
| 73889 | sqlite3BtreeLeave(p); |
| 73890 | return SQLITE_READONLY; |
| @@ -73986,11 +74486,11 @@ | |
| 73986 | sqlite3BtreeEnter(p); |
| 73987 | assert( BTS_OVERWRITE==BTS_SECURE_DELETE*2 ); |
| 73988 | assert( BTS_FAST_SECURE==(BTS_OVERWRITE|BTS_SECURE_DELETE) ); |
| 73989 | if( newFlag>=0 ){ |
| 73990 | p->pBt->btsFlags &= ~BTS_FAST_SECURE; |
| 73991 | p->pBt->btsFlags |= BTS_SECURE_DELETE*newFlag; |
| 73992 | } |
| 73993 | b = (p->pBt->btsFlags & BTS_FAST_SECURE)/BTS_SECURE_DELETE; |
| 73994 | sqlite3BtreeLeave(p); |
| 73995 | return b; |
| 73996 | } |
| @@ -78434,11 +78934,12 @@ | |
| 78434 | pSrcEnd = pCArray->apEnd[k]; |
| 78435 | } |
| 78436 | } |
| 78437 | |
| 78438 | /* The pPg->nFree field is now set incorrectly. The caller will fix it. */ |
| 78439 | pPg->nCell = nCell; |
| 78440 | pPg->nOverflow = 0; |
| 78441 | |
| 78442 | put2byte(&aData[hdr+1], 0); |
| 78443 | put2byte(&aData[hdr+3], pPg->nCell); |
| 78444 | put2byte(&aData[hdr+5], pData - aData); |
| @@ -78681,13 +79182,17 @@ | |
| 78681 | assert( nCell>=0 ); |
| 78682 | pCellptr = &pPg->aCellIdx[nCell*2]; |
| 78683 | if( pageInsertArray( |
| 78684 | pPg, pBegin, &pData, pCellptr, |
| 78685 | iNew+nCell, nNew-nCell, pCArray |
| 78686 | ) ) goto editpage_fail; |
| 78687 | |
| 78688 | pPg->nCell = nNew; |
| 78689 | pPg->nOverflow = 0; |
| 78690 | |
| 78691 | put2byte(&aData[hdr+3], pPg->nCell); |
| 78692 | put2byte(&aData[hdr+5], pData - aData); |
| 78693 | |
| @@ -78992,11 +79497,11 @@ | |
| 78992 | int leafData; /* True if pPage is a leaf of a LEAFDATA tree */ |
| 78993 | int usableSpace; /* Bytes in pPage beyond the header */ |
| 78994 | int pageFlags; /* Value of pPage->aData[0] */ |
| 78995 | int iSpace1 = 0; /* First unused byte of aSpace1[] */ |
| 78996 | int iOvflSpace = 0; /* First unused byte of aOvflSpace[] */ |
| 78997 | int szScratch; /* Size of scratch memory requested */ |
| 78998 | MemPage *apOld[NB]; /* pPage and up to two siblings */ |
| 78999 | MemPage *apNew[NB+2]; /* pPage and up to NB siblings after balancing */ |
| 79000 | u8 *pRight; /* Location in parent of right-sibling pointer */ |
| 79001 | u8 *apDiv[NB-1]; /* Divider cells in pParent */ |
| 79002 | int cntNew[NB+2]; /* Index in b.paCell[] of cell after i-th page */ |
| @@ -80277,11 +80782,11 @@ | |
| 80277 | if( loc==0 ){ |
| 80278 | getCellInfo(pCur); |
| 80279 | if( pCur->info.nKey==pX->nKey ){ |
| 80280 | BtreePayload x2; |
| 80281 | x2.pData = pX->pKey; |
| 80282 | x2.nData = pX->nKey; |
| 80283 | x2.nZero = 0; |
| 80284 | return btreeOverwriteCell(pCur, &x2); |
| 80285 | } |
| 80286 | } |
| 80287 | } |
| @@ -80458,11 +80963,11 @@ | |
| 80458 | u32 nIn; /* Size of input buffer aIn[] */ |
| 80459 | u32 nRem; /* Bytes of data still to copy */ |
| 80460 | |
| 80461 | getCellInfo(pSrc); |
| 80462 | if( pSrc->info.nPayload<0x80 ){ |
| 80463 | *(aOut++) = pSrc->info.nPayload; |
| 80464 | }else{ |
| 80465 | aOut += sqlite3PutVarint(aOut, pSrc->info.nPayload); |
| 80466 | } |
| 80467 | if( pDest->pKeyInfo==0 ) aOut += putVarint(aOut, iKey); |
| 80468 | nIn = pSrc->info.nLocal; |
| @@ -80471,11 +80976,11 @@ | |
| 80471 | return SQLITE_CORRUPT_PAGE(pSrc->pPage); |
| 80472 | } |
| 80473 | nRem = pSrc->info.nPayload; |
| 80474 | if( nIn==nRem && nIn<pDest->pPage->maxLocal ){ |
| 80475 | memcpy(aOut, aIn, nIn); |
| 80476 | pBt->nPreformatSize = nIn + (aOut - pBt->pTmpSpace); |
| 80477 | return SQLITE_OK; |
| 80478 | }else{ |
| 80479 | int rc = SQLITE_OK; |
| 80480 | Pager *pSrcPager = pSrc->pBt->pPager; |
| 80481 | u8 *pPgnoOut = 0; |
| @@ -80483,11 +80988,11 @@ | |
| 80483 | DbPage *pPageIn = 0; |
| 80484 | MemPage *pPageOut = 0; |
| 80485 | u32 nOut; /* Size of output buffer aOut[] */ |
| 80486 | |
| 80487 | nOut = btreePayloadToLocal(pDest->pPage, pSrc->info.nPayload); |
| 80488 | pBt->nPreformatSize = nOut + (aOut - pBt->pTmpSpace); |
| 80489 | if( nOut<pSrc->info.nPayload ){ |
| 80490 | pPgnoOut = &aOut[nOut]; |
| 80491 | pBt->nPreformatSize += 4; |
| 80492 | } |
| 80493 | |
| @@ -85584,16 +86089,14 @@ | |
| 85584 | int nArg, /* Number of argument */ |
| 85585 | const FuncDef *pFunc, /* The function to be invoked */ |
| 85586 | int eCallCtx /* Calling context */ |
| 85587 | ){ |
| 85588 | Vdbe *v = pParse->pVdbe; |
| 85589 | int nByte; |
| 85590 | int addr; |
| 85591 | sqlite3_context *pCtx; |
| 85592 | assert( v ); |
| 85593 | nByte = sizeof(*pCtx) + (nArg-1)*sizeof(sqlite3_value*); |
| 85594 | pCtx = sqlite3DbMallocRawNN(pParse->db, nByte); |
| 85595 | if( pCtx==0 ){ |
| 85596 | assert( pParse->db->mallocFailed ); |
| 85597 | freeEphemeralFunction(pParse->db, (FuncDef*)pFunc); |
| 85598 | return 0; |
| 85599 | } |
| @@ -90665,25 +91168,26 @@ | |
| 90665 | |
| 90666 | preupdate.v = v; |
| 90667 | preupdate.pCsr = pCsr; |
| 90668 | preupdate.op = op; |
| 90669 | preupdate.iNewReg = iReg; |
| 90670 | preupdate.keyinfo.db = db; |
| 90671 | preupdate.keyinfo.enc = ENC(db); |
| 90672 | preupdate.keyinfo.nKeyField = pTab->nCol; |
| 90673 | preupdate.keyinfo.aSortFlags = (u8*)&fakeSortOrder; |
| 90674 | preupdate.iKey1 = iKey1; |
| 90675 | preupdate.iKey2 = iKey2; |
| 90676 | preupdate.pTab = pTab; |
| 90677 | preupdate.iBlobWrite = iBlobWrite; |
| 90678 | |
| 90679 | db->pPreUpdate = &preupdate; |
| 90680 | db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2); |
| 90681 | db->pPreUpdate = 0; |
| 90682 | sqlite3DbFree(db, preupdate.aRecord); |
| 90683 | vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pUnpacked); |
| 90684 | vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pNewUnpacked); |
| 90685 | sqlite3VdbeMemRelease(&preupdate.oldipk); |
| 90686 | if( preupdate.aNew ){ |
| 90687 | int i; |
| 90688 | for(i=0; i<pCsr->nField; i++){ |
| 90689 | sqlite3VdbeMemRelease(&preupdate.aNew[i]); |
| @@ -92918,11 +93422,11 @@ | |
| 92918 | nRec = sqlite3BtreePayloadSize(p->pCsr->uc.pCursor); |
| 92919 | aRec = sqlite3DbMallocRaw(db, nRec); |
| 92920 | if( !aRec ) goto preupdate_old_out; |
| 92921 | rc = sqlite3BtreePayload(p->pCsr->uc.pCursor, 0, nRec, aRec); |
| 92922 | if( rc==SQLITE_OK ){ |
| 92923 | p->pUnpacked = vdbeUnpackRecord(&p->keyinfo, nRec, aRec); |
| 92924 | if( !p->pUnpacked ) rc = SQLITE_NOMEM; |
| 92925 | } |
| 92926 | if( rc!=SQLITE_OK ){ |
| 92927 | sqlite3DbFree(db, aRec); |
| 92928 | goto preupdate_old_out; |
| @@ -92983,11 +93487,11 @@ | |
| 92983 | #ifdef SQLITE_ENABLE_API_ARMOR |
| 92984 | p = db!=0 ? db->pPreUpdate : 0; |
| 92985 | #else |
| 92986 | p = db->pPreUpdate; |
| 92987 | #endif |
| 92988 | return (p ? p->keyinfo.nKeyField : 0); |
| 92989 | } |
| 92990 | #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ |
| 92991 | |
| 92992 | #ifdef SQLITE_ENABLE_PREUPDATE_HOOK |
| 92993 | /* |
| @@ -93066,11 +93570,11 @@ | |
| 93066 | UnpackedRecord *pUnpack = p->pNewUnpacked; |
| 93067 | if( !pUnpack ){ |
| 93068 | Mem *pData = &p->v->aMem[p->iNewReg]; |
| 93069 | rc = ExpandBlob(pData); |
| 93070 | if( rc!=SQLITE_OK ) goto preupdate_new_out; |
| 93071 | pUnpack = vdbeUnpackRecord(&p->keyinfo, pData->n, pData->z); |
| 93072 | if( !pUnpack ){ |
| 93073 | rc = SQLITE_NOMEM; |
| 93074 | goto preupdate_new_out; |
| 93075 | } |
| 93076 | p->pNewUnpacked = pUnpack; |
| @@ -93860,13 +94364,13 @@ | |
| 93860 | */ |
| 93861 | Mem *pMem = iCur>0 ? &p->aMem[p->nMem-iCur] : p->aMem; |
| 93862 | |
| 93863 | i64 nByte; |
| 93864 | VdbeCursor *pCx = 0; |
| 93865 | nByte = |
| 93866 | ROUND8P(sizeof(VdbeCursor)) + 2*sizeof(u32)*nField + |
| 93867 | (eCurType==CURTYPE_BTREE?sqlite3BtreeCursorSize():0); |
| 93868 | |
| 93869 | assert( iCur>=0 && iCur<p->nCursor ); |
| 93870 | if( p->apCsr[iCur] ){ /*OPTIMIZATION-IF-FALSE*/ |
| 93871 | sqlite3VdbeFreeCursorNN(p, p->apCsr[iCur]); |
| 93872 | p->apCsr[iCur] = 0; |
| @@ -93895,12 +94399,12 @@ | |
| 93895 | memset(pCx, 0, offsetof(VdbeCursor,pAltCursor)); |
| 93896 | pCx->eCurType = eCurType; |
| 93897 | pCx->nField = nField; |
| 93898 | pCx->aOffset = &pCx->aType[nField]; |
| 93899 | if( eCurType==CURTYPE_BTREE ){ |
| 93900 | pCx->uc.pCursor = (BtCursor*) |
| 93901 | &pMem->z[ROUND8P(sizeof(VdbeCursor))+2*sizeof(u32)*nField]; |
| 93902 | sqlite3BtreeCursorZero(pCx->uc.pCursor); |
| 93903 | } |
| 93904 | return pCx; |
| 93905 | } |
| 93906 | |
| @@ -99638,11 +100142,11 @@ | |
| 99638 | pCrsr = pC->uc.pCursor; |
| 99639 | |
| 99640 | /* The OP_RowData opcodes always follow OP_NotExists or |
| 99641 | ** OP_SeekRowid or OP_Rewind/Op_Next with no intervening instructions |
| 99642 | ** that might invalidate the cursor. |
| 99643 | ** If this where not the case, on of the following assert()s |
| 99644 | ** would fail. Should this ever change (because of changes in the code |
| 99645 | ** generator) then the fix would be to insert a call to |
| 99646 | ** sqlite3VdbeCursorMoveto(). |
| 99647 | */ |
| 99648 | assert( pC->deferredMoveto==0 ); |
| @@ -101287,11 +101791,11 @@ | |
| 101287 | ** cell in which to store the accumulation. Be careful that the memory |
| 101288 | ** cell is 8-byte aligned, even on platforms where a pointer is 32-bits. |
| 101289 | ** |
| 101290 | ** Note: We could avoid this by using a regular memory cell from aMem[] for |
| 101291 | ** the accumulator, instead of allocating one here. */ |
| 101292 | nAlloc = ROUND8P( sizeof(pCtx[0]) + (n-1)*sizeof(sqlite3_value*) ); |
| 101293 | pCtx = sqlite3DbMallocRawNN(db, nAlloc + sizeof(Mem)); |
| 101294 | if( pCtx==0 ) goto no_mem; |
| 101295 | pCtx->pOut = (Mem*)((u8*)pCtx + nAlloc); |
| 101296 | assert( EIGHT_BYTE_ALIGNMENT(pCtx->pOut) ); |
| 101297 | |
| @@ -102945,10 +103449,11 @@ | |
| 102945 | int iCol; /* Index of zColumn in row-record */ |
| 102946 | int rc = SQLITE_OK; |
| 102947 | char *zErr = 0; |
| 102948 | Table *pTab; |
| 102949 | Incrblob *pBlob = 0; |
| 102950 | Parse sParse; |
| 102951 | |
| 102952 | #ifdef SQLITE_ENABLE_API_ARMOR |
| 102953 | if( ppBlob==0 ){ |
| 102954 | return SQLITE_MISUSE_BKPT; |
| @@ -102990,11 +103495,14 @@ | |
| 102990 | if( pTab && IsView(pTab) ){ |
| 102991 | pTab = 0; |
| 102992 | sqlite3ErrorMsg(&sParse, "cannot open view: %s", zTable); |
| 102993 | } |
| 102994 | #endif |
| 102995 | if( !pTab ){ |
| 102996 | if( sParse.zErrMsg ){ |
| 102997 | sqlite3DbFree(db, zErr); |
| 102998 | zErr = sParse.zErrMsg; |
| 102999 | sParse.zErrMsg = 0; |
| 103000 | } |
| @@ -103001,11 +103509,11 @@ | |
| 103001 | rc = SQLITE_ERROR; |
| 103002 | sqlite3BtreeLeaveAll(db); |
| 103003 | goto blob_open_out; |
| 103004 | } |
| 103005 | pBlob->pTab = pTab; |
| 103006 | pBlob->zDb = db->aDb[sqlite3SchemaToIndex(db, pTab->pSchema)].zDbSName; |
| 103007 | |
| 103008 | /* Now search pTab for the exact column. */ |
| 103009 | iCol = sqlite3ColumnIndex(pTab, zColumn); |
| 103010 | if( iCol<0 ){ |
| 103011 | sqlite3DbFree(db, zErr); |
| @@ -103085,11 +103593,10 @@ | |
| 103085 | {OP_Column, 0, 0, 1}, /* 3 */ |
| 103086 | {OP_ResultRow, 1, 0, 0}, /* 4 */ |
| 103087 | {OP_Halt, 0, 0, 0}, /* 5 */ |
| 103088 | }; |
| 103089 | Vdbe *v = (Vdbe *)pBlob->pStmt; |
| 103090 | int iDb = sqlite3SchemaToIndex(db, pTab->pSchema); |
| 103091 | VdbeOp *aOp; |
| 103092 | |
| 103093 | sqlite3VdbeAddOp4Int(v, OP_Transaction, iDb, wrFlag, |
| 103094 | pTab->pSchema->schema_cookie, |
| 103095 | pTab->pSchema->iGeneration); |
| @@ -103663,12 +104170,15 @@ | |
| 103663 | u8 bUsePMA; /* True if one or more PMAs created */ |
| 103664 | u8 bUseThreads; /* True to use background threads */ |
| 103665 | u8 iPrev; /* Previous thread used to flush PMA */ |
| 103666 | u8 nTask; /* Size of aTask[] array */ |
| 103667 | u8 typeMask; |
| 103668 | SortSubtask aTask[1]; /* One or more subtasks */ |
| 103669 | }; |
| 103670 | |
| 103671 | #define SORTER_TYPE_INTEGER 0x01 |
| 103672 | #define SORTER_TYPE_TEXT 0x02 |
| 103673 | |
| 103674 | /* |
| @@ -104297,12 +104807,12 @@ | |
| 104297 | assert( pCsr->pKeyInfo ); |
| 104298 | assert( !pCsr->isEphemeral ); |
| 104299 | assert( pCsr->eCurType==CURTYPE_SORTER ); |
| 104300 | assert( sizeof(KeyInfo) + UMXV(pCsr->pKeyInfo->nKeyField)*sizeof(CollSeq*) |
| 104301 | < 0x7fffffff ); |
| 104302 | szKeyInfo = sizeof(KeyInfo) + (pCsr->pKeyInfo->nKeyField-1)*sizeof(CollSeq*); |
| 104303 | sz = sizeof(VdbeSorter) + nWorker * sizeof(SortSubtask); |
| 104304 | |
| 104305 | pSorter = (VdbeSorter*)sqlite3DbMallocZero(db, sz + szKeyInfo); |
| 104306 | pCsr->uc.pSorter = pSorter; |
| 104307 | if( pSorter==0 ){ |
| 104308 | rc = SQLITE_NOMEM_BKPT; |
| @@ -104762,10 +105272,14 @@ | |
| 104762 | } |
| 104763 | |
| 104764 | p->u.pNext = 0; |
| 104765 | for(i=0; aSlot[i]; i++){ |
| 104766 | p = vdbeSorterMerge(pTask, p, aSlot[i]); |
| 104767 | aSlot[i] = 0; |
| 104768 | } |
| 104769 | aSlot[i] = p; |
| 104770 | p = pNext; |
| 104771 | } |
| @@ -109536,32 +110050,34 @@ | |
| 109536 | Table *pTab, /* The table being referenced, or NULL */ |
| 109537 | int type, /* NC_IsCheck, NC_PartIdx, NC_IdxExpr, NC_GenCol, or 0 */ |
| 109538 | Expr *pExpr, /* Expression to resolve. May be NULL. */ |
| 109539 | ExprList *pList /* Expression list to resolve. May be NULL. */ |
| 109540 | ){ |
| 109541 | SrcList sSrc; /* Fake SrcList for pParse->pNewTable */ |
| 109542 | NameContext sNC; /* Name context for pParse->pNewTable */ |
| 109543 | int rc; |
| 109544 | |
| 109545 | assert( type==0 || pTab!=0 ); |
| 109546 | assert( type==NC_IsCheck || type==NC_PartIdx || type==NC_IdxExpr |
| 109547 | || type==NC_GenCol || pTab==0 ); |
| 109548 | memset(&sNC, 0, sizeof(sNC)); |
| 109549 | memset(&sSrc, 0, sizeof(sSrc)); |
| 109550 | if( pTab ){ |
| 109551 | sSrc.nSrc = 1; |
| 109552 | sSrc.a[0].zName = pTab->zName; |
| 109553 | sSrc.a[0].pSTab = pTab; |
| 109554 | sSrc.a[0].iCursor = -1; |
| 109555 | if( pTab->pSchema!=pParse->db->aDb[1].pSchema ){ |
| 109556 | /* Cause EP_FromDDL to be set on TK_FUNCTION nodes of non-TEMP |
| 109557 | ** schema elements */ |
| 109558 | type |= NC_FromDDL; |
| 109559 | } |
| 109560 | } |
| 109561 | sNC.pParse = pParse; |
| 109562 | sNC.pSrcList = &sSrc; |
| 109563 | sNC.ncFlags = type | NC_IsDDL; |
| 109564 | if( (rc = sqlite3ResolveExprNames(&sNC, pExpr))!=SQLITE_OK ) return rc; |
| 109565 | if( pList ) rc = sqlite3ResolveExprListNames(&sNC, pList); |
| 109566 | return rc; |
| 109567 | } |
| @@ -111306,11 +111822,11 @@ | |
| 111306 | */ |
| 111307 | #ifndef SQLITE_OMIT_CTE |
| 111308 | SQLITE_PRIVATE With *sqlite3WithDup(sqlite3 *db, With *p){ |
| 111309 | With *pRet = 0; |
| 111310 | if( p ){ |
| 111311 | sqlite3_int64 nByte = sizeof(*p) + sizeof(p->a[0]) * (p->nCte-1); |
| 111312 | pRet = sqlite3DbMallocZero(db, nByte); |
| 111313 | if( pRet ){ |
| 111314 | int i; |
| 111315 | pRet->nCte = p->nCte; |
| 111316 | for(i=0; i<p->nCte; i++){ |
| @@ -111433,15 +111949,13 @@ | |
| 111433 | #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) \ |
| 111434 | || !defined(SQLITE_OMIT_SUBQUERY) |
| 111435 | SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3 *db, const SrcList *p, int flags){ |
| 111436 | SrcList *pNew; |
| 111437 | int i; |
| 111438 | int nByte; |
| 111439 | assert( db!=0 ); |
| 111440 | if( p==0 ) return 0; |
| 111441 | nByte = sizeof(*p) + (p->nSrc>0 ? sizeof(p->a[0]) * (p->nSrc-1) : 0); |
| 111442 | pNew = sqlite3DbMallocRawNN(db, nByte ); |
| 111443 | if( pNew==0 ) return 0; |
| 111444 | pNew->nSrc = pNew->nAlloc = p->nSrc; |
| 111445 | for(i=0; i<p->nSrc; i++){ |
| 111446 | SrcItem *pNewItem = &pNew->a[i]; |
| 111447 | const SrcItem *pOldItem = &p->a[i]; |
| @@ -111499,11 +112013,11 @@ | |
| 111499 | SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3 *db, const IdList *p){ |
| 111500 | IdList *pNew; |
| 111501 | int i; |
| 111502 | assert( db!=0 ); |
| 111503 | if( p==0 ) return 0; |
| 111504 | pNew = sqlite3DbMallocRawNN(db, sizeof(*pNew)+(p->nId-1)*sizeof(p->a[0]) ); |
| 111505 | if( pNew==0 ) return 0; |
| 111506 | pNew->nId = p->nId; |
| 111507 | for(i=0; i<p->nId; i++){ |
| 111508 | struct IdList_item *pNewItem = &pNew->a[i]; |
| 111509 | const struct IdList_item *pOldItem = &p->a[i]; |
| @@ -111531,11 +112045,11 @@ | |
| 111531 | pNew->pNext = pNext; |
| 111532 | pNew->pPrior = 0; |
| 111533 | pNew->pLimit = sqlite3ExprDup(db, p->pLimit, flags); |
| 111534 | pNew->iLimit = 0; |
| 111535 | pNew->iOffset = 0; |
| 111536 | pNew->selFlags = p->selFlags & ~SF_UsesEphemeral; |
| 111537 | pNew->addrOpenEphm[0] = -1; |
| 111538 | pNew->addrOpenEphm[1] = -1; |
| 111539 | pNew->nSelectRow = p->nSelectRow; |
| 111540 | pNew->pWith = sqlite3WithDup(db, p->pWith); |
| 111541 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| @@ -111583,11 +112097,11 @@ | |
| 111583 | Expr *pExpr /* Expression to be appended. Might be NULL */ |
| 111584 | ){ |
| 111585 | struct ExprList_item *pItem; |
| 111586 | ExprList *pList; |
| 111587 | |
| 111588 | pList = sqlite3DbMallocRawNN(db, sizeof(ExprList)+sizeof(pList->a[0])*4 ); |
| 111589 | if( pList==0 ){ |
| 111590 | sqlite3ExprDelete(db, pExpr); |
| 111591 | return 0; |
| 111592 | } |
| 111593 | pList->nAlloc = 4; |
| @@ -111603,12 +112117,11 @@ | |
| 111603 | Expr *pExpr /* Expression to be appended. Might be NULL */ |
| 111604 | ){ |
| 111605 | struct ExprList_item *pItem; |
| 111606 | ExprList *pNew; |
| 111607 | pList->nAlloc *= 2; |
| 111608 | pNew = sqlite3DbRealloc(db, pList, |
| 111609 | sizeof(*pList)+(pList->nAlloc-1)*sizeof(pList->a[0])); |
| 111610 | if( pNew==0 ){ |
| 111611 | sqlite3ExprListDelete(db, pList); |
| 111612 | sqlite3ExprDelete(db, pExpr); |
| 111613 | return 0; |
| 111614 | }else{ |
| @@ -114240,11 +114753,11 @@ | |
| 114240 | return -1; /* Not found */ |
| 114241 | } |
| 114242 | |
| 114243 | |
| 114244 | /* |
| 114245 | ** Expresion pExpr is guaranteed to be a TK_COLUMN or equivalent. This |
| 114246 | ** function checks the Parse.pIdxPartExpr list to see if this column |
| 114247 | ** can be replaced with a constant value. If so, it generates code to |
| 114248 | ** put the constant value in a register (ideally, but not necessarily, |
| 114249 | ** register iTarget) and returns the register number. |
| 114250 | ** |
| @@ -117481,17 +117994,17 @@ | |
| 117481 | pNew->nTabRef = 1; |
| 117482 | pNew->nCol = pTab->nCol; |
| 117483 | assert( pNew->nCol>0 ); |
| 117484 | nAlloc = (((pNew->nCol-1)/8)*8)+8; |
| 117485 | assert( nAlloc>=pNew->nCol && nAlloc%8==0 && nAlloc-pNew->nCol<8 ); |
| 117486 | pNew->aCol = (Column*)sqlite3DbMallocZero(db, sizeof(Column)*nAlloc); |
| 117487 | pNew->zName = sqlite3MPrintf(db, "sqlite_altertab_%s", pTab->zName); |
| 117488 | if( !pNew->aCol || !pNew->zName ){ |
| 117489 | assert( db->mallocFailed ); |
| 117490 | goto exit_begin_add_column; |
| 117491 | } |
| 117492 | memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*pNew->nCol); |
| 117493 | for(i=0; i<pNew->nCol; i++){ |
| 117494 | Column *pCol = &pNew->aCol[i]; |
| 117495 | pCol->zCnName = sqlite3DbStrDup(db, pCol->zCnName); |
| 117496 | pCol->hName = sqlite3StrIHash(pCol->zCnName); |
| 117497 | } |
| @@ -118086,23 +118599,34 @@ | |
| 118086 | sqlite3 *db, /* Database handle */ |
| 118087 | const char *zSql, /* SQL to parse */ |
| 118088 | int bTemp /* True if SQL is from temp schema */ |
| 118089 | ){ |
| 118090 | int rc; |
| 118091 | |
| 118092 | sqlite3ParseObjectInit(p, db); |
| 118093 | if( zSql==0 ){ |
| 118094 | return SQLITE_NOMEM; |
| 118095 | } |
| 118096 | if( sqlite3StrNICmp(zSql,"CREATE ",7)!=0 ){ |
| 118097 | return SQLITE_CORRUPT_BKPT; |
| 118098 | } |
| 118099 | db->init.iDb = bTemp ? 1 : sqlite3FindDbName(db, zDb); |
| 118100 | p->eParseMode = PARSE_MODE_RENAME; |
| 118101 | p->db = db; |
| 118102 | p->nQueryLoop = 1; |
| 118103 | rc = sqlite3RunParser(p, zSql); |
| 118104 | if( db->mallocFailed ) rc = SQLITE_NOMEM; |
| 118105 | if( rc==SQLITE_OK |
| 118106 | && NEVER(p->pNewTable==0 && p->pNewIndex==0 && p->pNewTrigger==0) |
| 118107 | ){ |
| 118108 | rc = SQLITE_CORRUPT_BKPT; |
| @@ -118161,14 +118685,15 @@ | |
| 118161 | return SQLITE_NOMEM; |
| 118162 | }else{ |
| 118163 | nQuot = sqlite3Strlen30(zQuot)-1; |
| 118164 | } |
| 118165 | |
| 118166 | assert( nQuot>=nNew ); |
| 118167 | zOut = sqlite3DbMallocZero(db, nSql + pRename->nList*nQuot + 1); |
| 118168 | }else{ |
| 118169 | zOut = (char*)sqlite3DbMallocZero(db, (nSql*2+1) * 3); |
| 118170 | if( zOut ){ |
| 118171 | zBuf1 = &zOut[nSql*2+1]; |
| 118172 | zBuf2 = &zOut[nSql*4+2]; |
| 118173 | } |
| 118174 | } |
| @@ -118176,20 +118701,21 @@ | |
| 118176 | /* At this point pRename->pList contains a list of RenameToken objects |
| 118177 | ** corresponding to all tokens in the input SQL that must be replaced |
| 118178 | ** with the new column name, or with single-quoted versions of themselves. |
| 118179 | ** All that remains is to construct and return the edited SQL string. */ |
| 118180 | if( zOut ){ |
| 118181 | int nOut = nSql; |
| 118182 | memcpy(zOut, zSql, nSql); |
| 118183 | while( pRename->pList ){ |
| 118184 | int iOff; /* Offset of token to replace in zOut */ |
| 118185 | u32 nReplace; |
| 118186 | const char *zReplace; |
| 118187 | RenameToken *pBest = renameColumnTokenNext(pRename); |
| 118188 | |
| 118189 | if( zNew ){ |
| 118190 | if( bQuote==0 && sqlite3IsIdChar(*pBest->t.z) ){ |
| 118191 | nReplace = nNew; |
| 118192 | zReplace = zNew; |
| 118193 | }else{ |
| 118194 | nReplace = nQuot; |
| 118195 | zReplace = zQuot; |
| @@ -118203,18 +118729,19 @@ | |
| 118203 | ** token. This is so that (SELECT "string"'alias') maps to |
| 118204 | ** (SELECT 'string' 'alias'), and not (SELECT 'string''alias'). */ |
| 118205 | memcpy(zBuf1, pBest->t.z, pBest->t.n); |
| 118206 | zBuf1[pBest->t.n] = 0; |
| 118207 | sqlite3Dequote(zBuf1); |
| 118208 | sqlite3_snprintf(nSql*2, zBuf2, "%Q%s", zBuf1, |
| 118209 | pBest->t.z[pBest->t.n]=='\'' ? " " : "" |
| 118210 | ); |
| 118211 | zReplace = zBuf2; |
| 118212 | nReplace = sqlite3Strlen30(zReplace); |
| 118213 | } |
| 118214 | |
| 118215 | iOff = pBest->t.z - zSql; |
| 118216 | if( pBest->t.n!=nReplace ){ |
| 118217 | memmove(&zOut[iOff + nReplace], &zOut[iOff + pBest->t.n], |
| 118218 | nOut - (iOff + pBest->t.n) |
| 118219 | ); |
| 118220 | nOut += nReplace - pBest->t.n; |
| @@ -118236,15 +118763,16 @@ | |
| 118236 | |
| 118237 | /* |
| 118238 | ** Set all pEList->a[].fg.eEName fields in the expression-list to val. |
| 118239 | */ |
| 118240 | static void renameSetENames(ExprList *pEList, int val){ |
| 118241 | if( pEList ){ |
| 118242 | int i; |
| 118243 | for(i=0; i<pEList->nExpr; i++){ |
| 118244 | assert( val==ENAME_NAME || pEList->a[i].fg.eEName==ENAME_NAME ); |
| 118245 | pEList->a[i].fg.eEName = val; |
| 118246 | } |
| 118247 | } |
| 118248 | } |
| 118249 | |
| 118250 | /* |
| @@ -118497,11 +119025,11 @@ | |
| 118497 | sCtx.pTab = pTab; |
| 118498 | if( rc!=SQLITE_OK ) goto renameColumnFunc_done; |
| 118499 | if( sParse.pNewTable ){ |
| 118500 | if( IsView(sParse.pNewTable) ){ |
| 118501 | Select *pSelect = sParse.pNewTable->u.view.pSelect; |
| 118502 | pSelect->selFlags &= ~SF_View; |
| 118503 | sParse.rc = SQLITE_OK; |
| 118504 | sqlite3SelectPrep(&sParse, pSelect, 0); |
| 118505 | rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc); |
| 118506 | if( rc==SQLITE_OK ){ |
| 118507 | sqlite3WalkSelect(&sWalker, pSelect); |
| @@ -118715,11 +119243,11 @@ | |
| 118715 | NameContext sNC; |
| 118716 | memset(&sNC, 0, sizeof(sNC)); |
| 118717 | sNC.pParse = &sParse; |
| 118718 | |
| 118719 | assert( pSelect->selFlags & SF_View ); |
| 118720 | pSelect->selFlags &= ~SF_View; |
| 118721 | sqlite3SelectPrep(&sParse, pTab->u.view.pSelect, &sNC); |
| 118722 | if( sParse.nErr ){ |
| 118723 | rc = sParse.rc; |
| 118724 | }else{ |
| 118725 | sqlite3WalkSelect(&sWalker, pTab->u.view.pSelect); |
| @@ -118888,11 +119416,11 @@ | |
| 118888 | sWalker.u.pRename = &sCtx; |
| 118889 | |
| 118890 | if( sParse.pNewTable ){ |
| 118891 | if( IsView(sParse.pNewTable) ){ |
| 118892 | Select *pSelect = sParse.pNewTable->u.view.pSelect; |
| 118893 | pSelect->selFlags &= ~SF_View; |
| 118894 | sParse.rc = SQLITE_OK; |
| 118895 | sqlite3SelectPrep(&sParse, pSelect, 0); |
| 118896 | rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc); |
| 118897 | if( rc==SQLITE_OK ){ |
| 118898 | sqlite3WalkSelect(&sWalker, pSelect); |
| @@ -118987,14 +119515,14 @@ | |
| 118987 | UNUSED_PARAMETER(NotUsed); |
| 118988 | |
| 118989 | if( zDb && zInput ){ |
| 118990 | int rc; |
| 118991 | Parse sParse; |
| 118992 | int flags = db->flags; |
| 118993 | if( bNoDQS ) db->flags &= ~(SQLITE_DqsDML|SQLITE_DqsDDL); |
| 118994 | rc = renameParseSql(&sParse, zDb, db, zInput, bTemp); |
| 118995 | db->flags |= (flags & (SQLITE_DqsDML|SQLITE_DqsDDL)); |
| 118996 | if( rc==SQLITE_OK ){ |
| 118997 | if( isLegacy==0 && sParse.pNewTable && IsView(sParse.pNewTable) ){ |
| 118998 | NameContext sNC; |
| 118999 | memset(&sNC, 0, sizeof(sNC)); |
| 119000 | sNC.pParse = &sParse; |
| @@ -119710,11 +120238,11 @@ | |
| 119710 | } |
| 119711 | |
| 119712 | p->db = db; |
| 119713 | p->nEst = sqlite3_value_int64(argv[2]); |
| 119714 | p->nRow = 0; |
| 119715 | p->nLimit = sqlite3_value_int64(argv[3]); |
| 119716 | p->nCol = nCol; |
| 119717 | p->nKeyCol = nKeyCol; |
| 119718 | p->nSkipAhead = 0; |
| 119719 | p->current.anDLt = (tRowcnt*)&p[1]; |
| 119720 | |
| @@ -121519,10 +122047,17 @@ | |
| 121519 | */ |
| 121520 | if( rc==SQLITE_OK ){ |
| 121521 | sqlite3BtreeEnterAll(db); |
| 121522 | db->init.iDb = 0; |
| 121523 | db->mDbFlags &= ~(DBFLAG_SchemaKnownOk); |
| 121524 | if( !REOPEN_AS_MEMDB(db) ){ |
| 121525 | rc = sqlite3Init(db, &zErrDyn); |
| 121526 | } |
| 121527 | sqlite3BtreeLeaveAll(db); |
| 121528 | assert( zErrDyn==0 || rc!=SQLITE_OK ); |
| @@ -123240,14 +123775,20 @@ | |
| 123240 | ** Convert an table column number into a index column number. That is, |
| 123241 | ** for the column iCol in the table (as defined by the CREATE TABLE statement) |
| 123242 | ** find the (first) offset of that column in index pIdx. Or return -1 |
| 123243 | ** if column iCol is not used in index pIdx. |
| 123244 | */ |
| 123245 | SQLITE_PRIVATE i16 sqlite3TableColumnToIndex(Index *pIdx, i16 iCol){ |
| 123246 | int i; |
| 123247 | for(i=0; i<pIdx->nColumn; i++){ |
| 123248 | if( iCol==pIdx->aiColumn[i] ) return i; |
| 123249 | } |
| 123250 | return -1; |
| 123251 | } |
| 123252 | |
| 123253 | #ifndef SQLITE_OMIT_GENERATED_COLUMNS |
| @@ -124340,16 +124881,21 @@ | |
| 124340 | |
| 124341 | /* |
| 124342 | ** Resize an Index object to hold N columns total. Return SQLITE_OK |
| 124343 | ** on success and SQLITE_NOMEM on an OOM error. |
| 124344 | */ |
| 124345 | static int resizeIndexObject(sqlite3 *db, Index *pIdx, int N){ |
| 124346 | char *zExtra; |
| 124347 | int nByte; |
| 124348 | if( pIdx->nColumn>=N ) return SQLITE_OK; |
| 124349 | assert( pIdx->isResized==0 ); |
| 124350 | nByte = (sizeof(char*) + sizeof(LogEst) + sizeof(i16) + 1)*N; |
| 124351 | zExtra = sqlite3DbMallocZero(db, nByte); |
| 124352 | if( zExtra==0 ) return SQLITE_NOMEM_BKPT; |
| 124353 | memcpy(zExtra, pIdx->azColl, sizeof(char*)*pIdx->nColumn); |
| 124354 | pIdx->azColl = (const char**)zExtra; |
| 124355 | zExtra += sizeof(char*)*N; |
| @@ -124359,11 +124905,11 @@ | |
| 124359 | memcpy(zExtra, pIdx->aiColumn, sizeof(i16)*pIdx->nColumn); |
| 124360 | pIdx->aiColumn = (i16*)zExtra; |
| 124361 | zExtra += sizeof(i16)*N; |
| 124362 | memcpy(zExtra, pIdx->aSortOrder, pIdx->nColumn); |
| 124363 | pIdx->aSortOrder = (u8*)zExtra; |
| 124364 | pIdx->nColumn = N; |
| 124365 | pIdx->isResized = 1; |
| 124366 | return SQLITE_OK; |
| 124367 | } |
| 124368 | |
| 124369 | /* |
| @@ -124613,11 +125159,11 @@ | |
| 124613 | if( n==0 ){ |
| 124614 | /* This index is a superset of the primary key */ |
| 124615 | pIdx->nColumn = pIdx->nKeyCol; |
| 124616 | continue; |
| 124617 | } |
| 124618 | if( resizeIndexObject(db, pIdx, pIdx->nKeyCol+n) ) return; |
| 124619 | for(i=0, j=pIdx->nKeyCol; i<nPk; i++){ |
| 124620 | if( !isDupColumn(pIdx, pIdx->nKeyCol, pPk, i) ){ |
| 124621 | testcase( hasColumn(pIdx->aiColumn, pIdx->nKeyCol, pPk->aiColumn[i]) ); |
| 124622 | pIdx->aiColumn[j] = pPk->aiColumn[i]; |
| 124623 | pIdx->azColl[j] = pPk->azColl[i]; |
| @@ -124637,11 +125183,11 @@ | |
| 124637 | nExtra = 0; |
| 124638 | for(i=0; i<pTab->nCol; i++){ |
| 124639 | if( !hasColumn(pPk->aiColumn, nPk, i) |
| 124640 | && (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 ) nExtra++; |
| 124641 | } |
| 124642 | if( resizeIndexObject(db, pPk, nPk+nExtra) ) return; |
| 124643 | for(i=0, j=nPk; i<pTab->nCol; i++){ |
| 124644 | if( !hasColumn(pPk->aiColumn, j, i) |
| 124645 | && (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 |
| 124646 | ){ |
| 124647 | assert( j<pPk->nColumn ); |
| @@ -125795,11 +126341,11 @@ | |
| 125795 | "columns in the referenced table"); |
| 125796 | goto fk_end; |
| 125797 | }else{ |
| 125798 | nCol = pFromCol->nExpr; |
| 125799 | } |
| 125800 | nByte = sizeof(*pFKey) + (nCol-1)*sizeof(pFKey->aCol[0]) + pTo->n + 1; |
| 125801 | if( pToCol ){ |
| 125802 | for(i=0; i<pToCol->nExpr; i++){ |
| 125803 | nByte += sqlite3Strlen30(pToCol->a[i].zEName) + 1; |
| 125804 | } |
| 125805 | } |
| @@ -126021,17 +126567,18 @@ | |
| 126021 | ** of 8-byte aligned space after the Index object and return a |
| 126022 | ** pointer to this extra space in *ppExtra. |
| 126023 | */ |
| 126024 | SQLITE_PRIVATE Index *sqlite3AllocateIndexObject( |
| 126025 | sqlite3 *db, /* Database connection */ |
| 126026 | i16 nCol, /* Total number of columns in the index */ |
| 126027 | int nExtra, /* Number of bytes of extra space to alloc */ |
| 126028 | char **ppExtra /* Pointer to the "extra" space */ |
| 126029 | ){ |
| 126030 | Index *p; /* Allocated index object */ |
| 126031 | i64 nByte; /* Bytes of space for Index object + arrays */ |
| 126032 | |
| 126033 | nByte = ROUND8(sizeof(Index)) + /* Index structure */ |
| 126034 | ROUND8(sizeof(char*)*nCol) + /* Index.azColl */ |
| 126035 | ROUND8(sizeof(LogEst)*(nCol+1) + /* Index.aiRowLogEst */ |
| 126036 | sizeof(i16)*nCol + /* Index.aiColumn */ |
| 126037 | sizeof(u8)*nCol); /* Index.aSortOrder */ |
| @@ -126040,12 +126587,13 @@ | |
| 126040 | char *pExtra = ((char*)p)+ROUND8(sizeof(Index)); |
| 126041 | p->azColl = (const char**)pExtra; pExtra += ROUND8(sizeof(char*)*nCol); |
| 126042 | p->aiRowLogEst = (LogEst*)pExtra; pExtra += sizeof(LogEst)*(nCol+1); |
| 126043 | p->aiColumn = (i16*)pExtra; pExtra += sizeof(i16)*nCol; |
| 126044 | p->aSortOrder = (u8*)pExtra; |
| 126045 | p->nColumn = nCol; |
| 126046 | p->nKeyCol = nCol - 1; |
| 126047 | *ppExtra = ((char*)p) + nByte; |
| 126048 | } |
| 126049 | return p; |
| 126050 | } |
| 126051 | |
| @@ -126852,16 +127400,15 @@ | |
| 126852 | */ |
| 126853 | SQLITE_PRIVATE IdList *sqlite3IdListAppend(Parse *pParse, IdList *pList, Token *pToken){ |
| 126854 | sqlite3 *db = pParse->db; |
| 126855 | int i; |
| 126856 | if( pList==0 ){ |
| 126857 | pList = sqlite3DbMallocZero(db, sizeof(IdList) ); |
| 126858 | if( pList==0 ) return 0; |
| 126859 | }else{ |
| 126860 | IdList *pNew; |
| 126861 | pNew = sqlite3DbRealloc(db, pList, |
| 126862 | sizeof(IdList) + pList->nId*sizeof(pList->a)); |
| 126863 | if( pNew==0 ){ |
| 126864 | sqlite3IdListDelete(db, pList); |
| 126865 | return 0; |
| 126866 | } |
| 126867 | pList = pNew; |
| @@ -126956,12 +127503,11 @@ | |
| 126956 | sqlite3ErrorMsg(pParse, "too many FROM clause terms, max: %d", |
| 126957 | SQLITE_MAX_SRCLIST); |
| 126958 | return 0; |
| 126959 | } |
| 126960 | if( nAlloc>SQLITE_MAX_SRCLIST ) nAlloc = SQLITE_MAX_SRCLIST; |
| 126961 | pNew = sqlite3DbRealloc(db, pSrc, |
| 126962 | sizeof(*pSrc) + (nAlloc-1)*sizeof(pSrc->a[0]) ); |
| 126963 | if( pNew==0 ){ |
| 126964 | assert( db->mallocFailed ); |
| 126965 | return 0; |
| 126966 | } |
| 126967 | pSrc = pNew; |
| @@ -127032,11 +127578,11 @@ | |
| 127032 | assert( pDatabase==0 || pTable!=0 ); /* Cannot have C without B */ |
| 127033 | assert( pParse!=0 ); |
| 127034 | assert( pParse->db!=0 ); |
| 127035 | db = pParse->db; |
| 127036 | if( pList==0 ){ |
| 127037 | pList = sqlite3DbMallocRawNN(pParse->db, sizeof(SrcList) ); |
| 127038 | if( pList==0 ) return 0; |
| 127039 | pList->nAlloc = 1; |
| 127040 | pList->nSrc = 1; |
| 127041 | memset(&pList->a[0], 0, sizeof(pList->a[0])); |
| 127042 | pList->a[0].iCursor = -1; |
| @@ -127918,14 +128464,13 @@ | |
| 127918 | } |
| 127919 | } |
| 127920 | } |
| 127921 | |
| 127922 | if( pWith ){ |
| 127923 | sqlite3_int64 nByte = sizeof(*pWith) + (sizeof(pWith->a[1]) * pWith->nCte); |
| 127924 | pNew = sqlite3DbRealloc(db, pWith, nByte); |
| 127925 | }else{ |
| 127926 | pNew = sqlite3DbMallocZero(db, sizeof(*pWith)); |
| 127927 | } |
| 127928 | assert( (pNew!=0 && zName!=0) || db->mallocFailed ); |
| 127929 | |
| 127930 | if( db->mallocFailed ){ |
| 127931 | sqlite3CteDelete(db, pCte); |
| @@ -130629,11 +131174,11 @@ | |
| 130629 | |
| 130630 | /* |
| 130631 | ** Append to pStr text that is the SQL literal representation of the |
| 130632 | ** value contained in pValue. |
| 130633 | */ |
| 130634 | SQLITE_PRIVATE void sqlite3QuoteValue(StrAccum *pStr, sqlite3_value *pValue){ |
| 130635 | /* As currently implemented, the string must be initially empty. |
| 130636 | ** we might relax this requirement in the future, but that will |
| 130637 | ** require enhancements to the implementation. */ |
| 130638 | assert( pStr!=0 && pStr->nChar==0 ); |
| 130639 | |
| @@ -130677,20 +131222,119 @@ | |
| 130677 | } |
| 130678 | break; |
| 130679 | } |
| 130680 | case SQLITE_TEXT: { |
| 130681 | const unsigned char *zArg = sqlite3_value_text(pValue); |
| 130682 | sqlite3_str_appendf(pStr, "%Q", zArg); |
| 130683 | break; |
| 130684 | } |
| 130685 | default: { |
| 130686 | assert( sqlite3_value_type(pValue)==SQLITE_NULL ); |
| 130687 | sqlite3_str_append(pStr, "NULL", 4); |
| 130688 | break; |
| 130689 | } |
| 130690 | } |
| 130691 | } |
| 130692 | |
| 130693 | /* |
| 130694 | ** Implementation of the QUOTE() function. |
| 130695 | ** |
| 130696 | ** The quote(X) function returns the text of an SQL literal which is the |
| @@ -130697,18 +131341,22 @@ | |
| 130697 | ** value of its argument suitable for inclusion into an SQL statement. |
| 130698 | ** Strings are surrounded by single-quotes with escapes on interior quotes |
| 130699 | ** as needed. BLOBs are encoded as hexadecimal literals. Strings with |
| 130700 | ** embedded NUL characters cannot be represented as string literals in SQL |
| 130701 | ** and hence the returned string literal is truncated prior to the first NUL. |
| 130702 | */ |
| 130703 | static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ |
| 130704 | sqlite3_str str; |
| 130705 | sqlite3 *db = sqlite3_context_db_handle(context); |
| 130706 | assert( argc==1 ); |
| 130707 | UNUSED_PARAMETER(argc); |
| 130708 | sqlite3StrAccumInit(&str, db, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]); |
| 130709 | sqlite3QuoteValue(&str,argv[0]); |
| 130710 | sqlite3_result_text(context, sqlite3StrAccumFinish(&str), str.nChar, |
| 130711 | SQLITE_DYNAMIC); |
| 130712 | if( str.accError!=SQLITE_OK ){ |
| 130713 | sqlite3_result_null(context); |
| 130714 | sqlite3_result_error_code(context, str.accError); |
| @@ -131355,11 +132003,11 @@ | |
| 131355 | ** |
| 131356 | ** The SUM() function follows the (broken) SQL standard which means |
| 131357 | ** that it returns NULL if it sums over no inputs. TOTAL returns |
| 131358 | ** 0.0 in that case. In addition, TOTAL always returns a float where |
| 131359 | ** SUM might return an integer if it never encounters a floating point |
| 131360 | ** value. TOTAL never fails, but SUM might through an exception if |
| 131361 | ** it overflows an integer. |
| 131362 | */ |
| 131363 | static void sumStep(sqlite3_context *context, int argc, sqlite3_value **argv){ |
| 131364 | SumCtx *p; |
| 131365 | int type; |
| @@ -132275,11 +132923,13 @@ | |
| 132275 | VFUNCTION(randomblob, 1, 0, 0, randomBlob ), |
| 132276 | FUNCTION(nullif, 2, 0, 1, nullifFunc ), |
| 132277 | DFUNCTION(sqlite_version, 0, 0, 0, versionFunc ), |
| 132278 | DFUNCTION(sqlite_source_id, 0, 0, 0, sourceidFunc ), |
| 132279 | FUNCTION(sqlite_log, 2, 0, 0, errlogFunc ), |
| 132280 | FUNCTION(quote, 1, 0, 0, quoteFunc ), |
| 132281 | VFUNCTION(last_insert_rowid, 0, 0, 0, last_insert_rowid), |
| 132282 | VFUNCTION(changes, 0, 0, 0, changes ), |
| 132283 | VFUNCTION(total_changes, 0, 0, 0, total_changes ), |
| 132284 | FUNCTION(replace, 3, 0, 0, replaceFunc ), |
| 132285 | FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc ), |
| @@ -134562,11 +135212,11 @@ | |
| 134562 | }else if( pLeft->pPrior ){ |
| 134563 | /* In this case set the SF_MultiValue flag only if it was set on pLeft */ |
| 134564 | f = (f & pLeft->selFlags); |
| 134565 | } |
| 134566 | pSelect = sqlite3SelectNew(pParse, pRow, 0, 0, 0, 0, 0, f, 0); |
| 134567 | pLeft->selFlags &= ~SF_MultiValue; |
| 134568 | if( pSelect ){ |
| 134569 | pSelect->op = TK_ALL; |
| 134570 | pSelect->pPrior = pLeft; |
| 134571 | pLeft = pSelect; |
| 134572 | } |
| @@ -139168,52 +139818,52 @@ | |
| 139168 | /* 11 */ "notnull", |
| 139169 | /* 12 */ "dflt_value", |
| 139170 | /* 13 */ "pk", |
| 139171 | /* 14 */ "hidden", |
| 139172 | /* table_info reuses 8 */ |
| 139173 | /* 15 */ "schema", /* Used by: table_list */ |
| 139174 | /* 16 */ "name", |
| 139175 | /* 17 */ "type", |
| 139176 | /* 18 */ "ncol", |
| 139177 | /* 19 */ "wr", |
| 139178 | /* 20 */ "strict", |
| 139179 | /* 21 */ "seqno", /* Used by: index_xinfo */ |
| 139180 | /* 22 */ "cid", |
| 139181 | /* 23 */ "name", |
| 139182 | /* 24 */ "desc", |
| 139183 | /* 25 */ "coll", |
| 139184 | /* 26 */ "key", |
| 139185 | /* 27 */ "name", /* Used by: function_list */ |
| 139186 | /* 28 */ "builtin", |
| 139187 | /* 29 */ "type", |
| 139188 | /* 30 */ "enc", |
| 139189 | /* 31 */ "narg", |
| 139190 | /* 32 */ "flags", |
| 139191 | /* 33 */ "tbl", /* Used by: stats */ |
| 139192 | /* 34 */ "idx", |
| 139193 | /* 35 */ "wdth", |
| 139194 | /* 36 */ "hght", |
| 139195 | /* 37 */ "flgs", |
| 139196 | /* 38 */ "seq", /* Used by: index_list */ |
| 139197 | /* 39 */ "name", |
| 139198 | /* 40 */ "unique", |
| 139199 | /* 41 */ "origin", |
| 139200 | /* 42 */ "partial", |
| 139201 | /* 43 */ "table", /* Used by: foreign_key_check */ |
| 139202 | /* 44 */ "rowid", |
| 139203 | /* 45 */ "parent", |
| 139204 | /* 46 */ "fkid", |
| 139205 | /* index_info reuses 21 */ |
| 139206 | /* 47 */ "seq", /* Used by: database_list */ |
| 139207 | /* 48 */ "name", |
| 139208 | /* 49 */ "file", |
| 139209 | /* 50 */ "busy", /* Used by: wal_checkpoint */ |
| 139210 | /* 51 */ "log", |
| 139211 | /* 52 */ "checkpointed", |
| 139212 | /* collation_list reuses 38 */ |
| 139213 | /* 53 */ "database", /* Used by: lock_status */ |
| 139214 | /* 54 */ "status", |
| 139215 | /* 55 */ "cache_size", /* Used by: default_cache_size */ |
| 139216 | /* module_list pragma_list reuses 9 */ |
| 139217 | /* 56 */ "timeout", /* Used by: busy_timeout */ |
| 139218 | }; |
| 139219 | |
| @@ -139302,11 +139952,11 @@ | |
| 139302 | #endif |
| 139303 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) |
| 139304 | {/* zName: */ "collation_list", |
| 139305 | /* ePragTyp: */ PragTyp_COLLATION_LIST, |
| 139306 | /* ePragFlg: */ PragFlg_Result0, |
| 139307 | /* ColNames: */ 38, 2, |
| 139308 | /* iArg: */ 0 }, |
| 139309 | #endif |
| 139310 | #if !defined(SQLITE_OMIT_COMPILEOPTION_DIAGS) |
| 139311 | {/* zName: */ "compile_options", |
| 139312 | /* ePragTyp: */ PragTyp_COMPILE_OPTIONS, |
| @@ -139337,11 +139987,11 @@ | |
| 139337 | #endif |
| 139338 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) |
| 139339 | {/* zName: */ "database_list", |
| 139340 | /* ePragTyp: */ PragTyp_DATABASE_LIST, |
| 139341 | /* ePragFlg: */ PragFlg_Result0, |
| 139342 | /* ColNames: */ 47, 3, |
| 139343 | /* iArg: */ 0 }, |
| 139344 | #endif |
| 139345 | #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED) |
| 139346 | {/* zName: */ "default_cache_size", |
| 139347 | /* ePragTyp: */ PragTyp_DEFAULT_CACHE_SIZE, |
| @@ -139417,11 +140067,11 @@ | |
| 139417 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) |
| 139418 | #if !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS) |
| 139419 | {/* zName: */ "function_list", |
| 139420 | /* ePragTyp: */ PragTyp_FUNCTION_LIST, |
| 139421 | /* ePragFlg: */ PragFlg_Result0, |
| 139422 | /* ColNames: */ 27, 6, |
| 139423 | /* iArg: */ 0 }, |
| 139424 | #endif |
| 139425 | #endif |
| 139426 | {/* zName: */ "hard_heap_limit", |
| 139427 | /* ePragTyp: */ PragTyp_HARD_HEAP_LIMIT, |
| @@ -139446,21 +140096,21 @@ | |
| 139446 | #endif |
| 139447 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) |
| 139448 | {/* zName: */ "index_info", |
| 139449 | /* ePragTyp: */ PragTyp_INDEX_INFO, |
| 139450 | /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, |
| 139451 | /* ColNames: */ 21, 3, |
| 139452 | /* iArg: */ 0 }, |
| 139453 | {/* zName: */ "index_list", |
| 139454 | /* ePragTyp: */ PragTyp_INDEX_LIST, |
| 139455 | /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, |
| 139456 | /* ColNames: */ 38, 5, |
| 139457 | /* iArg: */ 0 }, |
| 139458 | {/* zName: */ "index_xinfo", |
| 139459 | /* ePragTyp: */ PragTyp_INDEX_INFO, |
| 139460 | /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, |
| 139461 | /* ColNames: */ 21, 6, |
| 139462 | /* iArg: */ 1 }, |
| 139463 | #endif |
| 139464 | #if !defined(SQLITE_OMIT_INTEGRITY_CHECK) |
| 139465 | {/* zName: */ "integrity_check", |
| 139466 | /* ePragTyp: */ PragTyp_INTEGRITY_CHECK, |
| @@ -139635,11 +140285,11 @@ | |
| 139635 | #endif |
| 139636 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) && defined(SQLITE_DEBUG) |
| 139637 | {/* zName: */ "stats", |
| 139638 | /* ePragTyp: */ PragTyp_STATS, |
| 139639 | /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq, |
| 139640 | /* ColNames: */ 33, 5, |
| 139641 | /* iArg: */ 0 }, |
| 139642 | #endif |
| 139643 | #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) |
| 139644 | {/* zName: */ "synchronous", |
| 139645 | /* ePragTyp: */ PragTyp_SYNCHRONOUS, |
| @@ -139654,11 +140304,11 @@ | |
| 139654 | /* ColNames: */ 8, 6, |
| 139655 | /* iArg: */ 0 }, |
| 139656 | {/* zName: */ "table_list", |
| 139657 | /* ePragTyp: */ PragTyp_TABLE_LIST, |
| 139658 | /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1, |
| 139659 | /* ColNames: */ 15, 6, |
| 139660 | /* iArg: */ 0 }, |
| 139661 | {/* zName: */ "table_xinfo", |
| 139662 | /* ePragTyp: */ PragTyp_TABLE_INFO, |
| 139663 | /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, |
| 139664 | /* ColNames: */ 8, 7, |
| @@ -139731,11 +140381,11 @@ | |
| 139731 | /* ColNames: */ 0, 0, |
| 139732 | /* iArg: */ 0 }, |
| 139733 | {/* zName: */ "wal_checkpoint", |
| 139734 | /* ePragTyp: */ PragTyp_WAL_CHECKPOINT, |
| 139735 | /* ePragFlg: */ PragFlg_NeedSchema, |
| 139736 | /* ColNames: */ 50, 3, |
| 139737 | /* iArg: */ 0 }, |
| 139738 | #endif |
| 139739 | #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) |
| 139740 | {/* zName: */ "writable_schema", |
| 139741 | /* ePragTyp: */ PragTyp_FLAG, |
| @@ -139753,11 +140403,11 @@ | |
| 139753 | ** When the 0x10 bit of PRAGMA optimize is set, any ANALYZE commands |
| 139754 | ** will be run with an analysis_limit set to the lessor of the value of |
| 139755 | ** the following macro or to the actual analysis_limit if it is non-zero, |
| 139756 | ** in order to prevent PRAGMA optimize from running for too long. |
| 139757 | ** |
| 139758 | ** The value of 2000 is chosen emperically so that the worst-case run-time |
| 139759 | ** for PRAGMA optimize does not exceed 100 milliseconds against a variety |
| 139760 | ** of test databases on a RaspberryPI-4 compiled using -Os and without |
| 139761 | ** -DSQLITE_DEBUG. Of course, your mileage may vary. For the purpose of |
| 139762 | ** this paragraph, "worst-case" means that ANALYZE ends up being |
| 139763 | ** run on every table in the database. The worst case typically only |
| @@ -144042,11 +144692,11 @@ | |
| 144042 | pNew->iOffset = 0; |
| 144043 | pNew->selId = ++pParse->nSelect; |
| 144044 | pNew->addrOpenEphm[0] = -1; |
| 144045 | pNew->addrOpenEphm[1] = -1; |
| 144046 | pNew->nSelectRow = 0; |
| 144047 | if( pSrc==0 ) pSrc = sqlite3DbMallocZero(pParse->db, sizeof(*pSrc)); |
| 144048 | pNew->pSrc = pSrc; |
| 144049 | pNew->pWhere = pWhere; |
| 144050 | pNew->pGroupBy = pGroupBy; |
| 144051 | pNew->pHaving = pHaving; |
| 144052 | pNew->pOrderBy = pOrderBy; |
| @@ -145425,20 +146075,20 @@ | |
| 145425 | /* |
| 145426 | ** Allocate a KeyInfo object sufficient for an index of N key columns and |
| 145427 | ** X extra columns. |
| 145428 | */ |
| 145429 | SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){ |
| 145430 | int nExtra = (N+X)*(sizeof(CollSeq*)+1) - sizeof(CollSeq*); |
| 145431 | KeyInfo *p = sqlite3DbMallocRawNN(db, sizeof(KeyInfo) + nExtra); |
| 145432 | if( p ){ |
| 145433 | p->aSortFlags = (u8*)&p->aColl[N+X]; |
| 145434 | p->nKeyField = (u16)N; |
| 145435 | p->nAllField = (u16)(N+X); |
| 145436 | p->enc = ENC(db); |
| 145437 | p->db = db; |
| 145438 | p->nRef = 1; |
| 145439 | memset(&p[1], 0, nExtra); |
| 145440 | }else{ |
| 145441 | return (KeyInfo*)sqlite3OomFault(db); |
| 145442 | } |
| 145443 | return p; |
| 145444 | } |
| @@ -149534,11 +150184,11 @@ | |
| 149534 | p->pNext = 0; |
| 149535 | p->pWith = 0; |
| 149536 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| 149537 | p->pWinDefn = 0; |
| 149538 | #endif |
| 149539 | p->selFlags &= ~SF_Compound; |
| 149540 | assert( (p->selFlags & SF_Converted)==0 ); |
| 149541 | p->selFlags |= SF_Converted; |
| 149542 | assert( pNew->pPrior!=0 ); |
| 149543 | pNew->pPrior->pNext = pNew; |
| 149544 | pNew->pLimit = 0; |
| @@ -149950,11 +150600,11 @@ | |
| 149950 | } |
| 149951 | pTabList = p->pSrc; |
| 149952 | pEList = p->pEList; |
| 149953 | if( pParse->pWith && (p->selFlags & SF_View) ){ |
| 149954 | if( p->pWith==0 ){ |
| 149955 | p->pWith = (With*)sqlite3DbMallocZero(db, sizeof(With)); |
| 149956 | if( p->pWith==0 ){ |
| 149957 | return WRC_Abort; |
| 149958 | } |
| 149959 | } |
| 149960 | p->pWith->bView = 1; |
| @@ -151089,10 +151739,11 @@ | |
| 151089 | ** * The subquery is a UNION ALL of two or more terms |
| 151090 | ** * The subquery does not have a LIMIT clause |
| 151091 | ** * There is no WHERE or GROUP BY or HAVING clauses on the subqueries |
| 151092 | ** * The outer query is a simple count(*) with no WHERE clause or other |
| 151093 | ** extraneous syntax. |
| 151094 | ** |
| 151095 | ** Return TRUE if the optimization is undertaken. |
| 151096 | */ |
| 151097 | static int countOfViewOptimization(Parse *pParse, Select *p){ |
| 151098 | Select *pSub, *pPrior; |
| @@ -151121,11 +151772,15 @@ | |
| 151121 | if( pSub->selFlags & SF_CopyCte ) return 0; /* Not a CTE */ |
| 151122 | do{ |
| 151123 | if( pSub->op!=TK_ALL && pSub->pPrior ) return 0; /* Must be UNION ALL */ |
| 151124 | if( pSub->pWhere ) return 0; /* No WHERE clause */ |
| 151125 | if( pSub->pLimit ) return 0; /* No LIMIT clause */ |
| 151126 | if( pSub->selFlags & SF_Aggregate ) return 0; /* Not an aggregate */ |
| 151127 | assert( pSub->pHaving==0 ); /* Due to the previous */ |
| 151128 | pSub = pSub->pPrior; /* Repeat over compound */ |
| 151129 | }while( pSub ); |
| 151130 | |
| 151131 | /* If we reach this point then it is OK to perform the transformation */ |
| @@ -151133,18 +151788,18 @@ | |
| 151133 | db = pParse->db; |
| 151134 | pCount = pExpr; |
| 151135 | pExpr = 0; |
| 151136 | pSub = sqlite3SubqueryDetach(db, pFrom); |
| 151137 | sqlite3SrcListDelete(db, p->pSrc); |
| 151138 | p->pSrc = sqlite3DbMallocZero(pParse->db, sizeof(*p->pSrc)); |
| 151139 | while( pSub ){ |
| 151140 | Expr *pTerm; |
| 151141 | pPrior = pSub->pPrior; |
| 151142 | pSub->pPrior = 0; |
| 151143 | pSub->pNext = 0; |
| 151144 | pSub->selFlags |= SF_Aggregate; |
| 151145 | pSub->selFlags &= ~SF_Compound; |
| 151146 | pSub->nSelectRow = 0; |
| 151147 | sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, pSub->pEList); |
| 151148 | pTerm = pPrior ? sqlite3ExprDup(db, pCount, 0) : pCount; |
| 151149 | pSub->pEList = sqlite3ExprListAppend(pParse, 0, pTerm); |
| 151150 | pTerm = sqlite3PExpr(pParse, TK_SELECT, 0, 0); |
| @@ -151155,11 +151810,11 @@ | |
| 151155 | pExpr = sqlite3PExpr(pParse, TK_PLUS, pTerm, pExpr); |
| 151156 | } |
| 151157 | pSub = pPrior; |
| 151158 | } |
| 151159 | p->pEList->a[0].pExpr = pExpr; |
| 151160 | p->selFlags &= ~SF_Aggregate; |
| 151161 | |
| 151162 | #if TREETRACE_ENABLED |
| 151163 | if( sqlite3TreeTrace & 0x200 ){ |
| 151164 | TREETRACE(0x200,pParse,p,("After count-of-view optimization:\n")); |
| 151165 | sqlite3TreeViewSelect(0, p, 0); |
| @@ -151362,11 +152017,11 @@ | |
| 151362 | sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, |
| 151363 | p->pOrderBy); |
| 151364 | testcase( pParse->earlyCleanup ); |
| 151365 | p->pOrderBy = 0; |
| 151366 | } |
| 151367 | p->selFlags &= ~SF_Distinct; |
| 151368 | p->selFlags |= SF_NoopOrderBy; |
| 151369 | } |
| 151370 | sqlite3SelectPrep(pParse, p, 0); |
| 151371 | if( pParse->nErr ){ |
| 151372 | goto select_end; |
| @@ -151401,11 +152056,11 @@ | |
| 151401 | |
| 151402 | /* Clear the SF_UFSrcCheck flag. The check has already been performed, |
| 151403 | ** and leaving this flag set can cause errors if a compound sub-query |
| 151404 | ** in p->pSrc is flattened into this query and this function called |
| 151405 | ** again as part of compound SELECT processing. */ |
| 151406 | p->selFlags &= ~SF_UFSrcCheck; |
| 151407 | } |
| 151408 | |
| 151409 | if( pDest->eDest==SRT_Output ){ |
| 151410 | sqlite3GenerateColumnNames(pParse, p); |
| 151411 | } |
| @@ -151890,11 +152545,11 @@ | |
| 151890 | && OptimizationEnabled(db, SQLITE_GroupByOrder) |
| 151891 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| 151892 | && p->pWin==0 |
| 151893 | #endif |
| 151894 | ){ |
| 151895 | p->selFlags &= ~SF_Distinct; |
| 151896 | pGroupBy = p->pGroupBy = sqlite3ExprListDup(db, pEList, 0); |
| 151897 | if( pGroupBy ){ |
| 151898 | for(i=0; i<pGroupBy->nExpr; i++){ |
| 151899 | pGroupBy->a[i].u.x.iOrderByCol = i+1; |
| 151900 | } |
| @@ -153924,11 +154579,12 @@ | |
| 153924 | Vdbe *v = pParse->pVdbe; |
| 153925 | sqlite3 *db = pParse->db; |
| 153926 | ExprList *pNew; |
| 153927 | Returning *pReturning; |
| 153928 | Select sSelect; |
| 153929 | SrcList sFrom; |
| 153930 | |
| 153931 | assert( v!=0 ); |
| 153932 | if( !pParse->bReturning ){ |
| 153933 | /* This RETURNING trigger must be for a different statement as |
| 153934 | ** this statement lacks a RETURNING clause. */ |
| @@ -153940,17 +154596,18 @@ | |
| 153940 | if( pTrigger != &(pReturning->retTrig) ){ |
| 153941 | /* This RETURNING trigger is for a different statement */ |
| 153942 | return; |
| 153943 | } |
| 153944 | memset(&sSelect, 0, sizeof(sSelect)); |
| 153945 | memset(&sFrom, 0, sizeof(sFrom)); |
| 153946 | sSelect.pEList = sqlite3ExprListDup(db, pReturning->pReturnEL, 0); |
| 153947 | sSelect.pSrc = &sFrom; |
| 153948 | sFrom.nSrc = 1; |
| 153949 | sFrom.a[0].pSTab = pTab; |
| 153950 | sFrom.a[0].zName = pTab->zName; /* tag-20240424-1 */ |
| 153951 | sFrom.a[0].iCursor = -1; |
| 153952 | sqlite3SelectPrep(pParse, &sSelect, 0); |
| 153953 | if( pParse->nErr==0 ){ |
| 153954 | assert( db->mallocFailed==0 ); |
| 153955 | sqlite3GenerateColumnNames(pParse, &sSelect); |
| 153956 | } |
| @@ -156347,11 +157004,11 @@ | |
| 156347 | saved_flags = db->flags; |
| 156348 | saved_mDbFlags = db->mDbFlags; |
| 156349 | saved_nChange = db->nChange; |
| 156350 | saved_nTotalChange = db->nTotalChange; |
| 156351 | saved_mTrace = db->mTrace; |
| 156352 | db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks; |
| 156353 | db->mDbFlags |= DBFLAG_PreferBuiltin | DBFLAG_Vacuum; |
| 156354 | db->flags &= ~(u64)(SQLITE_ForeignKeys | SQLITE_ReverseOrder |
| 156355 | | SQLITE_Defensive | SQLITE_CountRows); |
| 156356 | db->mTrace = 0; |
| 156357 | |
| @@ -158476,13 +159133,18 @@ | |
| 158476 | WhereLoop *pLoops; /* List of all WhereLoop objects */ |
| 158477 | WhereMemBlock *pMemToFree;/* Memory to free when this object destroyed */ |
| 158478 | Bitmask revMask; /* Mask of ORDER BY terms that need reversing */ |
| 158479 | WhereClause sWC; /* Decomposition of the WHERE clause */ |
| 158480 | WhereMaskSet sMaskSet; /* Map cursor numbers to bitmasks */ |
| 158481 | WhereLevel a[1]; /* Information about each nest loop in WHERE */ |
| 158482 | }; |
| 158483 | |
| 158484 | /* |
| 158485 | ** Private interfaces - callable only by other where.c routines. |
| 158486 | ** |
| 158487 | ** where.c: |
| 158488 | */ |
| @@ -160929,12 +161591,11 @@ | |
| 160929 | */ |
| 160930 | if( pWInfo->nLevel>1 ){ |
| 160931 | int nNotReady; /* The number of notReady tables */ |
| 160932 | SrcItem *origSrc; /* Original list of tables */ |
| 160933 | nNotReady = pWInfo->nLevel - iLevel - 1; |
| 160934 | pOrTab = sqlite3DbMallocRawNN(db, |
| 160935 | sizeof(*pOrTab)+ nNotReady*sizeof(pOrTab->a[0])); |
| 160936 | if( pOrTab==0 ) return notReady; |
| 160937 | pOrTab->nAlloc = (u8)(nNotReady + 1); |
| 160938 | pOrTab->nSrc = pOrTab->nAlloc; |
| 160939 | memcpy(pOrTab->a, pTabItem, sizeof(*pTabItem)); |
| 160940 | origSrc = pWInfo->pTabList->a; |
| @@ -161473,11 +162134,12 @@ | |
| 161473 | Expr *pSubWhere = 0; |
| 161474 | WhereClause *pWC = &pWInfo->sWC; |
| 161475 | WhereInfo *pSubWInfo; |
| 161476 | WhereLoop *pLoop = pLevel->pWLoop; |
| 161477 | SrcItem *pTabItem = &pWInfo->pTabList->a[pLevel->iFrom]; |
| 161478 | SrcList sFrom; |
| 161479 | Bitmask mAll = 0; |
| 161480 | int k; |
| 161481 | |
| 161482 | ExplainQueryPlan((pParse, 1, "RIGHT-JOIN %s", pTabItem->pSTab->zName)); |
| 161483 | sqlite3VdbeNoJumpsOutsideSubrtn(v, pRJ->addrSubrtn, pRJ->endSubrtn, |
| @@ -161517,17 +162179,18 @@ | |
| 161517 | if( ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) ) continue; |
| 161518 | pSubWhere = sqlite3ExprAnd(pParse, pSubWhere, |
| 161519 | sqlite3ExprDup(pParse->db, pTerm->pExpr, 0)); |
| 161520 | } |
| 161521 | } |
| 161522 | sFrom.nSrc = 1; |
| 161523 | sFrom.nAlloc = 1; |
| 161524 | memcpy(&sFrom.a[0], pTabItem, sizeof(SrcItem)); |
| 161525 | sFrom.a[0].fg.jointype = 0; |
| 161526 | assert( pParse->withinRJSubrtn < 100 ); |
| 161527 | pParse->withinRJSubrtn++; |
| 161528 | pSubWInfo = sqlite3WhereBegin(pParse, &sFrom, pSubWhere, 0, 0, 0, |
| 161529 | WHERE_RIGHT_JOIN, 0); |
| 161530 | if( pSubWInfo ){ |
| 161531 | int iCur = pLevel->iTabCur; |
| 161532 | int r = ++pParse->nMem; |
| 161533 | int nPk; |
| @@ -163511,15 +164174,20 @@ | |
| 163511 | WhereClause *pWC; /* The Where clause being analyzed */ |
| 163512 | Parse *pParse; /* The parsing context */ |
| 163513 | int eDistinct; /* Value to return from sqlite3_vtab_distinct() */ |
| 163514 | u32 mIn; /* Mask of terms that are <col> IN (...) */ |
| 163515 | u32 mHandleIn; /* Terms that vtab will handle as <col> IN (...) */ |
| 163516 | sqlite3_value *aRhs[1]; /* RHS values for constraints. MUST BE LAST |
| 163517 | ** because extra space is allocated to hold up |
| 163518 | ** to nTerm such values */ |
| 163519 | }; |
| 163520 | |
| 163521 | /* Forward declaration of methods */ |
| 163522 | static int whereLoopResize(sqlite3*, WhereLoop*, int); |
| 163523 | |
| 163524 | /* |
| 163525 | ** Return the estimated number of output rows from a WHERE clause |
| @@ -164580,10 +165248,12 @@ | |
| 164580 | if( pSrc->colUsed & MASKBIT(BMS-1) ){ |
| 164581 | nKeyCol += pTable->nCol - BMS + 1; |
| 164582 | } |
| 164583 | |
| 164584 | /* Construct the Index object to describe this index */ |
| 164585 | pIdx = sqlite3AllocateIndexObject(pParse->db, nKeyCol+HasRowid(pTable), |
| 164586 | 0, &zNotUsed); |
| 164587 | if( pIdx==0 ) goto end_auto_index_create; |
| 164588 | pLoop->u.btree.pIndex = pIdx; |
| 164589 | pIdx->zName = "auto-index"; |
| @@ -164991,12 +165661,12 @@ | |
| 164991 | |
| 164992 | /* Allocate the sqlite3_index_info structure |
| 164993 | */ |
| 164994 | pIdxInfo = sqlite3DbMallocZero(pParse->db, sizeof(*pIdxInfo) |
| 164995 | + (sizeof(*pIdxCons) + sizeof(*pUsage))*nTerm |
| 164996 | + sizeof(*pIdxOrderBy)*nOrderBy + sizeof(*pHidden) |
| 164997 | + sizeof(sqlite3_value*)*nTerm ); |
| 164998 | if( pIdxInfo==0 ){ |
| 164999 | sqlite3ErrorMsg(pParse, "out of memory"); |
| 165000 | return 0; |
| 165001 | } |
| 165002 | pHidden = (struct HiddenIndexInfo*)&pIdxInfo[1]; |
| @@ -170186,14 +170856,11 @@ | |
| 170186 | ** struct, the contents of WhereInfo.a[], the WhereClause structure |
| 170187 | ** and the WhereMaskSet structure. Since WhereClause contains an 8-byte |
| 170188 | ** field (type Bitmask) it must be aligned on an 8-byte boundary on |
| 170189 | ** some architectures. Hence the ROUND8() below. |
| 170190 | */ |
| 170191 | nByteWInfo = ROUND8P(sizeof(WhereInfo)); |
| 170192 | if( nTabList>1 ){ |
| 170193 | nByteWInfo = ROUND8P(nByteWInfo + (nTabList-1)*sizeof(WhereLevel)); |
| 170194 | } |
| 170195 | pWInfo = sqlite3DbMallocRawNN(db, nByteWInfo + sizeof(WhereLoop)); |
| 170196 | if( db->mallocFailed ){ |
| 170197 | sqlite3DbFree(db, pWInfo); |
| 170198 | pWInfo = 0; |
| 170199 | goto whereBeginError; |
| @@ -172141,11 +172808,11 @@ | |
| 172141 | |
| 172142 | p->pSrc = 0; |
| 172143 | p->pWhere = 0; |
| 172144 | p->pGroupBy = 0; |
| 172145 | p->pHaving = 0; |
| 172146 | p->selFlags &= ~SF_Aggregate; |
| 172147 | p->selFlags |= SF_WinRewrite; |
| 172148 | |
| 172149 | /* Create the ORDER BY clause for the sub-select. This is the concatenation |
| 172150 | ** of the window PARTITION and ORDER BY clauses. Then, if this makes it |
| 172151 | ** redundant, remove the ORDER BY from the parent SELECT. */ |
| @@ -178280,12 +178947,12 @@ | |
| 178280 | pRhs = sqlite3SelectNew(pParse,0,pFrom,0,0,0,0,0,0); |
| 178281 | } |
| 178282 | if( pRhs ){ |
| 178283 | pRhs->op = (u8)yymsp[-1].minor.yy502; |
| 178284 | pRhs->pPrior = pLhs; |
| 178285 | if( ALWAYS(pLhs) ) pLhs->selFlags &= ~SF_MultiValue; |
| 178286 | pRhs->selFlags &= ~SF_MultiValue; |
| 178287 | if( yymsp[-1].minor.yy502!=TK_ALL ) pParse->hasCompound = 1; |
| 178288 | }else{ |
| 178289 | sqlite3SelectDelete(pParse->db, pLhs); |
| 178290 | } |
| 178291 | yymsp[-2].minor.yy637 = pRhs; |
| @@ -181025,11 +181692,15 @@ | |
| 181025 | tokenType = analyzeOverKeyword((const u8*)&zSql[4], lastTokenParsed); |
| 181026 | }else if( tokenType==TK_FILTER ){ |
| 181027 | assert( n==6 ); |
| 181028 | tokenType = analyzeFilterKeyword((const u8*)&zSql[6], lastTokenParsed); |
| 181029 | #endif /* SQLITE_OMIT_WINDOWFUNC */ |
| 181030 | }else if( tokenType==TK_COMMENT && (db->flags & SQLITE_Comments)!=0 ){ |
| 181031 | zSql += n; |
| 181032 | continue; |
| 181033 | }else if( tokenType!=TK_QNUMBER ){ |
| 181034 | Token x; |
| 181035 | x.z = zSql; |
| @@ -181920,10 +182591,18 @@ | |
| 181920 | } |
| 181921 | #endif |
| 181922 | if( rc==SQLITE_OK ){ |
| 181923 | sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage, |
| 181924 | sqlite3GlobalConfig.szPage, sqlite3GlobalConfig.nPage); |
| 181925 | sqlite3MemoryBarrier(); |
| 181926 | sqlite3GlobalConfig.isInit = 1; |
| 181927 | #ifdef SQLITE_EXTRA_INIT |
| 181928 | bRunExtraInit = 1; |
| 181929 | #endif |
| @@ -183396,10 +184075,13 @@ | |
| 183396 | sqlite3_mutex_enter(db->mutex); |
| 183397 | db->busyHandler.xBusyHandler = xBusy; |
| 183398 | db->busyHandler.pBusyArg = pArg; |
| 183399 | db->busyHandler.nBusy = 0; |
| 183400 | db->busyTimeout = 0; |
| 183401 | sqlite3_mutex_leave(db->mutex); |
| 183402 | return SQLITE_OK; |
| 183403 | } |
| 183404 | |
| 183405 | #ifndef SQLITE_OMIT_PROGRESS_CALLBACK |
| @@ -183445,15 +184127,50 @@ | |
| 183445 | #endif |
| 183446 | if( ms>0 ){ |
| 183447 | sqlite3_busy_handler(db, (int(*)(void*,int))sqliteDefaultBusyCallback, |
| 183448 | (void*)db); |
| 183449 | db->busyTimeout = ms; |
| 183450 | }else{ |
| 183451 | sqlite3_busy_handler(db, 0, 0); |
| 183452 | } |
| 183453 | return SQLITE_OK; |
| 183454 | } |
| 183455 | |
| 183456 | /* |
| 183457 | ** Cause any pending operation to stop at its earliest opportunity. |
| 183458 | */ |
| 183459 | SQLITE_API void sqlite3_interrupt(sqlite3 *db){ |
| @@ -185416,11 +186133,11 @@ | |
| 185416 | }else if( pData==0 ){ |
| 185417 | sqlite3_mutex_leave(db->mutex); |
| 185418 | return SQLITE_OK; |
| 185419 | }else{ |
| 185420 | size_t n = strlen(zName); |
| 185421 | p = sqlite3_malloc64( sizeof(DbClientData)+n+1 ); |
| 185422 | if( p==0 ){ |
| 185423 | if( xDestructor ) xDestructor(pData); |
| 185424 | sqlite3_mutex_leave(db->mutex); |
| 185425 | return SQLITE_NOMEM; |
| 185426 | } |
| @@ -185782,12 +186499,12 @@ | |
| 185782 | #endif |
| 185783 | |
| 185784 | /* sqlite3_test_control(SQLITE_TESTCTRL_FK_NO_ACTION, sqlite3 *db, int b); |
| 185785 | ** |
| 185786 | ** If b is true, then activate the SQLITE_FkNoAction setting. If b is |
| 185787 | ** false then clearn that setting. If the SQLITE_FkNoAction setting is |
| 185788 | ** abled, all foreign key ON DELETE and ON UPDATE actions behave as if |
| 185789 | ** they were NO ACTION, regardless of how they are defined. |
| 185790 | ** |
| 185791 | ** NB: One must usually run "PRAGMA writable_schema=RESET" after |
| 185792 | ** using this test-control, before it will take full effect. failing |
| 185793 | ** to reset the schema can result in some unexpected behavior. |
| @@ -187130,11 +187847,11 @@ | |
| 187130 | ** } |
| 187131 | ** |
| 187132 | ** Here, array { X } means zero or more occurrences of X, adjacent in |
| 187133 | ** memory. A "position" is an index of a token in the token stream |
| 187134 | ** generated by the tokenizer. Note that POS_END and POS_COLUMN occur |
| 187135 | ** in the same logical place as the position element, and act as sentinals |
| 187136 | ** ending a position list array. POS_END is 0. POS_COLUMN is 1. |
| 187137 | ** The positions numbers are not stored literally but rather as two more |
| 187138 | ** than the difference from the prior position, or the just the position plus |
| 187139 | ** 2 for the first position. Example: |
| 187140 | ** |
| @@ -187817,10 +188534,23 @@ | |
| 187817 | |
| 187818 | #define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32)) |
| 187819 | #define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64) |
| 187820 | |
| 187821 | #define deliberate_fall_through |
| 187822 | |
| 187823 | #endif /* SQLITE_AMALGAMATION */ |
| 187824 | |
| 187825 | #ifdef SQLITE_DEBUG |
| 187826 | SQLITE_PRIVATE int sqlite3Fts3Corrupt(void); |
| @@ -187922,11 +188652,11 @@ | |
| 187922 | int inTransaction; /* True after xBegin but before xCommit/xRollback */ |
| 187923 | int mxSavepoint; /* Largest valid xSavepoint integer */ |
| 187924 | #endif |
| 187925 | |
| 187926 | #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) |
| 187927 | /* True to disable the incremental doclist optimization. This is controled |
| 187928 | ** by special insert command 'test-no-incr-doclist'. */ |
| 187929 | int bNoIncrDoclist; |
| 187930 | |
| 187931 | /* Number of segments in a level */ |
| 187932 | int nMergeCount; |
| @@ -187974,11 +188704,11 @@ | |
| 187974 | #define FTS3_EVAL_NEXT 1 |
| 187975 | #define FTS3_EVAL_MATCHINFO 2 |
| 187976 | |
| 187977 | /* |
| 187978 | ** The Fts3Cursor.eSearch member is always set to one of the following. |
| 187979 | ** Actualy, Fts3Cursor.eSearch can be greater than or equal to |
| 187980 | ** FTS3_FULLTEXT_SEARCH. If so, then Fts3Cursor.eSearch - 2 is the index |
| 187981 | ** of the column to be searched. For example, in |
| 187982 | ** |
| 187983 | ** CREATE VIRTUAL TABLE ex1 USING fts3(a,b,c,d); |
| 187984 | ** SELECT docid FROM ex1 WHERE b MATCH 'one two three'; |
| @@ -188047,12 +188777,16 @@ | |
| 188047 | /* Variables below this point are populated by fts3_expr.c when parsing |
| 188048 | ** a MATCH expression. Everything above is part of the evaluation phase. |
| 188049 | */ |
| 188050 | int nToken; /* Number of tokens in the phrase */ |
| 188051 | int iColumn; /* Index of column this phrase must match */ |
| 188052 | Fts3PhraseToken aToken[1]; /* One entry for each token in the phrase */ |
| 188053 | }; |
| 188054 | |
| 188055 | /* |
| 188056 | ** A tree of these objects forms the RHS of a MATCH operator. |
| 188057 | ** |
| 188058 | ** If Fts3Expr.eType is FTSQUERY_PHRASE and isLoaded is true, then aDoclist |
| @@ -190627,11 +191361,11 @@ | |
| 190627 | ** |
| 190628 | ** The space required to store the output is therefore the sum of the |
| 190629 | ** sizes of the two inputs, plus enough space for exactly one of the input |
| 190630 | ** docids to grow. |
| 190631 | ** |
| 190632 | ** A symetric argument may be made if the doclists are in descending |
| 190633 | ** order. |
| 190634 | */ |
| 190635 | aOut = sqlite3_malloc64((i64)n1+n2+FTS3_VARINT_MAX-1+FTS3_BUFFER_PADDING); |
| 190636 | if( !aOut ) return SQLITE_NOMEM; |
| 190637 | |
| @@ -192725,11 +193459,11 @@ | |
| 192725 | ** |
| 192726 | ** * features at least one token that uses an incremental doclist, and |
| 192727 | ** |
| 192728 | ** * does not contain any deferred tokens. |
| 192729 | ** |
| 192730 | ** Advance it to the next matching documnent in the database and populate |
| 192731 | ** the Fts3Doclist.pList and nList fields. |
| 192732 | ** |
| 192733 | ** If there is no "next" entry and no error occurs, then *pbEof is set to |
| 192734 | ** 1 before returning. Otherwise, if no error occurs and the iterator is |
| 192735 | ** successfully advanced, *pbEof is set to 0. |
| @@ -193732,11 +194466,11 @@ | |
| 193732 | |
| 193733 | return rc; |
| 193734 | } |
| 193735 | |
| 193736 | /* |
| 193737 | ** Restart interation for expression pExpr so that the next call to |
| 193738 | ** fts3EvalNext() visits the first row. Do not allow incremental |
| 193739 | ** loading or merging of phrase doclists for this iteration. |
| 193740 | ** |
| 193741 | ** If *pRc is other than SQLITE_OK when this function is called, it is |
| 193742 | ** a no-op. If an error occurs within this function, *pRc is set to an |
| @@ -194923,10 +195657,27 @@ | |
| 194923 | /* |
| 194924 | ** Function getNextNode(), which is called by fts3ExprParse(), may itself |
| 194925 | ** call fts3ExprParse(). So this forward declaration is required. |
| 194926 | */ |
| 194927 | static int fts3ExprParse(ParseContext *, const char *, int, Fts3Expr **, int *); |
| 194928 | |
| 194929 | /* |
| 194930 | ** Extract the next token from buffer z (length n) using the tokenizer |
| 194931 | ** and other information (column names etc.) in pParse. Create an Fts3Expr |
| 194932 | ** structure of type FTSQUERY_PHRASE containing a phrase consisting of this |
| @@ -194948,38 +195699,42 @@ | |
| 194948 | sqlite3_tokenizer *pTokenizer = pParse->pTokenizer; |
| 194949 | sqlite3_tokenizer_module const *pModule = pTokenizer->pModule; |
| 194950 | int rc; |
| 194951 | sqlite3_tokenizer_cursor *pCursor; |
| 194952 | Fts3Expr *pRet = 0; |
| 194953 | int i = 0; |
| 194954 | |
| 194955 | /* Set variable i to the maximum number of bytes of input to tokenize. */ |
| 194956 | for(i=0; i<n; i++){ |
| 194957 | if( sqlite3_fts3_enable_parentheses && (z[i]=='(' || z[i]==')') ) break; |
| 194958 | if( z[i]=='"' ) break; |
| 194959 | } |
| 194960 | |
| 194961 | *pnConsumed = i; |
| 194962 | rc = sqlite3Fts3OpenTokenizer(pTokenizer, pParse->iLangid, z, i, &pCursor); |
| 194963 | if( rc==SQLITE_OK ){ |
| 194964 | const char *zToken; |
| 194965 | int nToken = 0, iStart = 0, iEnd = 0, iPosition = 0; |
| 194966 | sqlite3_int64 nByte; /* total space to allocate */ |
| 194967 | |
| 194968 | rc = pModule->xNext(pCursor, &zToken, &nToken, &iStart, &iEnd, &iPosition); |
| 194969 | if( rc==SQLITE_OK ){ |
| 194970 | nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase) + nToken; |
| 194971 | pRet = (Fts3Expr *)sqlite3Fts3MallocZero(nByte); |
| 194972 | if( !pRet ){ |
| 194973 | rc = SQLITE_NOMEM; |
| 194974 | }else{ |
| 194975 | pRet->eType = FTSQUERY_PHRASE; |
| 194976 | pRet->pPhrase = (Fts3Phrase *)&pRet[1]; |
| 194977 | pRet->pPhrase->nToken = 1; |
| 194978 | pRet->pPhrase->iColumn = iCol; |
| 194979 | pRet->pPhrase->aToken[0].n = nToken; |
| 194980 | pRet->pPhrase->aToken[0].z = (char *)&pRet->pPhrase[1]; |
| 194981 | memcpy(pRet->pPhrase->aToken[0].z, zToken, nToken); |
| 194982 | |
| 194983 | if( iEnd<n && z[iEnd]=='*' ){ |
| 194984 | pRet->pPhrase->aToken[0].isPrefix = 1; |
| 194985 | iEnd++; |
| @@ -194999,11 +195754,15 @@ | |
| 194999 | } |
| 195000 | } |
| 195001 | |
| 195002 | } |
| 195003 | *pnConsumed = iEnd; |
| 195004 | }else if( i && rc==SQLITE_DONE ){ |
| 195005 | rc = SQLITE_OK; |
| 195006 | } |
| 195007 | |
| 195008 | pModule->xClose(pCursor); |
| 195009 | } |
| @@ -195048,11 +195807,11 @@ | |
| 195048 | Fts3Expr *p = 0; |
| 195049 | sqlite3_tokenizer_cursor *pCursor = 0; |
| 195050 | char *zTemp = 0; |
| 195051 | i64 nTemp = 0; |
| 195052 | |
| 195053 | const int nSpace = sizeof(Fts3Expr) + sizeof(Fts3Phrase); |
| 195054 | int nToken = 0; |
| 195055 | |
| 195056 | /* The final Fts3Expr data structure, including the Fts3Phrase, |
| 195057 | ** Fts3PhraseToken structures token buffers are all stored as a single |
| 195058 | ** allocation so that the expression can be freed with a single call to |
| @@ -195420,11 +196179,11 @@ | |
| 195420 | int eType = p->eType; |
| 195421 | isPhrase = (eType==FTSQUERY_PHRASE || p->pLeft); |
| 195422 | |
| 195423 | /* The isRequirePhrase variable is set to true if a phrase or |
| 195424 | ** an expression contained in parenthesis is required. If a |
| 195425 | ** binary operator (AND, OR, NOT or NEAR) is encounted when |
| 195426 | ** isRequirePhrase is set, this is a syntax error. |
| 195427 | */ |
| 195428 | if( !isPhrase && isRequirePhrase ){ |
| 195429 | sqlite3Fts3ExprFree(p); |
| 195430 | rc = SQLITE_ERROR; |
| @@ -196002,11 +196761,10 @@ | |
| 196002 | pTokenizer, 0, azCol, 0, nCol, nCol, zExpr, nExpr, &pExpr |
| 196003 | ); |
| 196004 | } |
| 196005 | |
| 196006 | if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM ){ |
| 196007 | sqlite3Fts3ExprFree(pExpr); |
| 196008 | sqlite3_result_error(context, "Error parsing expression", -1); |
| 196009 | }else if( rc==SQLITE_NOMEM || !(zBuf = exprToString(pExpr, 0)) ){ |
| 196010 | sqlite3_result_error_nomem(context); |
| 196011 | }else{ |
| 196012 | sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); |
| @@ -196245,11 +197003,11 @@ | |
| 196245 | pEntry->count++; |
| 196246 | pEntry->chain = pNew; |
| 196247 | } |
| 196248 | |
| 196249 | |
| 196250 | /* Resize the hash table so that it cantains "new_size" buckets. |
| 196251 | ** "new_size" must be a power of 2. The hash table might fail |
| 196252 | ** to resize if sqliteMalloc() fails. |
| 196253 | ** |
| 196254 | ** Return non-zero if a memory allocation error occurs. |
| 196255 | */ |
| @@ -196700,11 +197458,11 @@ | |
| 196700 | isConsonant(z+2); |
| 196701 | } |
| 196702 | |
| 196703 | /* |
| 196704 | ** If the word ends with zFrom and xCond() is true for the stem |
| 196705 | ** of the word that preceeds the zFrom ending, then change the |
| 196706 | ** ending to zTo. |
| 196707 | ** |
| 196708 | ** The input word *pz and zFrom are both in reverse order. zTo |
| 196709 | ** is in normal order. |
| 196710 | ** |
| @@ -202283,11 +203041,11 @@ | |
| 202283 | ** previous term. Before this function returns, it is updated to contain a |
| 202284 | ** copy of zTerm/nTerm. |
| 202285 | ** |
| 202286 | ** It is assumed that the buffer associated with pNode is already large |
| 202287 | ** enough to accommodate the new entry. The buffer associated with pPrev |
| 202288 | ** is extended by this function if requrired. |
| 202289 | ** |
| 202290 | ** If an error (i.e. OOM condition) occurs, an SQLite error code is |
| 202291 | ** returned. Otherwise, SQLITE_OK. |
| 202292 | */ |
| 202293 | static int fts3AppendToNode( |
| @@ -203946,11 +204704,11 @@ | |
| 203946 | #endif |
| 203947 | |
| 203948 | /* |
| 203949 | ** SQLite value pRowid contains the rowid of a row that may or may not be |
| 203950 | ** present in the FTS3 table. If it is, delete it and adjust the contents |
| 203951 | ** of subsiduary data structures accordingly. |
| 203952 | */ |
| 203953 | static int fts3DeleteByRowid( |
| 203954 | Fts3Table *p, |
| 203955 | sqlite3_value *pRowid, |
| 203956 | int *pnChng, /* IN/OUT: Decrement if row is deleted */ |
| @@ -204272,12 +205030,16 @@ | |
| 204272 | struct MatchinfoBuffer { |
| 204273 | u8 aRef[3]; |
| 204274 | int nElem; |
| 204275 | int bGlobal; /* Set if global data is loaded */ |
| 204276 | char *zMatchinfo; |
| 204277 | u32 aMatchinfo[1]; |
| 204278 | }; |
| 204279 | |
| 204280 | |
| 204281 | /* |
| 204282 | ** The snippet() and offsets() functions both return text values. An instance |
| 204283 | ** of the following structure is used to accumulate those values while the |
| @@ -204299,17 +205061,17 @@ | |
| 204299 | ** Allocate a two-slot MatchinfoBuffer object. |
| 204300 | */ |
| 204301 | static MatchinfoBuffer *fts3MIBufferNew(size_t nElem, const char *zMatchinfo){ |
| 204302 | MatchinfoBuffer *pRet; |
| 204303 | sqlite3_int64 nByte = sizeof(u32) * (2*(sqlite3_int64)nElem + 1) |
| 204304 | + sizeof(MatchinfoBuffer); |
| 204305 | sqlite3_int64 nStr = strlen(zMatchinfo); |
| 204306 | |
| 204307 | pRet = sqlite3Fts3MallocZero(nByte + nStr+1); |
| 204308 | if( pRet ){ |
| 204309 | pRet->aMatchinfo[0] = (u8*)(&pRet->aMatchinfo[1]) - (u8*)pRet; |
| 204310 | pRet->aMatchinfo[1+nElem] = pRet->aMatchinfo[0] |
| 204311 | + sizeof(u32)*((int)nElem+1); |
| 204312 | pRet->nElem = (int)nElem; |
| 204313 | pRet->zMatchinfo = ((char*)pRet) + nByte; |
| 204314 | memcpy(pRet->zMatchinfo, zMatchinfo, nStr+1); |
| 204315 | pRet->aRef[0] = 1; |
| @@ -204319,14 +205081,14 @@ | |
| 204319 | } |
| 204320 | |
| 204321 | static void fts3MIBufferFree(void *p){ |
| 204322 | MatchinfoBuffer *pBuf = (MatchinfoBuffer*)((u8*)p - ((u32*)p)[-1]); |
| 204323 | |
| 204324 | assert( (u32*)p==&pBuf->aMatchinfo[1] |
| 204325 | || (u32*)p==&pBuf->aMatchinfo[pBuf->nElem+2] |
| 204326 | ); |
| 204327 | if( (u32*)p==&pBuf->aMatchinfo[1] ){ |
| 204328 | pBuf->aRef[1] = 0; |
| 204329 | }else{ |
| 204330 | pBuf->aRef[2] = 0; |
| 204331 | } |
| 204332 | |
| @@ -204339,32 +205101,32 @@ | |
| 204339 | void (*xRet)(void*) = 0; |
| 204340 | u32 *aOut = 0; |
| 204341 | |
| 204342 | if( p->aRef[1]==0 ){ |
| 204343 | p->aRef[1] = 1; |
| 204344 | aOut = &p->aMatchinfo[1]; |
| 204345 | xRet = fts3MIBufferFree; |
| 204346 | } |
| 204347 | else if( p->aRef[2]==0 ){ |
| 204348 | p->aRef[2] = 1; |
| 204349 | aOut = &p->aMatchinfo[p->nElem+2]; |
| 204350 | xRet = fts3MIBufferFree; |
| 204351 | }else{ |
| 204352 | aOut = (u32*)sqlite3_malloc64(p->nElem * sizeof(u32)); |
| 204353 | if( aOut ){ |
| 204354 | xRet = sqlite3_free; |
| 204355 | if( p->bGlobal ) memcpy(aOut, &p->aMatchinfo[1], p->nElem*sizeof(u32)); |
| 204356 | } |
| 204357 | } |
| 204358 | |
| 204359 | *paOut = aOut; |
| 204360 | return xRet; |
| 204361 | } |
| 204362 | |
| 204363 | static void fts3MIBufferSetGlobal(MatchinfoBuffer *p){ |
| 204364 | p->bGlobal = 1; |
| 204365 | memcpy(&p->aMatchinfo[2+p->nElem], &p->aMatchinfo[1], p->nElem*sizeof(u32)); |
| 204366 | } |
| 204367 | |
| 204368 | /* |
| 204369 | ** Free a MatchinfoBuffer object allocated using fts3MIBufferNew() |
| 204370 | */ |
| @@ -204775,11 +205537,11 @@ | |
| 204775 | if( nAppend<0 ){ |
| 204776 | nAppend = (int)strlen(zAppend); |
| 204777 | } |
| 204778 | |
| 204779 | /* If there is insufficient space allocated at StrBuffer.z, use realloc() |
| 204780 | ** to grow the buffer until so that it is big enough to accomadate the |
| 204781 | ** appended data. |
| 204782 | */ |
| 204783 | if( pStr->n+nAppend+1>=pStr->nAlloc ){ |
| 204784 | sqlite3_int64 nAlloc = pStr->nAlloc+(sqlite3_int64)nAppend+100; |
| 204785 | char *zNew = sqlite3_realloc64(pStr->z, nAlloc); |
| @@ -207150,11 +207912,11 @@ | |
| 207150 | ** |
| 207151 | ** When a match if found, the matching entry is moved to become the |
| 207152 | ** most-recently used entry if it isn't so already. |
| 207153 | ** |
| 207154 | ** The JsonParse object returned still belongs to the Cache and might |
| 207155 | ** be deleted at any moment. If the caller whants the JsonParse to |
| 207156 | ** linger, it needs to increment the nPJRef reference counter. |
| 207157 | */ |
| 207158 | static JsonParse *jsonCacheSearch( |
| 207159 | sqlite3_context *ctx, /* The SQL statement context holding the cache */ |
| 207160 | sqlite3_value *pArg /* Function argument containing SQL text */ |
| @@ -210195,11 +210957,11 @@ | |
| 210195 | ** |
| 210196 | ** This goes against all historical documentation about how the SQLite |
| 210197 | ** JSON functions were suppose to work. From the beginning, blob was |
| 210198 | ** reserved for expansion and a blob value should have raised an error. |
| 210199 | ** But it did not, due to a bug. And many applications came to depend |
| 210200 | ** upon this buggy behavior, espeically when using the CLI and reading |
| 210201 | ** JSON text using readfile(), which returns a blob. For this reason |
| 210202 | ** we will continue to support the bug moving forward. |
| 210203 | ** See for example https://sqlite.org/forum/forumpost/012136abd5292b8d |
| 210204 | */ |
| 210205 | } |
| @@ -212295,10 +213057,18 @@ | |
| 212295 | # define NEVER(X) ((X)?(assert(0),1):0) |
| 212296 | #else |
| 212297 | # define ALWAYS(X) (X) |
| 212298 | # define NEVER(X) (X) |
| 212299 | #endif |
| 212300 | #endif /* !defined(SQLITE_AMALGAMATION) */ |
| 212301 | |
| 212302 | /* Macro to check for 4-byte alignment. Only used inside of assert() */ |
| 212303 | #ifdef SQLITE_DEBUG |
| 212304 | # define FOUR_BYTE_ALIGNED(X) ((((char*)(X) - (char*)0) & 3)==0) |
| @@ -212615,13 +213385,17 @@ | |
| 212615 | struct RtreeMatchArg { |
| 212616 | u32 iSize; /* Size of this object */ |
| 212617 | RtreeGeomCallback cb; /* Info about the callback functions */ |
| 212618 | int nParam; /* Number of parameters to the SQL function */ |
| 212619 | sqlite3_value **apSqlParam; /* Original SQL parameter values */ |
| 212620 | RtreeDValue aParam[1]; /* Values for parameters to the SQL function */ |
| 212621 | }; |
| 212622 | |
| 212623 | #ifndef MAX |
| 212624 | # define MAX(x,y) ((x) < (y) ? (y) : (x)) |
| 212625 | #endif |
| 212626 | #ifndef MIN |
| 212627 | # define MIN(x,y) ((x) > (y) ? (y) : (x)) |
| @@ -214306,11 +215080,11 @@ | |
| 214306 | |
| 214307 | return rc; |
| 214308 | } |
| 214309 | |
| 214310 | /* |
| 214311 | ** Return the N-dimensional volumn of the cell stored in *p. |
| 214312 | */ |
| 214313 | static RtreeDValue cellArea(Rtree *pRtree, RtreeCell *p){ |
| 214314 | RtreeDValue area = (RtreeDValue)1; |
| 214315 | assert( pRtree->nDim>=1 && pRtree->nDim<=5 ); |
| 214316 | #ifndef SQLITE_RTREE_INT_ONLY |
| @@ -216072,11 +216846,11 @@ | |
| 216072 | } |
| 216073 | |
| 216074 | /* |
| 216075 | ** The second and subsequent arguments to this function are a printf() |
| 216076 | ** style format string and arguments. This function formats the string and |
| 216077 | ** appends it to the report being accumuated in pCheck. |
| 216078 | */ |
| 216079 | static void rtreeCheckAppendMsg(RtreeCheck *pCheck, const char *zFmt, ...){ |
| 216080 | va_list ap; |
| 216081 | va_start(ap, zFmt); |
| 216082 | if( pCheck->rc==SQLITE_OK && pCheck->nErr<RTREE_CHECK_MAX_ERROR ){ |
| @@ -217260,11 +218034,11 @@ | |
| 217260 | |
| 217261 | /* |
| 217262 | ** Determine if point (x0,y0) is beneath line segment (x1,y1)->(x2,y2). |
| 217263 | ** Returns: |
| 217264 | ** |
| 217265 | ** +2 x0,y0 is on the line segement |
| 217266 | ** |
| 217267 | ** +1 x0,y0 is beneath line segment |
| 217268 | ** |
| 217269 | ** 0 x0,y0 is not on or beneath the line segment or the line segment |
| 217270 | ** is vertical and x0,y0 is not on the line segment |
| @@ -217366,11 +218140,11 @@ | |
| 217366 | } |
| 217367 | sqlite3_free(p1); |
| 217368 | sqlite3_free(p2); |
| 217369 | } |
| 217370 | |
| 217371 | /* Objects used by the overlap algorihm. */ |
| 217372 | typedef struct GeoEvent GeoEvent; |
| 217373 | typedef struct GeoSegment GeoSegment; |
| 217374 | typedef struct GeoOverlap GeoOverlap; |
| 217375 | struct GeoEvent { |
| 217376 | double x; /* X coordinate at which event occurs */ |
| @@ -218413,12 +219187,11 @@ | |
| 218413 | RtreeGeomCallback *pGeomCtx = (RtreeGeomCallback *)sqlite3_user_data(ctx); |
| 218414 | RtreeMatchArg *pBlob; |
| 218415 | sqlite3_int64 nBlob; |
| 218416 | int memErr = 0; |
| 218417 | |
| 218418 | nBlob = sizeof(RtreeMatchArg) + (nArg-1)*sizeof(RtreeDValue) |
| 218419 | + nArg*sizeof(sqlite3_value*); |
| 218420 | pBlob = (RtreeMatchArg *)sqlite3_malloc64(nBlob); |
| 218421 | if( !pBlob ){ |
| 218422 | sqlite3_result_error_nomem(ctx); |
| 218423 | }else{ |
| 218424 | int i; |
| @@ -219509,11 +220282,11 @@ | |
| 219509 | ** to read from the original database snapshot. In other words, partially |
| 219510 | ** applied transactions are not visible to other clients. |
| 219511 | ** |
| 219512 | ** "RBU" stands for "Resumable Bulk Update". As in a large database update |
| 219513 | ** transmitted via a wireless network to a mobile device. A transaction |
| 219514 | ** applied using this extension is hence refered to as an "RBU update". |
| 219515 | ** |
| 219516 | ** |
| 219517 | ** LIMITATIONS |
| 219518 | ** |
| 219519 | ** An "RBU update" transaction is subject to the following limitations: |
| @@ -219806,11 +220579,11 @@ | |
| 219806 | ** sqlite3rbu_close() returns any value other than SQLITE_OK, the contents |
| 219807 | ** of the state tables within the state database are zeroed. This way, |
| 219808 | ** the next call to sqlite3rbu_vacuum() opens a handle that starts a |
| 219809 | ** new RBU vacuum operation. |
| 219810 | ** |
| 219811 | ** As with sqlite3rbu_open(), Zipvfs users should rever to the comment |
| 219812 | ** describing the sqlite3rbu_create_vfs() API function below for |
| 219813 | ** a description of the complications associated with using RBU with |
| 219814 | ** zipvfs databases. |
| 219815 | */ |
| 219816 | SQLITE_API sqlite3rbu *sqlite3rbu_vacuum( |
| @@ -219902,11 +220675,11 @@ | |
| 219902 | /* |
| 219903 | ** Close an RBU handle. |
| 219904 | ** |
| 219905 | ** If the RBU update has been completely applied, mark the RBU database |
| 219906 | ** as fully applied. Otherwise, assuming no error has occurred, save the |
| 219907 | ** current state of the RBU update appliation to the RBU database. |
| 219908 | ** |
| 219909 | ** If an error has already occurred as part of an sqlite3rbu_step() |
| 219910 | ** or sqlite3rbu_open() call, or if one occurs within this function, an |
| 219911 | ** SQLite error code is returned. Additionally, if pzErrmsg is not NULL, |
| 219912 | ** *pzErrmsg may be set to point to a buffer containing a utf-8 formatted |
| @@ -224828,11 +225601,11 @@ | |
| 224828 | int rc; |
| 224829 | rc = p->pReal->pMethods->xFileSize(p->pReal, pSize); |
| 224830 | |
| 224831 | /* If this is an RBU vacuum operation and this is the target database, |
| 224832 | ** pretend that it has at least one page. Otherwise, SQLite will not |
| 224833 | ** check for the existance of a *-wal file. rbuVfsRead() contains |
| 224834 | ** similar logic. */ |
| 224835 | if( rc==SQLITE_OK && *pSize==0 |
| 224836 | && p->pRbu && rbuIsVacuum(p->pRbu) |
| 224837 | && (p->openFlags & SQLITE_OPEN_MAIN_DB) |
| 224838 | ){ |
| @@ -228058,11 +228831,11 @@ | |
| 228058 | } |
| 228059 | |
| 228060 | /* |
| 228061 | ** This function is called to initialize the SessionTable.nCol, azCol[] |
| 228062 | ** abPK[] and azDflt[] members of SessionTable object pTab. If these |
| 228063 | ** fields are already initilialized, this function is a no-op. |
| 228064 | ** |
| 228065 | ** If an error occurs, an error code is stored in sqlite3_session.rc and |
| 228066 | ** non-zero returned. Or, if no error occurs but the table has no primary |
| 228067 | ** key, sqlite3_session.rc is left set to SQLITE_OK and non-zero returned to |
| 228068 | ** indicate that updates on this table should be ignored. SessionTable.abPK |
| @@ -229881,11 +230654,11 @@ | |
| 229881 | int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */ |
| 229882 | void **ppChangeset /* OUT: Buffer containing changeset */ |
| 229883 | ){ |
| 229884 | sqlite3 *db = pSession->db; /* Source database handle */ |
| 229885 | SessionTable *pTab; /* Used to iterate through attached tables */ |
| 229886 | SessionBuffer buf = {0,0,0}; /* Buffer in which to accumlate changeset */ |
| 229887 | int rc; /* Return code */ |
| 229888 | |
| 229889 | assert( xOutput==0 || (pnChangeset==0 && ppChangeset==0) ); |
| 229890 | assert( xOutput!=0 || (pnChangeset!=0 && ppChangeset!=0) ); |
| 229891 | |
| @@ -234315,10 +235088,22 @@ | |
| 234315 | # define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&3)==0) |
| 234316 | #else |
| 234317 | # define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&7)==0) |
| 234318 | #endif |
| 234319 | |
| 234320 | #endif |
| 234321 | |
| 234322 | /* Truncate very long tokens to this many bytes. Hard limit is |
| 234323 | ** (65536-1-1-4-9)==65521 bytes. The limiting factor is the 16-bit offset |
| 234324 | ** field that occurs at the start of each leaf page (see fts5_index.c). */ |
| @@ -234387,14 +235172,15 @@ | |
| 234387 | ** |
| 234388 | ** This object is used by fts5_expr.c and fts5_index.c. |
| 234389 | */ |
| 234390 | struct Fts5Colset { |
| 234391 | int nCol; |
| 234392 | int aiCol[1]; |
| 234393 | }; |
| 234394 | |
| 234395 | |
| 234396 | |
| 234397 | /************************************************************************** |
| 234398 | ** Interface to code in fts5_config.c. fts5_config.c contains contains code |
| 234399 | ** to parse the arguments passed to the CREATE VIRTUAL TABLE statement. |
| 234400 | */ |
| @@ -235219,11 +236005,11 @@ | |
| 235219 | ************************************************************************* |
| 235220 | ** Driver template for the LEMON parser generator. |
| 235221 | ** |
| 235222 | ** The "lemon" program processes an LALR(1) input grammar file, then uses |
| 235223 | ** this template to construct a parser. The "lemon" program inserts text |
| 235224 | ** at each "%%" line. Also, any "P-a-r-s-e" identifer prefix (without the |
| 235225 | ** interstitial "-" characters) contained in this template is changed into |
| 235226 | ** the value of the %name directive from the grammar. Otherwise, the content |
| 235227 | ** of this template is copied straight through into the generate parser |
| 235228 | ** source file. |
| 235229 | ** |
| @@ -237373,11 +238159,11 @@ | |
| 237373 | ** where "N" is the total number of documents in the set and nHit |
| 237374 | ** is the number that contain at least one instance of the phrase |
| 237375 | ** under consideration. |
| 237376 | ** |
| 237377 | ** The problem with this is that if (N < 2*nHit), the IDF is |
| 237378 | ** negative. Which is undesirable. So the mimimum allowable IDF is |
| 237379 | ** (1e-6) - roughly the same as a term that appears in just over |
| 237380 | ** half of set of 5,000,000 documents. */ |
| 237381 | double idf = log( (nRow - nHit + 0.5) / (nHit + 0.5) ); |
| 237382 | if( idf<=0.0 ) idf = 1e-6; |
| 237383 | p->aIDF[i] = idf; |
| @@ -237836,11 +238622,11 @@ | |
| 237836 | ** |
| 237837 | ** * All non-ASCII characters, |
| 237838 | ** * The 52 upper and lower case ASCII characters, and |
| 237839 | ** * The 10 integer ASCII characters. |
| 237840 | ** * The underscore character "_" (0x5F). |
| 237841 | ** * The unicode "subsitute" character (0x1A). |
| 237842 | */ |
| 237843 | static int sqlite3Fts5IsBareword(char t){ |
| 237844 | u8 aBareword[128] = { |
| 237845 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 .. 0x0F */ |
| 237846 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* 0x10 .. 0x1F */ |
| @@ -239154,12 +239940,16 @@ | |
| 239154 | Fts5ExprNearset *pNear; /* For FTS5_STRING - cluster of phrases */ |
| 239155 | |
| 239156 | /* Child nodes. For a NOT node, this array always contains 2 entries. For |
| 239157 | ** AND or OR nodes, it contains 2 or more entries. */ |
| 239158 | int nChild; /* Number of child nodes */ |
| 239159 | Fts5ExprNode *apChild[1]; /* Array of child nodes */ |
| 239160 | }; |
| 239161 | |
| 239162 | #define Fts5NodeIsString(p) ((p)->eType==FTS5_TERM || (p)->eType==FTS5_STRING) |
| 239163 | |
| 239164 | /* |
| 239165 | ** Invoke the xNext method of an Fts5ExprNode object. This macro should be |
| @@ -239187,24 +239977,31 @@ | |
| 239187 | */ |
| 239188 | struct Fts5ExprPhrase { |
| 239189 | Fts5ExprNode *pNode; /* FTS5_STRING node this phrase is part of */ |
| 239190 | Fts5Buffer poslist; /* Current position list */ |
| 239191 | int nTerm; /* Number of entries in aTerm[] */ |
| 239192 | Fts5ExprTerm aTerm[1]; /* Terms that make up this phrase */ |
| 239193 | }; |
| 239194 | |
| 239195 | /* |
| 239196 | ** One or more phrases that must appear within a certain token distance of |
| 239197 | ** each other within each matching document. |
| 239198 | */ |
| 239199 | struct Fts5ExprNearset { |
| 239200 | int nNear; /* NEAR parameter */ |
| 239201 | Fts5Colset *pColset; /* Columns to search (NULL -> all columns) */ |
| 239202 | int nPhrase; /* Number of entries in aPhrase[] array */ |
| 239203 | Fts5ExprPhrase *apPhrase[1]; /* Array of phrase pointers */ |
| 239204 | }; |
| 239205 | |
| 239206 | |
| 239207 | /* |
| 239208 | ** Parse context. |
| 239209 | */ |
| 239210 | struct Fts5Parse { |
| @@ -239360,11 +240157,11 @@ | |
| 239360 | assert_expr_depth_ok(sParse.rc, sParse.pExpr); |
| 239361 | |
| 239362 | /* If the LHS of the MATCH expression was a user column, apply the |
| 239363 | ** implicit column-filter. */ |
| 239364 | if( sParse.rc==SQLITE_OK && iCol<pConfig->nCol ){ |
| 239365 | int n = sizeof(Fts5Colset); |
| 239366 | Fts5Colset *pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&sParse.rc, n); |
| 239367 | if( pColset ){ |
| 239368 | pColset->nCol = 1; |
| 239369 | pColset->aiCol[0] = iCol; |
| 239370 | sqlite3Fts5ParseSetColset(&sParse, sParse.pExpr, pColset); |
| @@ -240718,11 +241515,11 @@ | |
| 240718 | Fts5ExprNearset *pRet = 0; |
| 240719 | |
| 240720 | if( pParse->rc==SQLITE_OK ){ |
| 240721 | if( pNear==0 ){ |
| 240722 | sqlite3_int64 nByte; |
| 240723 | nByte = sizeof(Fts5ExprNearset) + SZALLOC * sizeof(Fts5ExprPhrase*); |
| 240724 | pRet = sqlite3_malloc64(nByte); |
| 240725 | if( pRet==0 ){ |
| 240726 | pParse->rc = SQLITE_NOMEM; |
| 240727 | }else{ |
| 240728 | memset(pRet, 0, (size_t)nByte); |
| @@ -240729,11 +241526,11 @@ | |
| 240729 | } |
| 240730 | }else if( (pNear->nPhrase % SZALLOC)==0 ){ |
| 240731 | int nNew = pNear->nPhrase + SZALLOC; |
| 240732 | sqlite3_int64 nByte; |
| 240733 | |
| 240734 | nByte = sizeof(Fts5ExprNearset) + nNew * sizeof(Fts5ExprPhrase*); |
| 240735 | pRet = (Fts5ExprNearset*)sqlite3_realloc64(pNear, nByte); |
| 240736 | if( pRet==0 ){ |
| 240737 | pParse->rc = SQLITE_NOMEM; |
| 240738 | } |
| 240739 | }else{ |
| @@ -240820,16 +241617,16 @@ | |
| 240820 | if( pPhrase==0 || (pPhrase->nTerm % SZALLOC)==0 ){ |
| 240821 | Fts5ExprPhrase *pNew; |
| 240822 | int nNew = SZALLOC + (pPhrase ? pPhrase->nTerm : 0); |
| 240823 | |
| 240824 | pNew = (Fts5ExprPhrase*)sqlite3_realloc64(pPhrase, |
| 240825 | sizeof(Fts5ExprPhrase) + sizeof(Fts5ExprTerm) * nNew |
| 240826 | ); |
| 240827 | if( pNew==0 ){ |
| 240828 | rc = SQLITE_NOMEM; |
| 240829 | }else{ |
| 240830 | if( pPhrase==0 ) memset(pNew, 0, sizeof(Fts5ExprPhrase)); |
| 240831 | pCtx->pPhrase = pPhrase = pNew; |
| 240832 | pNew->nTerm = nNew - SZALLOC; |
| 240833 | } |
| 240834 | } |
| 240835 | |
| @@ -240933,11 +241730,11 @@ | |
| 240933 | } |
| 240934 | |
| 240935 | if( sCtx.pPhrase==0 ){ |
| 240936 | /* This happens when parsing a token or quoted phrase that contains |
| 240937 | ** no token characters at all. (e.g ... MATCH '""'). */ |
| 240938 | sCtx.pPhrase = sqlite3Fts5MallocZero(&pParse->rc, sizeof(Fts5ExprPhrase)); |
| 240939 | }else if( sCtx.pPhrase->nTerm ){ |
| 240940 | sCtx.pPhrase->aTerm[sCtx.pPhrase->nTerm-1].bPrefix = (u8)bPrefix; |
| 240941 | } |
| 240942 | assert( pParse->apPhrase!=0 ); |
| 240943 | pParse->apPhrase[pParse->nPhrase-1] = sCtx.pPhrase; |
| @@ -240968,23 +241765,22 @@ | |
| 240968 | if( rc==SQLITE_OK ){ |
| 240969 | pNew->apExprPhrase = (Fts5ExprPhrase**)sqlite3Fts5MallocZero(&rc, |
| 240970 | sizeof(Fts5ExprPhrase*)); |
| 240971 | } |
| 240972 | if( rc==SQLITE_OK ){ |
| 240973 | pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&rc, |
| 240974 | sizeof(Fts5ExprNode)); |
| 240975 | } |
| 240976 | if( rc==SQLITE_OK ){ |
| 240977 | pNew->pRoot->pNear = (Fts5ExprNearset*)sqlite3Fts5MallocZero(&rc, |
| 240978 | sizeof(Fts5ExprNearset) + sizeof(Fts5ExprPhrase*)); |
| 240979 | } |
| 240980 | if( rc==SQLITE_OK && ALWAYS(pOrig!=0) ){ |
| 240981 | Fts5Colset *pColsetOrig = pOrig->pNode->pNear->pColset; |
| 240982 | if( pColsetOrig ){ |
| 240983 | sqlite3_int64 nByte; |
| 240984 | Fts5Colset *pColset; |
| 240985 | nByte = sizeof(Fts5Colset) + (pColsetOrig->nCol-1) * sizeof(int); |
| 240986 | pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&rc, nByte); |
| 240987 | if( pColset ){ |
| 240988 | memcpy(pColset, pColsetOrig, (size_t)nByte); |
| 240989 | } |
| 240990 | pNew->pRoot->pNear->pColset = pColset; |
| @@ -241008,11 +241804,11 @@ | |
| 241008 | } |
| 241009 | } |
| 241010 | }else{ |
| 241011 | /* This happens when parsing a token or quoted phrase that contains |
| 241012 | ** no token characters at all. (e.g ... MATCH '""'). */ |
| 241013 | sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprPhrase)); |
| 241014 | } |
| 241015 | } |
| 241016 | |
| 241017 | if( rc==SQLITE_OK && ALWAYS(sCtx.pPhrase) ){ |
| 241018 | /* All the allocations succeeded. Put the expression object together. */ |
| @@ -241102,11 +241898,11 @@ | |
| 241102 | Fts5Colset *pNew; /* New colset object to return */ |
| 241103 | |
| 241104 | assert( pParse->rc==SQLITE_OK ); |
| 241105 | assert( iCol>=0 && iCol<pParse->pConfig->nCol ); |
| 241106 | |
| 241107 | pNew = sqlite3_realloc64(p, sizeof(Fts5Colset) + sizeof(int)*nCol); |
| 241108 | if( pNew==0 ){ |
| 241109 | pParse->rc = SQLITE_NOMEM; |
| 241110 | }else{ |
| 241111 | int *aiCol = pNew->aiCol; |
| 241112 | int i, j; |
| @@ -241137,11 +241933,11 @@ | |
| 241137 | static Fts5Colset *sqlite3Fts5ParseColsetInvert(Fts5Parse *pParse, Fts5Colset *p){ |
| 241138 | Fts5Colset *pRet; |
| 241139 | int nCol = pParse->pConfig->nCol; |
| 241140 | |
| 241141 | pRet = (Fts5Colset*)sqlite3Fts5MallocZero(&pParse->rc, |
| 241142 | sizeof(Fts5Colset) + sizeof(int)*nCol |
| 241143 | ); |
| 241144 | if( pRet ){ |
| 241145 | int i; |
| 241146 | int iOld = 0; |
| 241147 | for(i=0; i<nCol; i++){ |
| @@ -241198,11 +241994,11 @@ | |
| 241198 | ** fails, (*pRc) is set to SQLITE_NOMEM and NULL is returned. |
| 241199 | */ |
| 241200 | static Fts5Colset *fts5CloneColset(int *pRc, Fts5Colset *pOrig){ |
| 241201 | Fts5Colset *pRet; |
| 241202 | if( pOrig ){ |
| 241203 | sqlite3_int64 nByte = sizeof(Fts5Colset) + (pOrig->nCol-1) * sizeof(int); |
| 241204 | pRet = (Fts5Colset*)sqlite3Fts5MallocZero(pRc, nByte); |
| 241205 | if( pRet ){ |
| 241206 | memcpy(pRet, pOrig, (size_t)nByte); |
| 241207 | } |
| 241208 | }else{ |
| @@ -241366,21 +242162,21 @@ | |
| 241366 | Fts5ExprNode *pRet; |
| 241367 | |
| 241368 | assert( pNear->nPhrase==1 ); |
| 241369 | assert( pParse->bPhraseToAnd ); |
| 241370 | |
| 241371 | nByte = sizeof(Fts5ExprNode) + nTerm*sizeof(Fts5ExprNode*); |
| 241372 | pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte); |
| 241373 | if( pRet ){ |
| 241374 | pRet->eType = FTS5_AND; |
| 241375 | pRet->nChild = nTerm; |
| 241376 | pRet->iHeight = 1; |
| 241377 | fts5ExprAssignXNext(pRet); |
| 241378 | pParse->nPhrase--; |
| 241379 | for(ii=0; ii<nTerm; ii++){ |
| 241380 | Fts5ExprPhrase *pPhrase = (Fts5ExprPhrase*)sqlite3Fts5MallocZero( |
| 241381 | &pParse->rc, sizeof(Fts5ExprPhrase) |
| 241382 | ); |
| 241383 | if( pPhrase ){ |
| 241384 | if( parseGrowPhraseArray(pParse) ){ |
| 241385 | fts5ExprPhraseFree(pPhrase); |
| 241386 | }else{ |
| @@ -241445,11 +242241,11 @@ | |
| 241445 | nChild = 2; |
| 241446 | if( pLeft->eType==eType ) nChild += pLeft->nChild-1; |
| 241447 | if( pRight->eType==eType ) nChild += pRight->nChild-1; |
| 241448 | } |
| 241449 | |
| 241450 | nByte = sizeof(Fts5ExprNode) + sizeof(Fts5ExprNode*)*(nChild-1); |
| 241451 | pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte); |
| 241452 | |
| 241453 | if( pRet ){ |
| 241454 | pRet->eType = eType; |
| 241455 | pRet->pNear = pNear; |
| @@ -242320,11 +243116,11 @@ | |
| 242320 | } |
| 242321 | return rc; |
| 242322 | } |
| 242323 | |
| 242324 | /* |
| 242325 | ** Clear the token mappings for all Fts5IndexIter objects mannaged by |
| 242326 | ** the expression passed as the only argument. |
| 242327 | */ |
| 242328 | static void sqlite3Fts5ExprClearTokens(Fts5Expr *pExpr){ |
| 242329 | int ii; |
| 242330 | for(ii=0; ii<pExpr->nPhrase; ii++){ |
| @@ -242355,11 +243151,11 @@ | |
| 242355 | |
| 242356 | typedef struct Fts5HashEntry Fts5HashEntry; |
| 242357 | |
| 242358 | /* |
| 242359 | ** This file contains the implementation of an in-memory hash table used |
| 242360 | ** to accumuluate "term -> doclist" content before it is flused to a level-0 |
| 242361 | ** segment. |
| 242362 | */ |
| 242363 | |
| 242364 | |
| 242365 | struct Fts5Hash { |
| @@ -242412,11 +243208,11 @@ | |
| 242412 | int iPos; /* Position of last value written */ |
| 242413 | i64 iRowid; /* Rowid of last value written */ |
| 242414 | }; |
| 242415 | |
| 242416 | /* |
| 242417 | ** Eqivalent to: |
| 242418 | ** |
| 242419 | ** char *fts5EntryKey(Fts5HashEntry *pEntry){ return zKey; } |
| 242420 | */ |
| 242421 | #define fts5EntryKey(p) ( ((char *)(&(p)[1])) ) |
| 242422 | |
| @@ -243348,13 +244144,17 @@ | |
| 243348 | int nRef; /* Object reference count */ |
| 243349 | u64 nWriteCounter; /* Total leaves written to level 0 */ |
| 243350 | u64 nOriginCntr; /* Origin value for next top-level segment */ |
| 243351 | int nSegment; /* Total segments in this structure */ |
| 243352 | int nLevel; /* Number of levels in this index */ |
| 243353 | Fts5StructureLevel aLevel[1]; /* Array of nLevel level objects */ |
| 243354 | }; |
| 243355 | |
| 243356 | /* |
| 243357 | ** An object of type Fts5SegWriter is used to write to segments. |
| 243358 | */ |
| 243359 | struct Fts5PageWriter { |
| 243360 | int pgno; /* Page number for this page */ |
| @@ -243480,14 +244280,18 @@ | |
| 243480 | |
| 243481 | /* |
| 243482 | ** Array of tombstone pages. Reference counted. |
| 243483 | */ |
| 243484 | struct Fts5TombstoneArray { |
| 243485 | int nRef; /* Number of pointers to this object */ |
| 243486 | int nTombstone; |
| 243487 | Fts5Data *apTombstone[1]; /* Array of tombstone pages */ |
| 243488 | }; |
| 243489 | |
| 243490 | /* |
| 243491 | ** Argument is a pointer to an Fts5Data structure that contains a |
| 243492 | ** leaf page. |
| 243493 | */ |
| @@ -243553,12 +244357,15 @@ | |
| 243553 | int bRev; /* True to iterate in reverse order */ |
| 243554 | u8 bSkipEmpty; /* True to skip deleted entries */ |
| 243555 | |
| 243556 | i64 iSwitchRowid; /* Firstest rowid of other than aFirst[1] */ |
| 243557 | Fts5CResult *aFirst; /* Current merge state (see above) */ |
| 243558 | Fts5SegIter aSeg[1]; /* Array of segment iterators */ |
| 243559 | }; |
| 243560 | |
| 243561 | /* |
| 243562 | ** An instance of the following type is used to iterate through the contents |
| 243563 | ** of a doclist-index record. |
| 243564 | ** |
| @@ -243582,12 +244389,16 @@ | |
| 243582 | i64 iRowid; /* First rowid on leaf iLeafPgno */ |
| 243583 | }; |
| 243584 | struct Fts5DlidxIter { |
| 243585 | int nLvl; |
| 243586 | int iSegid; |
| 243587 | Fts5DlidxLvl aLvl[1]; |
| 243588 | }; |
| 243589 | |
| 243590 | static void fts5PutU16(u8 *aOut, u16 iVal){ |
| 243591 | aOut[0] = (iVal>>8); |
| 243592 | aOut[1] = (iVal&0xFF); |
| 243593 | } |
| @@ -243952,11 +244763,11 @@ | |
| 243952 | ** an error occurs, (*pRc) is set to an SQLite error code before returning. |
| 243953 | */ |
| 243954 | static void fts5StructureMakeWritable(int *pRc, Fts5Structure **pp){ |
| 243955 | Fts5Structure *p = *pp; |
| 243956 | if( *pRc==SQLITE_OK && p->nRef>1 ){ |
| 243957 | i64 nByte = sizeof(Fts5Structure)+(p->nLevel-1)*sizeof(Fts5StructureLevel); |
| 243958 | Fts5Structure *pNew; |
| 243959 | pNew = (Fts5Structure*)sqlite3Fts5MallocZero(pRc, nByte); |
| 243960 | if( pNew ){ |
| 243961 | int i; |
| 243962 | memcpy(pNew, p, nByte); |
| @@ -244026,14 +244837,11 @@ | |
| 244026 | if( nLevel>FTS5_MAX_SEGMENT || nLevel<0 |
| 244027 | || nSegment>FTS5_MAX_SEGMENT || nSegment<0 |
| 244028 | ){ |
| 244029 | return FTS5_CORRUPT; |
| 244030 | } |
| 244031 | nByte = ( |
| 244032 | sizeof(Fts5Structure) + /* Main structure */ |
| 244033 | sizeof(Fts5StructureLevel) * (nLevel-1) /* aLevel[] array */ |
| 244034 | ); |
| 244035 | pRet = (Fts5Structure*)sqlite3Fts5MallocZero(&rc, nByte); |
| 244036 | |
| 244037 | if( pRet ){ |
| 244038 | pRet->nRef = 1; |
| 244039 | pRet->nLevel = nLevel; |
| @@ -244109,14 +244917,11 @@ | |
| 244109 | fts5StructureMakeWritable(pRc, ppStruct); |
| 244110 | assert( (ppStruct!=0 && (*ppStruct)!=0) || (*pRc)!=SQLITE_OK ); |
| 244111 | if( *pRc==SQLITE_OK ){ |
| 244112 | Fts5Structure *pStruct = *ppStruct; |
| 244113 | int nLevel = pStruct->nLevel; |
| 244114 | sqlite3_int64 nByte = ( |
| 244115 | sizeof(Fts5Structure) + /* Main structure */ |
| 244116 | sizeof(Fts5StructureLevel) * (nLevel+1) /* aLevel[] array */ |
| 244117 | ); |
| 244118 | |
| 244119 | pStruct = sqlite3_realloc64(pStruct, nByte); |
| 244120 | if( pStruct ){ |
| 244121 | memset(&pStruct->aLevel[nLevel], 0, sizeof(Fts5StructureLevel)); |
| 244122 | pStruct->nLevel++; |
| @@ -244651,11 +245456,11 @@ | |
| 244651 | Fts5DlidxIter *pIter = 0; |
| 244652 | int i; |
| 244653 | int bDone = 0; |
| 244654 | |
| 244655 | for(i=0; p->rc==SQLITE_OK && bDone==0; i++){ |
| 244656 | sqlite3_int64 nByte = sizeof(Fts5DlidxIter) + i * sizeof(Fts5DlidxLvl); |
| 244657 | Fts5DlidxIter *pNew; |
| 244658 | |
| 244659 | pNew = (Fts5DlidxIter*)sqlite3_realloc64(pIter, nByte); |
| 244660 | if( pNew==0 ){ |
| 244661 | p->rc = SQLITE_NOMEM; |
| @@ -244869,11 +245674,11 @@ | |
| 244869 | ** leave an error in the Fts5Index object. |
| 244870 | */ |
| 244871 | static void fts5SegIterAllocTombstone(Fts5Index *p, Fts5SegIter *pIter){ |
| 244872 | const int nTomb = pIter->pSeg->nPgTombstone; |
| 244873 | if( nTomb>0 ){ |
| 244874 | int nByte = nTomb * sizeof(Fts5Data*) + sizeof(Fts5TombstoneArray); |
| 244875 | Fts5TombstoneArray *pNew; |
| 244876 | pNew = (Fts5TombstoneArray*)sqlite3Fts5MallocZero(&p->rc, nByte); |
| 244877 | if( pNew ){ |
| 244878 | pNew->nTombstone = nTomb; |
| 244879 | pNew->nRef = 1; |
| @@ -246330,12 +247135,11 @@ | |
| 246330 | Fts5Iter *pNew; |
| 246331 | i64 nSlot; /* Power of two >= nSeg */ |
| 246332 | |
| 246333 | for(nSlot=2; nSlot<nSeg; nSlot=nSlot*2); |
| 246334 | pNew = fts5IdxMalloc(p, |
| 246335 | sizeof(Fts5Iter) + /* pNew */ |
| 246336 | sizeof(Fts5SegIter) * (nSlot-1) + /* pNew->aSeg[] */ |
| 246337 | sizeof(Fts5CResult) * nSlot /* pNew->aFirst[] */ |
| 246338 | ); |
| 246339 | if( pNew ){ |
| 246340 | pNew->nSeg = nSlot; |
| 246341 | pNew->aFirst = (Fts5CResult*)&pNew->aSeg[nSlot]; |
| @@ -248697,11 +249501,11 @@ | |
| 248697 | static Fts5Structure *fts5IndexOptimizeStruct( |
| 248698 | Fts5Index *p, |
| 248699 | Fts5Structure *pStruct |
| 248700 | ){ |
| 248701 | Fts5Structure *pNew = 0; |
| 248702 | sqlite3_int64 nByte = sizeof(Fts5Structure); |
| 248703 | int nSeg = pStruct->nSegment; |
| 248704 | int i; |
| 248705 | |
| 248706 | /* Figure out if this structure requires optimization. A structure does |
| 248707 | ** not require optimization if either: |
| @@ -248727,10 +249531,11 @@ | |
| 248727 | } |
| 248728 | assert( pStruct->aLevel[i].nMerge<=nThis ); |
| 248729 | } |
| 248730 | |
| 248731 | nByte += (((i64)pStruct->nLevel)+1) * sizeof(Fts5StructureLevel); |
| 248732 | pNew = (Fts5Structure*)sqlite3Fts5MallocZero(&p->rc, nByte); |
| 248733 | |
| 248734 | if( pNew ){ |
| 248735 | Fts5StructureLevel *pLvl; |
| 248736 | nByte = nSeg * sizeof(Fts5StructureSegment); |
| @@ -249303,12 +250108,16 @@ | |
| 249303 | /* The following are used for other full-token tokendata queries only. */ |
| 249304 | int nIter; |
| 249305 | int nIterAlloc; |
| 249306 | Fts5PoslistReader *aPoslistReader; |
| 249307 | int *aPoslistToIter; |
| 249308 | Fts5Iter *apIter[1]; |
| 249309 | }; |
| 249310 | |
| 249311 | /* |
| 249312 | ** The two input arrays - a1[] and a2[] - are in sorted order. This function |
| 249313 | ** merges the two arrays together and writes the result to output array |
| 249314 | ** aOut[]. aOut[] is guaranteed to be large enough to hold the result. |
| @@ -249377,11 +250186,11 @@ | |
| 249377 | } |
| 249378 | |
| 249379 | /* |
| 249380 | ** Sort the contents of the pT->aMap[] array. |
| 249381 | ** |
| 249382 | ** The sorting algorithm requries a malloc(). If this fails, an error code |
| 249383 | ** is left in Fts5Index.rc before returning. |
| 249384 | */ |
| 249385 | static void fts5TokendataIterSortMap(Fts5Index *p, Fts5TokenDataIter *pT){ |
| 249386 | Fts5TokenDataMap *aTmp = 0; |
| 249387 | int nByte = pT->nMap * sizeof(Fts5TokenDataMap); |
| @@ -249568,11 +250377,11 @@ | |
| 249568 | if( iIdx==0 |
| 249569 | && p->pConfig->eDetail==FTS5_DETAIL_FULL |
| 249570 | && p->pConfig->bPrefixInsttoken |
| 249571 | ){ |
| 249572 | s.pTokendata = &s2; |
| 249573 | s2.pT = (Fts5TokenDataIter*)fts5IdxMalloc(p, sizeof(*s2.pT)); |
| 249574 | } |
| 249575 | |
| 249576 | if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){ |
| 249577 | s.xMerge = fts5MergeRowidLists; |
| 249578 | s.xAppend = fts5AppendRowid; |
| @@ -249696,19 +250505,21 @@ | |
| 249696 | ** The %_data table is completely empty when this function is called. This |
| 249697 | ** function populates it with the initial structure objects for each index, |
| 249698 | ** and the initial version of the "averages" record (a zero-byte blob). |
| 249699 | */ |
| 249700 | static int sqlite3Fts5IndexReinit(Fts5Index *p){ |
| 249701 | Fts5Structure s; |
| 249702 | fts5StructureInvalidate(p); |
| 249703 | fts5IndexDiscardData(p); |
| 249704 | memset(&s, 0, sizeof(Fts5Structure)); |
| 249705 | if( p->pConfig->bContentlessDelete ){ |
| 249706 | s.nOriginCntr = 1; |
| 249707 | } |
| 249708 | fts5DataWrite(p, FTS5_AVERAGES_ROWID, (const u8*)"", 0); |
| 249709 | fts5StructureWrite(p, &s); |
| 249710 | return fts5IndexReturn(p); |
| 249711 | } |
| 249712 | |
| 249713 | /* |
| 249714 | ** Open a new Fts5Index handle. If the bCreate argument is true, create |
| @@ -249912,11 +250723,11 @@ | |
| 249912 | Fts5TokenDataIter *pRet = pIn; |
| 249913 | |
| 249914 | if( p->rc==SQLITE_OK ){ |
| 249915 | if( pIn==0 || pIn->nIter==pIn->nIterAlloc ){ |
| 249916 | int nAlloc = pIn ? pIn->nIterAlloc*2 : 16; |
| 249917 | int nByte = nAlloc * sizeof(Fts5Iter*) + sizeof(Fts5TokenDataIter); |
| 249918 | Fts5TokenDataIter *pNew = (Fts5TokenDataIter*)sqlite3_realloc(pIn, nByte); |
| 249919 | |
| 249920 | if( pNew==0 ){ |
| 249921 | p->rc = SQLITE_NOMEM; |
| 249922 | }else{ |
| @@ -250428,11 +251239,12 @@ | |
| 250428 | |
| 250429 | memset(&ctx, 0, sizeof(ctx)); |
| 250430 | |
| 250431 | fts5BufferGrow(&p->rc, &token, nToken+1); |
| 250432 | assert( token.p!=0 || p->rc!=SQLITE_OK ); |
| 250433 | ctx.pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, sizeof(*ctx.pT)); |
| 250434 | |
| 250435 | if( p->rc==SQLITE_OK ){ |
| 250436 | |
| 250437 | /* Fill in the token prefix to search for */ |
| 250438 | token.p[0] = FTS5_MAIN_PREFIX; |
| @@ -250559,11 +251371,12 @@ | |
| 250559 | assert( p->pConfig->eDetail!=FTS5_DETAIL_FULL ); |
| 250560 | assert( pIter->pTokenDataIter || pIter->nSeg>0 ); |
| 250561 | if( pIter->nSeg>0 ){ |
| 250562 | /* This is a prefix term iterator. */ |
| 250563 | if( pT==0 ){ |
| 250564 | pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, sizeof(*pT)); |
| 250565 | pIter->pTokenDataIter = pT; |
| 250566 | } |
| 250567 | if( pT ){ |
| 250568 | fts5TokendataIterAppendMap(p, pT, pT->terms.n, nToken, iRowid, iPos); |
| 250569 | fts5BufferAppendBlob(&p->rc, &pT->terms, nToken, (const u8*)pToken); |
| @@ -251593,11 +252406,11 @@ | |
| 251593 | } |
| 251594 | #endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ |
| 251595 | |
| 251596 | #if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) |
| 251597 | static void fts5DebugRowid(int *pRc, Fts5Buffer *pBuf, i64 iKey){ |
| 251598 | int iSegid, iHeight, iPgno, bDlidx, bTomb; /* Rowid compenents */ |
| 251599 | fts5DecodeRowid(iKey, &bTomb, &iSegid, &bDlidx, &iHeight, &iPgno); |
| 251600 | |
| 251601 | if( iSegid==0 ){ |
| 251602 | if( iKey==FTS5_AVERAGES_ROWID ){ |
| 251603 | sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "{averages} "); |
| @@ -252554,13 +253367,15 @@ | |
| 252554 | struct Fts5Sorter { |
| 252555 | sqlite3_stmt *pStmt; |
| 252556 | i64 iRowid; /* Current rowid */ |
| 252557 | const u8 *aPoslist; /* Position lists for current row */ |
| 252558 | int nIdx; /* Number of entries in aIdx[] */ |
| 252559 | int aIdx[1]; /* Offsets into aPoslist for current row */ |
| 252560 | }; |
| 252561 | |
| 252562 | |
| 252563 | /* |
| 252564 | ** Virtual-table cursor object. |
| 252565 | ** |
| 252566 | ** iSpecial: |
| @@ -253434,11 +254249,11 @@ | |
| 253434 | int rc; |
| 253435 | const char *zRank = pCsr->zRank; |
| 253436 | const char *zRankArgs = pCsr->zRankArgs; |
| 253437 | |
| 253438 | nPhrase = sqlite3Fts5ExprPhraseCount(pCsr->pExpr); |
| 253439 | nByte = sizeof(Fts5Sorter) + sizeof(int) * (nPhrase-1); |
| 253440 | pSorter = (Fts5Sorter*)sqlite3_malloc64(nByte); |
| 253441 | if( pSorter==0 ) return SQLITE_NOMEM; |
| 253442 | memset(pSorter, 0, (size_t)nByte); |
| 253443 | pSorter->nIdx = nPhrase; |
| 253444 | |
| @@ -255960,11 +256775,11 @@ | |
| 255960 | int nArg, /* Number of args */ |
| 255961 | sqlite3_value **apUnused /* Function arguments */ |
| 255962 | ){ |
| 255963 | assert( nArg==0 ); |
| 255964 | UNUSED_PARAM2(nArg, apUnused); |
| 255965 | sqlite3_result_text(pCtx, "fts5: 2025-02-18 01:16:26 57caa3136d1bfca06e4f2285734a4977b8d3fa1f75bf87453b975867e9de38fc", -1, SQLITE_TRANSIENT); |
| 255966 | } |
| 255967 | |
| 255968 | /* |
| 255969 | ** Implementation of fts5_locale(LOCALE, TEXT) function. |
| 255970 | ** |
| @@ -256185,12 +257000,12 @@ | |
| 256185 | /* If SQLITE_FTS5_ENABLE_TEST_MI is defined, assume that the file |
| 256186 | ** fts5_test_mi.c is compiled and linked into the executable. And call |
| 256187 | ** its entry point to enable the matchinfo() demo. */ |
| 256188 | #ifdef SQLITE_FTS5_ENABLE_TEST_MI |
| 256189 | if( rc==SQLITE_OK ){ |
| 256190 | extern int sqlite3Fts5TestRegisterMatchinfo(sqlite3*); |
| 256191 | rc = sqlite3Fts5TestRegisterMatchinfo(db); |
| 256192 | } |
| 256193 | #endif |
| 256194 | |
| 256195 | return rc; |
| 256196 | } |
| 256197 |
| --- extsrc/sqlite3.c | |
| +++ extsrc/sqlite3.c | |
| @@ -16,11 +16,11 @@ | |
| 16 | ** if you want a wrapper to interface SQLite with your choice of programming |
| 17 | ** language. The code for the "sqlite3" command-line shell is also in a |
| 18 | ** separate file. This file contains only code for the core SQLite library. |
| 19 | ** |
| 20 | ** The content in this amalgamation comes from Fossil check-in |
| 21 | ** 18bda13e197e4b4ec7464b3e70012f71edc0 with changes in files: |
| 22 | ** |
| 23 | ** |
| 24 | */ |
| 25 | #ifndef SQLITE_AMALGAMATION |
| 26 | #define SQLITE_CORE 1 |
| @@ -465,11 +465,11 @@ | |
| 465 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 466 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 467 | */ |
| 468 | #define SQLITE_VERSION "3.50.0" |
| 469 | #define SQLITE_VERSION_NUMBER 3050000 |
| 470 | #define SQLITE_SOURCE_ID "2025-03-16 00:13:29 18bda13e197e4b4ec7464b3e70012f71edc05f73d8b14bb48bad452f81c7e185" |
| 471 | |
| 472 | /* |
| 473 | ** CAPI3REF: Run-Time Library Version Numbers |
| 474 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 475 | ** |
| @@ -1479,10 +1479,16 @@ | |
| 1479 | ** to block for up to M milliseconds before failing when attempting to |
| 1480 | ** obtain a file lock using the xLock or xShmLock methods of the VFS. |
| 1481 | ** The parameter is a pointer to a 32-bit signed integer that contains |
| 1482 | ** the value that M is to be set to. Before returning, the 32-bit signed |
| 1483 | ** integer is overwritten with the previous value of M. |
| 1484 | ** |
| 1485 | ** <li>[[SQLITE_FCNTL_BLOCK_ON_CONNECT]] |
| 1486 | ** The [SQLITE_FCNTL_BLOCK_ON_CONNECT] opcode is used to configure the |
| 1487 | ** VFS to block when taking a SHARED lock to connect to a wal mode database. |
| 1488 | ** This is used to implement the functionality associated with |
| 1489 | ** SQLITE_SETLK_BLOCK_ON_CONNECT. |
| 1490 | ** |
| 1491 | ** <li>[[SQLITE_FCNTL_DATA_VERSION]] |
| 1492 | ** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to |
| 1493 | ** a database file. The argument is a pointer to a 32-bit unsigned integer. |
| 1494 | ** The "data version" for the pager is written into the pointer. The |
| @@ -1576,10 +1582,11 @@ | |
| 1582 | #define SQLITE_FCNTL_CKPT_START 39 |
| 1583 | #define SQLITE_FCNTL_EXTERNAL_READER 40 |
| 1584 | #define SQLITE_FCNTL_CKSM_FILE 41 |
| 1585 | #define SQLITE_FCNTL_RESET_CACHE 42 |
| 1586 | #define SQLITE_FCNTL_NULL_IO 43 |
| 1587 | #define SQLITE_FCNTL_BLOCK_ON_CONNECT 44 |
| 1588 | |
| 1589 | /* deprecated names */ |
| 1590 | #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE |
| 1591 | #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE |
| 1592 | #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO |
| @@ -3332,10 +3339,48 @@ | |
| 3339 | ** |
| 3340 | ** See also: [PRAGMA busy_timeout] |
| 3341 | */ |
| 3342 | SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); |
| 3343 | |
| 3344 | /* |
| 3345 | ** CAPI3REF: Set the Setlk Timeout |
| 3346 | ** METHOD: sqlite3 |
| 3347 | ** |
| 3348 | ** This routine is only useful in SQLITE_ENABLE_SETLK_TIMEOUT builds. If |
| 3349 | ** the VFS supports blocking locks, it sets the timeout in ms used by |
| 3350 | ** eligible locks taken on wal mode databases by the specified database |
| 3351 | ** handle. In non-SQLITE_ENABLE_SETLK_TIMEOUT builds, or if the VFS does |
| 3352 | ** not support blocking locks, this function is a no-op. |
| 3353 | ** |
| 3354 | ** Passing 0 to this function disables blocking locks altogether. Passing |
| 3355 | ** -1 to this function requests that the VFS blocks for a long time - |
| 3356 | ** indefinitely if possible. The results of passing any other negative value |
| 3357 | ** are undefined. |
| 3358 | ** |
| 3359 | ** Internally, each SQLite database handle store two timeout values - the |
| 3360 | ** busy-timeout (used for rollback mode databases, or if the VFS does not |
| 3361 | ** support blocking locks) and the setlk-timeout (used for blocking locks |
| 3362 | ** on wal-mode databases). The sqlite3_busy_timeout() method sets both |
| 3363 | ** values, this function sets only the setlk-timeout value. Therefore, |
| 3364 | ** to configure separate busy-timeout and setlk-timeout values for a single |
| 3365 | ** database handle, call sqlite3_busy_timeout() followed by this function. |
| 3366 | ** |
| 3367 | ** Whenever the number of connections to a wal mode database falls from |
| 3368 | ** 1 to 0, the last connection takes an exclusive lock on the database, |
| 3369 | ** then checkpoints and deletes the wal file. While it is doing this, any |
| 3370 | ** new connection that tries to read from the database fails with an |
| 3371 | ** SQLITE_BUSY error. Or, if the SQLITE_SETLK_BLOCK_ON_CONNECT flag is |
| 3372 | ** passed to this API, the new connection blocks until the exclusive lock |
| 3373 | ** has been released. |
| 3374 | */ |
| 3375 | SQLITE_API int sqlite3_setlk_timeout(sqlite3*, int ms, int flags); |
| 3376 | |
| 3377 | /* |
| 3378 | ** CAPI3REF: Flags for sqlite3_setlk_timeout() |
| 3379 | */ |
| 3380 | #define SQLITE_SETLK_BLOCK_ON_CONNECT 0x01 |
| 3381 | |
| 3382 | /* |
| 3383 | ** CAPI3REF: Convenience Routines For Running Queries |
| 3384 | ** METHOD: sqlite3 |
| 3385 | ** |
| 3386 | ** This is a legacy interface that is preserved for backwards compatibility. |
| @@ -5447,11 +5492,11 @@ | |
| 5492 | ** For all versions of SQLite up to and including 3.6.23.1, a call to |
| 5493 | ** [sqlite3_reset()] was required after sqlite3_step() returned anything |
| 5494 | ** other than [SQLITE_ROW] before any subsequent invocation of |
| 5495 | ** sqlite3_step(). Failure to reset the prepared statement using |
| 5496 | ** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from |
| 5497 | ** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1]), |
| 5498 | ** sqlite3_step() began |
| 5499 | ** calling [sqlite3_reset()] automatically in this circumstance rather |
| 5500 | ** than returning [SQLITE_MISUSE]. This is not considered a compatibility |
| 5501 | ** break because any application that ever receives an SQLITE_MISUSE error |
| 5502 | ** is broken by definition. The [SQLITE_OMIT_AUTORESET] compile-time option |
| @@ -7343,10 +7388,12 @@ | |
| 7388 | ** ^Any callback set by a previous call to this function |
| 7389 | ** for the same database connection is overridden. |
| 7390 | ** |
| 7391 | ** ^The second argument is a pointer to the function to invoke when a |
| 7392 | ** row is updated, inserted or deleted in a rowid table. |
| 7393 | ** ^The update hook is disabled by invoking sqlite3_update_hook() |
| 7394 | ** with a NULL pointer as the second parameter. |
| 7395 | ** ^The first argument to the callback is a copy of the third argument |
| 7396 | ** to sqlite3_update_hook(). |
| 7397 | ** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE], |
| 7398 | ** or [SQLITE_UPDATE], depending on the operation that caused the callback |
| 7399 | ** to be invoked. |
| @@ -14097,18 +14144,26 @@ | |
| 14144 | ** * Terms in the SET clause of an UPDATE statement |
| 14145 | ** * Terms in the result set of a SELECT statement |
| 14146 | ** * Terms in the GROUP BY or ORDER BY clauses of a SELECT statement. |
| 14147 | ** * Terms in the VALUES clause of an INSERT statement |
| 14148 | ** |
| 14149 | ** The hard upper limit here is 32767. Most database people will |
| 14150 | ** tell you that in a well-normalized database, you usually should |
| 14151 | ** not have more than a dozen or so columns in any table. And if |
| 14152 | ** that is the case, there is no point in having more than a few |
| 14153 | ** dozen values in any of the other situations described above. |
| 14154 | ** |
| 14155 | ** An index can only have SQLITE_MAX_COLUMN columns from the user |
| 14156 | ** point of view, but the underlying b-tree that implements the index |
| 14157 | ** might have up to twice as many columns in a WITHOUT ROWID table, |
| 14158 | ** since must also store the primary key at the end. Hence the |
| 14159 | ** column count for Index is u16 instead of i16. |
| 14160 | */ |
| 14161 | #if !defined(SQLITE_MAX_COLUMN) |
| 14162 | # define SQLITE_MAX_COLUMN 2000 |
| 14163 | #elif SQLITE_MAX_COLUMN>32767 |
| 14164 | # error SQLITE_MAX_COLUMN may not exceed 32767 |
| 14165 | #endif |
| 14166 | |
| 14167 | /* |
| 14168 | ** The maximum length of a single SQL statement in bytes. |
| 14169 | ** |
| @@ -15117,11 +15172,21 @@ | |
| 15172 | /* |
| 15173 | ** GCC does not define the offsetof() macro so we'll have to do it |
| 15174 | ** ourselves. |
| 15175 | */ |
| 15176 | #ifndef offsetof |
| 15177 | #define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD)) |
| 15178 | #endif |
| 15179 | |
| 15180 | /* |
| 15181 | ** Work around C99 "flex-array" syntax for pre-C99 compilers, so as |
| 15182 | ** to avoid complaints from -fsanitize=strict-bounds. |
| 15183 | */ |
| 15184 | #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) |
| 15185 | # define FLEXARRAY |
| 15186 | #else |
| 15187 | # define FLEXARRAY 1 |
| 15188 | #endif |
| 15189 | |
| 15190 | /* |
| 15191 | ** Macros to compute minimum and maximum of two numbers. |
| 15192 | */ |
| @@ -17352,12 +17417,12 @@ | |
| 17417 | SQLITE_PRIVATE int sqlite3NotPureFunc(sqlite3_context*); |
| 17418 | #ifdef SQLITE_ENABLE_BYTECODE_VTAB |
| 17419 | SQLITE_PRIVATE int sqlite3VdbeBytecodeVtabInit(sqlite3*); |
| 17420 | #endif |
| 17421 | |
| 17422 | /* Use SQLITE_ENABLE_EXPLAIN_COMMENTS to enable generation of extra |
| 17423 | ** comments on each VDBE opcode. |
| 17424 | ** |
| 17425 | ** Use the SQLITE_ENABLE_MODULE_COMMENTS macro to see some extra no-op |
| 17426 | ** comments in VDBE programs that show key decision points in the code |
| 17427 | ** generator. |
| 17428 | */ |
| @@ -18076,10 +18141,14 @@ | |
| 18141 | BusyHandler busyHandler; /* Busy callback */ |
| 18142 | Db aDbStatic[2]; /* Static space for the 2 default backends */ |
| 18143 | Savepoint *pSavepoint; /* List of active savepoints */ |
| 18144 | int nAnalysisLimit; /* Number of index rows to ANALYZE */ |
| 18145 | int busyTimeout; /* Busy handler timeout, in msec */ |
| 18146 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 18147 | int setlkTimeout; /* Blocking lock timeout, in msec. -1 -> inf. */ |
| 18148 | int setlkFlags; /* Flags passed to setlk_timeout() */ |
| 18149 | #endif |
| 18150 | int nSavepoint; /* Number of non-transaction savepoints */ |
| 18151 | int nStatement; /* Number of nested statement-transactions */ |
| 18152 | i64 nDeferredCons; /* Net deferred constraints this transaction. */ |
| 18153 | i64 nDeferredImmCons; /* Net deferred immediate constraints */ |
| 18154 | int *pnBytesFreed; /* If not NULL, increment this in DbFree() */ |
| @@ -18888,12 +18957,16 @@ | |
| 18957 | u8 aAction[2]; /* ON DELETE and ON UPDATE actions, respectively */ |
| 18958 | Trigger *apTrigger[2];/* Triggers for aAction[] actions */ |
| 18959 | struct sColMap { /* Mapping of columns in pFrom to columns in zTo */ |
| 18960 | int iFrom; /* Index of column in pFrom */ |
| 18961 | char *zCol; /* Name of column in zTo. If NULL use PRIMARY KEY */ |
| 18962 | } aCol[FLEXARRAY]; /* One entry for each of nCol columns */ |
| 18963 | }; |
| 18964 | |
| 18965 | /* The size (in bytes) of an FKey object holding N columns. The answer |
| 18966 | ** does NOT include space to hold the zTo name. */ |
| 18967 | #define SZ_FKEY(N) (offsetof(FKey,aCol)+(N)*sizeof(struct sColMap)) |
| 18968 | |
| 18969 | /* |
| 18970 | ** SQLite supports many different ways to resolve a constraint |
| 18971 | ** error. ROLLBACK processing means that a constraint violation |
| 18972 | ** causes the operation in process to fail and for the current transaction |
| @@ -18952,13 +19025,16 @@ | |
| 19025 | u8 enc; /* Text encoding - one of the SQLITE_UTF* values */ |
| 19026 | u16 nKeyField; /* Number of key columns in the index */ |
| 19027 | u16 nAllField; /* Total columns, including key plus others */ |
| 19028 | sqlite3 *db; /* The database connection */ |
| 19029 | u8 *aSortFlags; /* Sort order for each column. */ |
| 19030 | CollSeq *aColl[FLEXARRAY]; /* Collating sequence for each term of the key */ |
| 19031 | }; |
| 19032 | |
| 19033 | /* The size (in bytes) of a KeyInfo object with up to N fields */ |
| 19034 | #define SZ_KEYINFO(N) (offsetof(KeyInfo,aColl) + (N)*sizeof(CollSeq*)) |
| 19035 | |
| 19036 | /* |
| 19037 | ** Allowed bit values for entries in the KeyInfo.aSortFlags[] array. |
| 19038 | */ |
| 19039 | #define KEYINFO_ORDER_DESC 0x01 /* DESC sort order */ |
| 19040 | #define KEYINFO_ORDER_BIGNULL 0x02 /* NULL is larger than any other value */ |
| @@ -19074,11 +19150,11 @@ | |
| 19150 | Expr *pPartIdxWhere; /* WHERE clause for partial indices */ |
| 19151 | ExprList *aColExpr; /* Column expressions */ |
| 19152 | Pgno tnum; /* DB Page containing root of this index */ |
| 19153 | LogEst szIdxRow; /* Estimated average row size in bytes */ |
| 19154 | u16 nKeyCol; /* Number of columns forming the key */ |
| 19155 | u16 nColumn; /* Nr columns in btree. Can be 2*Table.nCol */ |
| 19156 | u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */ |
| 19157 | unsigned idxType:2; /* 0:Normal 1:UNIQUE, 2:PRIMARY KEY, 3:IPK */ |
| 19158 | unsigned bUnordered:1; /* Use this index for == or IN queries only */ |
| 19159 | unsigned uniqNotNull:1; /* True if UNIQUE and NOT NULL for all columns */ |
| 19160 | unsigned isResized:1; /* True if resizeIndexObject() has been called */ |
| @@ -19412,14 +19488,14 @@ | |
| 19488 | #define EP_Propagate (EP_Collate|EP_Subquery|EP_HasFunc) |
| 19489 | |
| 19490 | /* Macros can be used to test, set, or clear bits in the |
| 19491 | ** Expr.flags field. |
| 19492 | */ |
| 19493 | #define ExprHasProperty(E,P) (((E)->flags&(u32)(P))!=0) |
| 19494 | #define ExprHasAllProperty(E,P) (((E)->flags&(u32)(P))==(u32)(P)) |
| 19495 | #define ExprSetProperty(E,P) (E)->flags|=(u32)(P) |
| 19496 | #define ExprClearProperty(E,P) (E)->flags&=~(u32)(P) |
| 19497 | #define ExprAlwaysTrue(E) (((E)->flags&(EP_OuterON|EP_IsTrue))==EP_IsTrue) |
| 19498 | #define ExprAlwaysFalse(E) (((E)->flags&(EP_OuterON|EP_IsFalse))==EP_IsFalse) |
| 19499 | #define ExprIsFullSize(E) (((E)->flags&(EP_Reduced|EP_TokenOnly))==0) |
| 19500 | |
| 19501 | /* Macros used to ensure that the correct members of unions are accessed |
| @@ -19527,12 +19603,17 @@ | |
| 19603 | u16 iAlias; /* Index into Parse.aAlias[] for zName */ |
| 19604 | } x; |
| 19605 | int iConstExprReg; /* Register in which Expr value is cached. Used only |
| 19606 | ** by Parse.pConstExpr */ |
| 19607 | } u; |
| 19608 | } a[FLEXARRAY]; /* One slot for each expression in the list */ |
| 19609 | }; |
| 19610 | |
| 19611 | /* The size (in bytes) of an ExprList object that is big enough to hold |
| 19612 | ** as many as N expressions. */ |
| 19613 | #define SZ_EXPRLIST(N) \ |
| 19614 | (offsetof(ExprList,a) + (N)*sizeof(struct ExprList_item)) |
| 19615 | |
| 19616 | /* |
| 19617 | ** Allowed values for Expr.a.eEName |
| 19618 | */ |
| 19619 | #define ENAME_NAME 0 /* The AS clause of a result set */ |
| @@ -19557,13 +19638,16 @@ | |
| 19638 | */ |
| 19639 | struct IdList { |
| 19640 | int nId; /* Number of identifiers on the list */ |
| 19641 | struct IdList_item { |
| 19642 | char *zName; /* Name of the identifier */ |
| 19643 | } a[FLEXARRAY]; |
| 19644 | }; |
| 19645 | |
| 19646 | /* The size (in bytes) of an IdList object that can hold up to N IDs. */ |
| 19647 | #define SZ_IDLIST(N) (offsetof(IdList,a)+(N)*sizeof(struct IdList_item)) |
| 19648 | |
| 19649 | /* |
| 19650 | ** Allowed values for IdList.eType, which determines which value of the a.u4 |
| 19651 | ** is valid. |
| 19652 | */ |
| 19653 | #define EU4_NONE 0 /* Does not use IdList.a.u4 */ |
| @@ -19679,14 +19763,22 @@ | |
| 19763 | ** is used to hold the FROM clause of a SELECT statement. SrcList also |
| 19764 | ** represents the target tables for DELETE, INSERT, and UPDATE statements. |
| 19765 | ** |
| 19766 | */ |
| 19767 | struct SrcList { |
| 19768 | int nSrc; /* Number of tables or subqueries in the FROM clause */ |
| 19769 | u32 nAlloc; /* Number of entries allocated in a[] below */ |
| 19770 | SrcItem a[FLEXARRAY]; /* One entry for each identifier on the list */ |
| 19771 | }; |
| 19772 | |
| 19773 | /* Size (in bytes) of a SrcList object that can hold as many as N |
| 19774 | ** SrcItem objects. */ |
| 19775 | #define SZ_SRCLIST(N) (offsetof(SrcList,a)+(N)*sizeof(SrcItem)) |
| 19776 | |
| 19777 | /* Size (in bytes( of a SrcList object that holds 1 SrcItem. This is a |
| 19778 | ** special case of SZ_SRCITEM(1) that comes up often. */ |
| 19779 | #define SZ_SRCLIST_1 (offsetof(SrcList,a)+sizeof(SrcItem)) |
| 19780 | |
| 19781 | /* |
| 19782 | ** Permitted values of the SrcList.a.jointype field |
| 19783 | */ |
| 19784 | #define JT_INNER 0x01 /* Any kind of inner or cross join */ |
| @@ -20747,12 +20839,16 @@ | |
| 20839 | */ |
| 20840 | struct With { |
| 20841 | int nCte; /* Number of CTEs in the WITH clause */ |
| 20842 | int bView; /* Belongs to the outermost Select of a view */ |
| 20843 | With *pOuter; /* Containing WITH clause, or NULL */ |
| 20844 | Cte a[FLEXARRAY]; /* For each CTE in the WITH clause.... */ |
| 20845 | }; |
| 20846 | |
| 20847 | /* The size (in bytes) of a With object that can hold as many |
| 20848 | ** as N different CTEs. */ |
| 20849 | #define SZ_WITH(N) (offsetof(With,a) + (N)*sizeof(Cte)) |
| 20850 | |
| 20851 | /* |
| 20852 | ** The Cte object is not guaranteed to persist for the entire duration |
| 20853 | ** of code generation. (The query flattener or other parser tree |
| 20854 | ** edits might delete it.) The following object records information |
| @@ -20778,12 +20874,16 @@ | |
| 20874 | */ |
| 20875 | struct DbClientData { |
| 20876 | DbClientData *pNext; /* Next in a linked list */ |
| 20877 | void *pData; /* The data */ |
| 20878 | void (*xDestructor)(void*); /* Destructor. Might be NULL */ |
| 20879 | char zName[FLEXARRAY]; /* Name of this client data. MUST BE LAST */ |
| 20880 | }; |
| 20881 | |
| 20882 | /* The size (in bytes) of a DbClientData object that can has a name |
| 20883 | ** that is N bytes long, including the zero-terminator. */ |
| 20884 | #define SZ_DBCLIENTDATA(N) (offsetof(DbClientData,zName)+(N)) |
| 20885 | |
| 20886 | #ifdef SQLITE_DEBUG |
| 20887 | /* |
| 20888 | ** An instance of the TreeView object is used for printing the content of |
| 20889 | ** data structures on sqlite3DebugPrintf() using a tree-like view. |
| @@ -21223,11 +21323,11 @@ | |
| 21323 | SQLITE_PRIVATE int sqlite3ColumnsFromExprList(Parse*,ExprList*,i16*,Column**); |
| 21324 | SQLITE_PRIVATE void sqlite3SubqueryColumnTypes(Parse*,Table*,Select*,char); |
| 21325 | SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse*,Select*,char); |
| 21326 | SQLITE_PRIVATE void sqlite3OpenSchemaTable(Parse *, int); |
| 21327 | SQLITE_PRIVATE Index *sqlite3PrimaryKeyIndex(Table*); |
| 21328 | SQLITE_PRIVATE int sqlite3TableColumnToIndex(Index*, int); |
| 21329 | #ifdef SQLITE_OMIT_GENERATED_COLUMNS |
| 21330 | # define sqlite3TableColumnToStorage(T,X) (X) /* No-op pass-through */ |
| 21331 | # define sqlite3StorageColumnToTable(T,X) (X) /* No-op pass-through */ |
| 21332 | #else |
| 21333 | SQLITE_PRIVATE i16 sqlite3TableColumnToStorage(Table*, i16); |
| @@ -21321,11 +21421,11 @@ | |
| 21421 | SQLITE_PRIVATE void sqlite3SrcListShiftJoinType(Parse*,SrcList*); |
| 21422 | SQLITE_PRIVATE void sqlite3SrcListAssignCursors(Parse*, SrcList*); |
| 21423 | SQLITE_PRIVATE void sqlite3IdListDelete(sqlite3*, IdList*); |
| 21424 | SQLITE_PRIVATE void sqlite3ClearOnOrUsing(sqlite3*, OnOrUsing*); |
| 21425 | SQLITE_PRIVATE void sqlite3SrcListDelete(sqlite3*, SrcList*); |
| 21426 | SQLITE_PRIVATE Index *sqlite3AllocateIndexObject(sqlite3*,int,int,char**); |
| 21427 | SQLITE_PRIVATE void sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*, |
| 21428 | Expr*, int, int, u8); |
| 21429 | SQLITE_PRIVATE void sqlite3DropIndex(Parse*, SrcList*, int); |
| 21430 | SQLITE_PRIVATE int sqlite3Select(Parse*, Select*, SelectDest*); |
| 21431 | SQLITE_PRIVATE Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*, |
| @@ -21457,11 +21557,12 @@ | |
| 21557 | SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3*,const IdList*); |
| 21558 | SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3*,const Select*,int); |
| 21559 | SQLITE_PRIVATE FuncDef *sqlite3FunctionSearch(int,const char*); |
| 21560 | SQLITE_PRIVATE void sqlite3InsertBuiltinFuncs(FuncDef*,int); |
| 21561 | SQLITE_PRIVATE FuncDef *sqlite3FindFunction(sqlite3*,const char*,int,u8,u8); |
| 21562 | SQLITE_PRIVATE void sqlite3QuoteValue(StrAccum*,sqlite3_value*,int); |
| 21563 | SQLITE_PRIVATE int sqlite3AppendOneUtf8Character(char*, u32); |
| 21564 | SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void); |
| 21565 | SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void); |
| 21566 | SQLITE_PRIVATE void sqlite3RegisterJsonFunctions(void); |
| 21567 | SQLITE_PRIVATE void sqlite3RegisterPerConnectionBuiltinFunctions(sqlite3*); |
| 21568 | #if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON) |
| @@ -22557,10 +22658,13 @@ | |
| 22658 | #ifdef SQLITE_ENABLE_RTREE |
| 22659 | "ENABLE_RTREE", |
| 22660 | #endif |
| 22661 | #ifdef SQLITE_ENABLE_SESSION |
| 22662 | "ENABLE_SESSION", |
| 22663 | #endif |
| 22664 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 22665 | "ENABLE_SETLK_TIMEOUT", |
| 22666 | #endif |
| 22667 | #ifdef SQLITE_ENABLE_SNAPSHOT |
| 22668 | "ENABLE_SNAPSHOT", |
| 22669 | #endif |
| 22670 | #ifdef SQLITE_ENABLE_SORTER_REFERENCES |
| @@ -22612,10 +22716,13 @@ | |
| 22716 | "EXTRA_IFNULLROW", |
| 22717 | #endif |
| 22718 | #ifdef SQLITE_EXTRA_INIT |
| 22719 | "EXTRA_INIT=" CTIMEOPT_VAL(SQLITE_EXTRA_INIT), |
| 22720 | #endif |
| 22721 | #ifdef SQLITE_EXTRA_INIT_MUTEXED |
| 22722 | "EXTRA_INIT_MUTEXED=" CTIMEOPT_VAL(SQLITE_EXTRA_INIT_MUTEXED), |
| 22723 | #endif |
| 22724 | #ifdef SQLITE_EXTRA_SHUTDOWN |
| 22725 | "EXTRA_SHUTDOWN=" CTIMEOPT_VAL(SQLITE_EXTRA_SHUTDOWN), |
| 22726 | #endif |
| 22727 | #ifdef SQLITE_FTS3_MAX_EXPR_DEPTH |
| 22728 | "FTS3_MAX_EXPR_DEPTH=" CTIMEOPT_VAL(SQLITE_FTS3_MAX_EXPR_DEPTH), |
| @@ -23596,16 +23703,23 @@ | |
| 23703 | #ifdef SQLITE_ENABLE_COLUMN_USED_MASK |
| 23704 | u64 maskUsed; /* Mask of columns used by this cursor */ |
| 23705 | #endif |
| 23706 | VdbeTxtBlbCache *pCache; /* Cache of large TEXT or BLOB values */ |
| 23707 | |
| 23708 | /* Space is allocated for aType to hold at least 2*nField+1 entries: |
| 23709 | ** nField slots for aType[] and nField+1 array slots for aOffset[] */ |
| 23710 | u32 aType[FLEXARRAY]; /* Type values record decode. MUST BE LAST */ |
| 23711 | }; |
| 23712 | |
| 23713 | /* |
| 23714 | ** The size (in bytes) of a VdbeCursor object that has an nField value of N |
| 23715 | ** or less. The value of SZ_VDBECURSOR(n) is guaranteed to be a multiple |
| 23716 | ** of 8. |
| 23717 | */ |
| 23718 | #define SZ_VDBECURSOR(N) \ |
| 23719 | (ROUND8(offsetof(VdbeCursor,aType)) + ((N)+1)*sizeof(u64)) |
| 23720 | |
| 23721 | /* Return true if P is a null-only cursor |
| 23722 | */ |
| 23723 | #define IsNullCursor(P) \ |
| 23724 | ((P)->eCurType==CURTYPE_PSEUDO && (P)->nullRow && (P)->seekResult==0) |
| 23725 | |
| @@ -23858,13 +23972,20 @@ | |
| 23972 | int iOp; /* Instruction number of OP_Function */ |
| 23973 | int isError; /* Error code returned by the function. */ |
| 23974 | u8 enc; /* Encoding to use for results */ |
| 23975 | u8 skipFlag; /* Skip accumulator loading if true */ |
| 23976 | u16 argc; /* Number of arguments */ |
| 23977 | sqlite3_value *argv[FLEXARRAY]; /* Argument set */ |
| 23978 | }; |
| 23979 | |
| 23980 | /* |
| 23981 | ** The size (in bytes) of an sqlite3_context object that holds N |
| 23982 | ** argv[] arguments. |
| 23983 | */ |
| 23984 | #define SZ_CONTEXT(N) \ |
| 23985 | (offsetof(sqlite3_context,argv)+(N)*sizeof(sqlite3_value*)) |
| 23986 | |
| 23987 | |
| 23988 | /* The ScanStatus object holds a single value for the |
| 23989 | ** sqlite3_stmt_scanstatus() interface. |
| 23990 | ** |
| 23991 | ** aAddrRange[]: |
| @@ -23994,11 +24115,11 @@ | |
| 24115 | struct PreUpdate { |
| 24116 | Vdbe *v; |
| 24117 | VdbeCursor *pCsr; /* Cursor to read old values from */ |
| 24118 | int op; /* One of SQLITE_INSERT, UPDATE, DELETE */ |
| 24119 | u8 *aRecord; /* old.* database record */ |
| 24120 | KeyInfo *pKeyinfo; /* Key information */ |
| 24121 | UnpackedRecord *pUnpacked; /* Unpacked version of aRecord[] */ |
| 24122 | UnpackedRecord *pNewUnpacked; /* Unpacked version of new.* record */ |
| 24123 | int iNewReg; /* Register for new.* values */ |
| 24124 | int iBlobWrite; /* Value returned by preupdate_blobwrite() */ |
| 24125 | i64 iKey1; /* First key value passed to hook */ |
| @@ -24006,10 +24127,11 @@ | |
| 24127 | Mem oldipk; /* Memory cell holding "old" IPK value */ |
| 24128 | Mem *aNew; /* Array of new.* values */ |
| 24129 | Table *pTab; /* Schema object being updated */ |
| 24130 | Index *pPk; /* PK index if pTab is WITHOUT ROWID */ |
| 24131 | sqlite3_value **apDflt; /* Array of default values, if required */ |
| 24132 | u8 keyinfoSpace[SZ_KEYINFO(0)]; /* Space to hold pKeyinfo[0] content */ |
| 24133 | }; |
| 24134 | |
| 24135 | /* |
| 24136 | ** An instance of this object is used to pass an vector of values into |
| 24137 | ** OP_VFilter, the xFilter method of a virtual table. The vector is the |
| @@ -24372,12 +24494,13 @@ | |
| 24494 | u32 nFree = countLookasideSlots(db->lookaside.pFree); |
| 24495 | #ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE |
| 24496 | nInit += countLookasideSlots(db->lookaside.pSmallInit); |
| 24497 | nFree += countLookasideSlots(db->lookaside.pSmallFree); |
| 24498 | #endif /* SQLITE_OMIT_TWOSIZE_LOOKASIDE */ |
| 24499 | assert( db->lookaside.nSlot >= nInit+nFree ); |
| 24500 | if( pHighwater ) *pHighwater = (int)(db->lookaside.nSlot - nInit); |
| 24501 | return (int)(db->lookaside.nSlot - (nInit+nFree)); |
| 24502 | } |
| 24503 | |
| 24504 | /* |
| 24505 | ** Query status information for a single database connection |
| 24506 | */ |
| @@ -24426,11 +24549,11 @@ | |
| 24549 | testcase( op==SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE ); |
| 24550 | testcase( op==SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL ); |
| 24551 | assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)>=0 ); |
| 24552 | assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)<3 ); |
| 24553 | *pCurrent = 0; |
| 24554 | *pHighwater = (int)db->lookaside.anStat[op-SQLITE_DBSTATUS_LOOKASIDE_HIT]; |
| 24555 | if( resetFlag ){ |
| 24556 | db->lookaside.anStat[op - SQLITE_DBSTATUS_LOOKASIDE_HIT] = 0; |
| 24557 | } |
| 24558 | break; |
| 24559 | } |
| @@ -25938,11 +26061,11 @@ | |
| 26061 | ** Return the number of days after the most recent Sunday. |
| 26062 | ** |
| 26063 | ** In other words, return the day of the week according |
| 26064 | ** to this code: |
| 26065 | ** |
| 26066 | ** 0=Sunday, 1=Monday, 2=Tuesday, ..., 6=Saturday |
| 26067 | */ |
| 26068 | static int daysAfterSunday(DateTime *pDate){ |
| 26069 | assert( pDate->validJD ); |
| 26070 | return (int)((pDate->iJD+129600000)/86400000) % 7; |
| 26071 | } |
| @@ -31541,21 +31664,21 @@ | |
| 31664 | #define etSTRING 5 /* Strings. %s */ |
| 31665 | #define etDYNSTRING 6 /* Dynamically allocated strings. %z */ |
| 31666 | #define etPERCENT 7 /* Percent symbol. %% */ |
| 31667 | #define etCHARX 8 /* Characters. %c */ |
| 31668 | /* The rest are extensions, not normally found in printf() */ |
| 31669 | #define etESCAPE_q 9 /* Strings with '\'' doubled. %q */ |
| 31670 | #define etESCAPE_Q 10 /* Strings with '\'' doubled and enclosed in '', |
| 31671 | NULL pointers replaced by SQL NULL. %Q */ |
| 31672 | #define etTOKEN 11 /* a pointer to a Token structure */ |
| 31673 | #define etSRCITEM 12 /* a pointer to a SrcItem */ |
| 31674 | #define etPOINTER 13 /* The %p conversion */ |
| 31675 | #define etESCAPE_w 14 /* %w -> Strings with '\"' doubled */ |
| 31676 | #define etORDINAL 15 /* %r -> 1st, 2nd, 3rd, 4th, etc. English only */ |
| 31677 | #define etDECIMAL 16 /* %d or %u, but not %x, %o */ |
| 31678 | |
| 31679 | #define etINVALID 17 /* Any unrecognized conversion type */ |
| 31680 | |
| 31681 | |
| 31682 | /* |
| 31683 | ** An "etByte" is an 8-bit unsigned value. |
| 31684 | */ |
| @@ -31590,13 +31713,13 @@ | |
| 31713 | static const et_info fmtinfo[] = { |
| 31714 | { 'd', 10, 1, etDECIMAL, 0, 0 }, |
| 31715 | { 's', 0, 4, etSTRING, 0, 0 }, |
| 31716 | { 'g', 0, 1, etGENERIC, 30, 0 }, |
| 31717 | { 'z', 0, 4, etDYNSTRING, 0, 0 }, |
| 31718 | { 'q', 0, 4, etESCAPE_q, 0, 0 }, |
| 31719 | { 'Q', 0, 4, etESCAPE_Q, 0, 0 }, |
| 31720 | { 'w', 0, 4, etESCAPE_w, 0, 0 }, |
| 31721 | { 'c', 0, 0, etCHARX, 0, 0 }, |
| 31722 | { 'o', 8, 0, etRADIX, 0, 2 }, |
| 31723 | { 'u', 10, 0, etDECIMAL, 0, 0 }, |
| 31724 | { 'x', 16, 0, etRADIX, 16, 1 }, |
| 31725 | { 'X', 16, 0, etRADIX, 0, 4 }, |
| @@ -32189,29 +32312,11 @@ | |
| 32312 | }else{ |
| 32313 | buf[0] = 0; |
| 32314 | } |
| 32315 | }else{ |
| 32316 | unsigned int ch = va_arg(ap,unsigned int); |
| 32317 | length = sqlite3AppendOneUtf8Character(buf, ch); |
| 32318 | } |
| 32319 | if( precision>1 ){ |
| 32320 | i64 nPrior = 1; |
| 32321 | width -= precision-1; |
| 32322 | if( width>1 && !flag_leftjustify ){ |
| @@ -32287,26 +32392,35 @@ | |
| 32392 | /* Adjust width to account for extra bytes in UTF-8 characters */ |
| 32393 | int ii = length - 1; |
| 32394 | while( ii>=0 ) if( (bufpt[ii--] & 0xc0)==0x80 ) width++; |
| 32395 | } |
| 32396 | break; |
| 32397 | case etESCAPE_q: /* %q: Escape ' characters */ |
| 32398 | case etESCAPE_Q: /* %Q: Escape ' and enclose in '...' */ |
| 32399 | case etESCAPE_w: { /* %w: Escape " characters */ |
| 32400 | i64 i, j, k, n; |
| 32401 | int needQuote = 0; |
| 32402 | char ch; |
| 32403 | char *escarg; |
| 32404 | char q; |
| 32405 | |
| 32406 | if( bArgList ){ |
| 32407 | escarg = getTextArg(pArgList); |
| 32408 | }else{ |
| 32409 | escarg = va_arg(ap,char*); |
| 32410 | } |
| 32411 | if( escarg==0 ){ |
| 32412 | escarg = (xtype==etESCAPE_Q ? "NULL" : "(NULL)"); |
| 32413 | }else if( xtype==etESCAPE_Q ){ |
| 32414 | needQuote = 1; |
| 32415 | } |
| 32416 | if( xtype==etESCAPE_w ){ |
| 32417 | q = '"'; |
| 32418 | flag_alternateform = 0; |
| 32419 | }else{ |
| 32420 | q = '\''; |
| 32421 | } |
| 32422 | /* For %q, %Q, and %w, the precision is the number of bytes (or |
| 32423 | ** characters if the ! flags is present) to use from the input. |
| 32424 | ** Because of the extra quoting characters inserted, the number |
| 32425 | ** of output characters may be larger than the precision. |
| 32426 | */ |
| @@ -32315,26 +32429,77 @@ | |
| 32429 | if( ch==q ) n++; |
| 32430 | if( flag_altform2 && (ch&0xc0)==0xc0 ){ |
| 32431 | while( (escarg[i+1]&0xc0)==0x80 ){ i++; } |
| 32432 | } |
| 32433 | } |
| 32434 | if( flag_alternateform ){ |
| 32435 | /* For %#q, do unistr()-style backslash escapes for |
| 32436 | ** all control characters, and for backslash itself. |
| 32437 | ** For %#Q, do the same but only if there is at least |
| 32438 | ** one control character. */ |
| 32439 | u32 nBack = 0; |
| 32440 | u32 nCtrl = 0; |
| 32441 | for(k=0; k<i; k++){ |
| 32442 | if( escarg[k]=='\\' ){ |
| 32443 | nBack++; |
| 32444 | }else if( ((u8*)escarg)[k]<=0x1f ){ |
| 32445 | nCtrl++; |
| 32446 | } |
| 32447 | } |
| 32448 | if( nCtrl || xtype==etESCAPE_q ){ |
| 32449 | n += nBack + 5*nCtrl; |
| 32450 | if( xtype==etESCAPE_Q ){ |
| 32451 | n += 10; |
| 32452 | needQuote = 2; |
| 32453 | } |
| 32454 | }else{ |
| 32455 | flag_alternateform = 0; |
| 32456 | } |
| 32457 | } |
| 32458 | n += i + 3; |
| 32459 | if( n>etBUFSIZE ){ |
| 32460 | bufpt = zExtra = printfTempBuf(pAccum, n); |
| 32461 | if( bufpt==0 ) return; |
| 32462 | }else{ |
| 32463 | bufpt = buf; |
| 32464 | } |
| 32465 | j = 0; |
| 32466 | if( needQuote ){ |
| 32467 | if( needQuote==2 ){ |
| 32468 | memcpy(&bufpt[j], "unistr('", 8); |
| 32469 | j += 8; |
| 32470 | }else{ |
| 32471 | bufpt[j++] = '\''; |
| 32472 | } |
| 32473 | } |
| 32474 | k = i; |
| 32475 | if( flag_alternateform ){ |
| 32476 | for(i=0; i<k; i++){ |
| 32477 | bufpt[j++] = ch = escarg[i]; |
| 32478 | if( ch==q ){ |
| 32479 | bufpt[j++] = ch; |
| 32480 | }else if( ch=='\\' ){ |
| 32481 | bufpt[j++] = '\\'; |
| 32482 | }else if( ((unsigned char)ch)<=0x1f ){ |
| 32483 | bufpt[j-1] = '\\'; |
| 32484 | bufpt[j++] = 'u'; |
| 32485 | bufpt[j++] = '0'; |
| 32486 | bufpt[j++] = '0'; |
| 32487 | bufpt[j++] = ch>=0x10 ? '1' : '0'; |
| 32488 | bufpt[j++] = "0123456789abcdef"[ch&0xf]; |
| 32489 | } |
| 32490 | } |
| 32491 | }else{ |
| 32492 | for(i=0; i<k; i++){ |
| 32493 | bufpt[j++] = ch = escarg[i]; |
| 32494 | if( ch==q ) bufpt[j++] = ch; |
| 32495 | } |
| 32496 | } |
| 32497 | if( needQuote ){ |
| 32498 | bufpt[j++] = '\''; |
| 32499 | if( needQuote==2 ) bufpt[j++] = ')'; |
| 32500 | } |
| 32501 | bufpt[j] = 0; |
| 32502 | length = j; |
| 32503 | goto adjust_width_for_utf8; |
| 32504 | } |
| 32505 | case etTOKEN: { |
| @@ -34828,10 +34993,39 @@ | |
| 34993 | *zOut++ = (u8)(((c>>10)&0x003F) + (((c-0x10000)>>10)&0x00C0)); \ |
| 34994 | *zOut++ = (u8)(0x00DC + ((c>>8)&0x03)); \ |
| 34995 | *zOut++ = (u8)(c&0x00FF); \ |
| 34996 | } \ |
| 34997 | } |
| 34998 | |
| 34999 | /* |
| 35000 | ** Write a single UTF8 character whose value is v into the |
| 35001 | ** buffer starting at zOut. zOut must be sized to hold at |
| 35002 | ** least for bytes. Return the number of bytes needed |
| 35003 | ** to encode the new character. |
| 35004 | */ |
| 35005 | SQLITE_PRIVATE int sqlite3AppendOneUtf8Character(char *zOut, u32 v){ |
| 35006 | if( v<0x00080 ){ |
| 35007 | zOut[0] = (u8)(v & 0xff); |
| 35008 | return 1; |
| 35009 | } |
| 35010 | if( v<0x00800 ){ |
| 35011 | zOut[0] = 0xc0 + (u8)((v>>6) & 0x1f); |
| 35012 | zOut[1] = 0x80 + (u8)(v & 0x3f); |
| 35013 | return 2; |
| 35014 | } |
| 35015 | if( v<0x10000 ){ |
| 35016 | zOut[0] = 0xe0 + (u8)((v>>12) & 0x0f); |
| 35017 | zOut[1] = 0x80 + (u8)((v>>6) & 0x3f); |
| 35018 | zOut[2] = 0x80 + (u8)(v & 0x3f); |
| 35019 | return 3; |
| 35020 | } |
| 35021 | zOut[0] = 0xf0 + (u8)((v>>18) & 0x07); |
| 35022 | zOut[1] = 0x80 + (u8)((v>>12) & 0x3f); |
| 35023 | zOut[2] = 0x80 + (u8)((v>>6) & 0x3f); |
| 35024 | zOut[3] = 0x80 + (u8)(v & 0x3f); |
| 35025 | return 4; |
| 35026 | } |
| 35027 | |
| 35028 | /* |
| 35029 | ** Translate a single UTF-8 character. Return the unicode value. |
| 35030 | ** |
| 35031 | ** During translation, assume that the byte that zTerm points |
| @@ -36934,11 +37128,11 @@ | |
| 37128 | return 0; |
| 37129 | #endif |
| 37130 | } |
| 37131 | |
| 37132 | /* |
| 37133 | ** Compute the absolute value of a 32-bit signed integer, if possible. Or |
| 37134 | ** if the integer has a value of -2147483648, return +2147483647 |
| 37135 | */ |
| 37136 | SQLITE_PRIVATE int sqlite3AbsInt32(int x){ |
| 37137 | if( x>=0 ) return x; |
| 37138 | if( x==(int)0x80000000 ) return 0x7fffffff; |
| @@ -38911,10 +39105,11 @@ | |
| 39105 | #if SQLITE_ENABLE_LOCKING_STYLE || defined(__APPLE__) |
| 39106 | unsigned fsFlags; /* cached details from statfs() */ |
| 39107 | #endif |
| 39108 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 39109 | unsigned iBusyTimeout; /* Wait this many millisec on locks */ |
| 39110 | int bBlockOnConnect; /* True to block for SHARED locks */ |
| 39111 | #endif |
| 39112 | #if OS_VXWORKS |
| 39113 | struct vxworksFileId *pId; /* Unique file ID */ |
| 39114 | #endif |
| 39115 | #ifdef SQLITE_DEBUG |
| @@ -40304,10 +40499,17 @@ | |
| 40499 | pInode->nLock++; |
| 40500 | }else{ |
| 40501 | rc = 0; |
| 40502 | } |
| 40503 | }else{ |
| 40504 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 40505 | if( pFile->bBlockOnConnect && pLock->l_type==F_RDLCK |
| 40506 | && pLock->l_start==SHARED_FIRST && pLock->l_len==SHARED_SIZE |
| 40507 | ){ |
| 40508 | rc = osFcntl(pFile->h, F_SETLKW, pLock); |
| 40509 | }else |
| 40510 | #endif |
| 40511 | rc = osSetPosixAdvisoryLock(pFile->h, pLock, pFile); |
| 40512 | } |
| 40513 | return rc; |
| 40514 | } |
| 40515 | |
| @@ -42665,21 +42867,27 @@ | |
| 42867 | return SQLITE_OK; |
| 42868 | } |
| 42869 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 42870 | case SQLITE_FCNTL_LOCK_TIMEOUT: { |
| 42871 | int iOld = pFile->iBusyTimeout; |
| 42872 | int iNew = *(int*)pArg; |
| 42873 | #if SQLITE_ENABLE_SETLK_TIMEOUT==1 |
| 42874 | pFile->iBusyTimeout = iNew<0 ? 0x7FFFFFFF : (unsigned)iNew; |
| 42875 | #elif SQLITE_ENABLE_SETLK_TIMEOUT==2 |
| 42876 | pFile->iBusyTimeout = !!(*(int*)pArg); |
| 42877 | #else |
| 42878 | # error "SQLITE_ENABLE_SETLK_TIMEOUT must be set to 1 or 2" |
| 42879 | #endif |
| 42880 | *(int*)pArg = iOld; |
| 42881 | return SQLITE_OK; |
| 42882 | } |
| 42883 | case SQLITE_FCNTL_BLOCK_ON_CONNECT: { |
| 42884 | int iNew = *(int*)pArg; |
| 42885 | pFile->bBlockOnConnect = iNew; |
| 42886 | return SQLITE_OK; |
| 42887 | } |
| 42888 | #endif /* SQLITE_ENABLE_SETLK_TIMEOUT */ |
| 42889 | #if SQLITE_MAX_MMAP_SIZE>0 |
| 42890 | case SQLITE_FCNTL_MMAP_SIZE: { |
| 42891 | i64 newLimit = *(i64*)pArg; |
| 42892 | int rc = SQLITE_OK; |
| 42893 | if( newLimit>sqlite3GlobalConfig.mxMmap ){ |
| @@ -43658,11 +43866,11 @@ | |
| 43866 | ** occur later in the above list than the lock being obtained may be |
| 43867 | ** held. |
| 43868 | ** |
| 43869 | ** It is not permitted to block on the RECOVER lock. |
| 43870 | */ |
| 43871 | #if defined(SQLITE_ENABLE_SETLK_TIMEOUT) && defined(SQLITE_DEBUG) |
| 43872 | { |
| 43873 | u16 lockMask = (p->exclMask|p->sharedMask); |
| 43874 | assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || ( |
| 43875 | (ofst!=2) /* not RECOVER */ |
| 43876 | && (ofst!=1 || lockMask==0 || lockMask==2) |
| @@ -45467,11 +45675,11 @@ | |
| 45675 | sp.tv_sec = microseconds / 1000000; |
| 45676 | sp.tv_nsec = (microseconds % 1000000) * 1000; |
| 45677 | |
| 45678 | /* Almost all modern unix systems support nanosleep(). But if you are |
| 45679 | ** compiling for one of the rare exceptions, you can use |
| 45680 | ** -DHAVE_NANOSLEEP=0 (perhaps in conjunction with -DHAVE_USLEEP if |
| 45681 | ** usleep() is available) in order to bypass the use of nanosleep() */ |
| 45682 | nanosleep(&sp, NULL); |
| 45683 | |
| 45684 | UNUSED_PARAMETER(NotUsed); |
| 45685 | return microseconds; |
| @@ -47188,11 +47396,21 @@ | |
| 47396 | HANDLE hMap; /* Handle for accessing memory mapping */ |
| 47397 | void *pMapRegion; /* Area memory mapped */ |
| 47398 | sqlite3_int64 mmapSize; /* Size of mapped region */ |
| 47399 | sqlite3_int64 mmapSizeMax; /* Configured FCNTL_MMAP_SIZE value */ |
| 47400 | #endif |
| 47401 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 47402 | DWORD iBusyTimeout; /* Wait this many millisec on locks */ |
| 47403 | int bBlockOnConnect; |
| 47404 | #endif |
| 47405 | }; |
| 47406 | |
| 47407 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 47408 | # define winFileBusyTimeout(pDbFd) pDbFd->iBusyTimeout |
| 47409 | #else |
| 47410 | # define winFileBusyTimeout(pDbFd) 0 |
| 47411 | #endif |
| 47412 | |
| 47413 | /* |
| 47414 | ** The winVfsAppData structure is used for the pAppData member for all of the |
| 47415 | ** Win32 VFS variants. |
| 47416 | */ |
| @@ -47623,10 +47841,16 @@ | |
| 47841 | #endif |
| 47842 | |
| 47843 | #define osGetFullPathNameW ((DWORD(WINAPI*)(LPCWSTR,DWORD,LPWSTR, \ |
| 47844 | LPWSTR*))aSyscall[25].pCurrent) |
| 47845 | |
| 47846 | /* |
| 47847 | ** For GetLastError(), MSDN says: |
| 47848 | ** |
| 47849 | ** Minimum supported client: Windows XP [desktop apps | UWP apps] |
| 47850 | ** Minimum supported server: Windows Server 2003 [desktop apps | UWP apps] |
| 47851 | */ |
| 47852 | { "GetLastError", (SYSCALL)GetLastError, 0 }, |
| 47853 | |
| 47854 | #define osGetLastError ((DWORD(WINAPI*)(VOID))aSyscall[26].pCurrent) |
| 47855 | |
| 47856 | #if !defined(SQLITE_OMIT_LOAD_EXTENSION) |
| @@ -47905,15 +48129,17 @@ | |
| 48129 | #endif |
| 48130 | |
| 48131 | #define osCreateEventExW ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,LPCWSTR, \ |
| 48132 | DWORD,DWORD))aSyscall[62].pCurrent) |
| 48133 | |
| 48134 | /* |
| 48135 | ** For WaitForSingleObject(), MSDN says: |
| 48136 | ** |
| 48137 | ** Minimum supported client: Windows XP [desktop apps | UWP apps] |
| 48138 | ** Minimum supported server: Windows Server 2003 [desktop apps | UWP apps] |
| 48139 | */ |
| 48140 | { "WaitForSingleObject", (SYSCALL)WaitForSingleObject, 0 }, |
| 48141 | |
| 48142 | #define osWaitForSingleObject ((DWORD(WINAPI*)(HANDLE, \ |
| 48143 | DWORD))aSyscall[63].pCurrent) |
| 48144 | |
| 48145 | #if !SQLITE_OS_WINCE |
| @@ -48056,10 +48282,44 @@ | |
| 48282 | #endif |
| 48283 | |
| 48284 | #define osFlushViewOfFile \ |
| 48285 | ((BOOL(WINAPI*)(LPCVOID,SIZE_T))aSyscall[79].pCurrent) |
| 48286 | |
| 48287 | /* |
| 48288 | ** If SQLITE_ENABLE_SETLK_TIMEOUT is defined, we require CreateEvent() |
| 48289 | ** to implement blocking locks with timeouts. MSDN says: |
| 48290 | ** |
| 48291 | ** Minimum supported client: Windows XP [desktop apps | UWP apps] |
| 48292 | ** Minimum supported server: Windows Server 2003 [desktop apps | UWP apps] |
| 48293 | */ |
| 48294 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 48295 | { "CreateEvent", (SYSCALL)CreateEvent, 0 }, |
| 48296 | #else |
| 48297 | { "CreateEvent", (SYSCALL)0, 0 }, |
| 48298 | #endif |
| 48299 | |
| 48300 | #define osCreateEvent ( \ |
| 48301 | (HANDLE(WINAPI*) (LPSECURITY_ATTRIBUTES,BOOL,BOOL,LPCSTR)) \ |
| 48302 | aSyscall[80].pCurrent \ |
| 48303 | ) |
| 48304 | |
| 48305 | /* |
| 48306 | ** If SQLITE_ENABLE_SETLK_TIMEOUT is defined, we require CancelIo() |
| 48307 | ** for the case where a timeout expires and a lock request must be |
| 48308 | ** cancelled. |
| 48309 | ** |
| 48310 | ** Minimum supported client: Windows XP [desktop apps | UWP apps] |
| 48311 | ** Minimum supported server: Windows Server 2003 [desktop apps | UWP apps] |
| 48312 | */ |
| 48313 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 48314 | { "CancelIo", (SYSCALL)CancelIo, 0 }, |
| 48315 | #else |
| 48316 | { "CancelIo", (SYSCALL)0, 0 }, |
| 48317 | #endif |
| 48318 | |
| 48319 | #define osCancelIo ((BOOL(WINAPI*)(HANDLE))aSyscall[81].pCurrent) |
| 48320 | |
| 48321 | }; /* End of the overrideable system calls */ |
| 48322 | |
| 48323 | /* |
| 48324 | ** This is the xSetSystemCall() method of sqlite3_vfs for all of the |
| 48325 | ** "win32" VFSes. Return SQLITE_OK upon successfully updating the |
| @@ -48354,11 +48614,13 @@ | |
| 48614 | (sInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) ? 2 : 1, 0); |
| 48615 | #endif |
| 48616 | } |
| 48617 | return osInterlockedCompareExchange(&sqlite3_os_type, 2, 2)==2; |
| 48618 | #elif SQLITE_TEST |
| 48619 | return osInterlockedCompareExchange(&sqlite3_os_type, 2, 2)==2 |
| 48620 | || osInterlockedCompareExchange(&sqlite3_os_type, 0, 0)==0 |
| 48621 | ; |
| 48622 | #else |
| 48623 | /* |
| 48624 | ** NOTE: All sub-platforms where the GetVersionEx[AW] functions are |
| 48625 | ** deprecated are always assumed to be based on the NT kernel. |
| 48626 | */ |
| @@ -49440,10 +49702,89 @@ | |
| 49702 | return osLockFile(*phFile, offsetLow, offsetHigh, numBytesLow, |
| 49703 | numBytesHigh); |
| 49704 | } |
| 49705 | #endif |
| 49706 | } |
| 49707 | |
| 49708 | /* |
| 49709 | ** Lock a region of nByte bytes starting at offset offset of file hFile. |
| 49710 | ** Take an EXCLUSIVE lock if parameter bExclusive is true, or a SHARED lock |
| 49711 | ** otherwise. If nMs is greater than zero and the lock cannot be obtained |
| 49712 | ** immediately, block for that many ms before giving up. |
| 49713 | ** |
| 49714 | ** This function returns SQLITE_OK if the lock is obtained successfully. If |
| 49715 | ** some other process holds the lock, SQLITE_BUSY is returned if nMs==0, or |
| 49716 | ** SQLITE_BUSY_TIMEOUT otherwise. Or, if an error occurs, SQLITE_IOERR. |
| 49717 | */ |
| 49718 | static int winHandleLockTimeout( |
| 49719 | HANDLE hFile, |
| 49720 | DWORD offset, |
| 49721 | DWORD nByte, |
| 49722 | int bExcl, |
| 49723 | DWORD nMs |
| 49724 | ){ |
| 49725 | DWORD flags = LOCKFILE_FAIL_IMMEDIATELY | (bExcl?LOCKFILE_EXCLUSIVE_LOCK:0); |
| 49726 | int rc = SQLITE_OK; |
| 49727 | BOOL ret; |
| 49728 | |
| 49729 | if( !osIsNT() ){ |
| 49730 | ret = winLockFile(&hFile, flags, offset, 0, nByte, 0); |
| 49731 | }else{ |
| 49732 | OVERLAPPED ovlp; |
| 49733 | memset(&ovlp, 0, sizeof(OVERLAPPED)); |
| 49734 | ovlp.Offset = offset; |
| 49735 | |
| 49736 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 49737 | if( nMs!=0 ){ |
| 49738 | flags &= ~LOCKFILE_FAIL_IMMEDIATELY; |
| 49739 | } |
| 49740 | ovlp.hEvent = osCreateEvent(NULL, TRUE, FALSE, NULL); |
| 49741 | if( ovlp.hEvent==NULL ){ |
| 49742 | return SQLITE_IOERR_LOCK; |
| 49743 | } |
| 49744 | #endif |
| 49745 | |
| 49746 | ret = osLockFileEx(hFile, flags, 0, nByte, 0, &ovlp); |
| 49747 | |
| 49748 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 49749 | /* If SQLITE_ENABLE_SETLK_TIMEOUT is defined, then the file-handle was |
| 49750 | ** opened with FILE_FLAG_OVERHEAD specified. In this case, the call to |
| 49751 | ** LockFileEx() may fail because the request is still pending. This can |
| 49752 | ** happen even if LOCKFILE_FAIL_IMMEDIATELY was specified. |
| 49753 | ** |
| 49754 | ** If nMs is 0, then LOCKFILE_FAIL_IMMEDIATELY was set in the flags |
| 49755 | ** passed to LockFileEx(). In this case, if the operation is pending, |
| 49756 | ** block indefinitely until it is finished. |
| 49757 | ** |
| 49758 | ** Otherwise, wait for up to nMs ms for the operation to finish. nMs |
| 49759 | ** may be set to INFINITE. |
| 49760 | */ |
| 49761 | if( !ret && GetLastError()==ERROR_IO_PENDING ){ |
| 49762 | DWORD nDelay = (nMs==0 ? INFINITE : nMs); |
| 49763 | DWORD res = osWaitForSingleObject(ovlp.hEvent, nDelay); |
| 49764 | if( res==WAIT_OBJECT_0 ){ |
| 49765 | ret = TRUE; |
| 49766 | }else if( res==WAIT_TIMEOUT ){ |
| 49767 | rc = SQLITE_BUSY_TIMEOUT; |
| 49768 | }else{ |
| 49769 | /* Some other error has occurred */ |
| 49770 | rc = SQLITE_IOERR_LOCK; |
| 49771 | } |
| 49772 | |
| 49773 | /* If it is still pending, cancel the LockFileEx() call. */ |
| 49774 | osCancelIo(hFile); |
| 49775 | } |
| 49776 | |
| 49777 | osCloseHandle(ovlp.hEvent); |
| 49778 | #endif |
| 49779 | } |
| 49780 | |
| 49781 | if( rc==SQLITE_OK && !ret ){ |
| 49782 | rc = SQLITE_BUSY; |
| 49783 | } |
| 49784 | return rc; |
| 49785 | } |
| 49786 | |
| 49787 | /* |
| 49788 | ** Unlock a file region. |
| 49789 | */ |
| 49790 | static BOOL winUnlockFile( |
| @@ -49471,10 +49812,18 @@ | |
| 49812 | return osUnlockFile(*phFile, offsetLow, offsetHigh, numBytesLow, |
| 49813 | numBytesHigh); |
| 49814 | } |
| 49815 | #endif |
| 49816 | } |
| 49817 | |
| 49818 | /* |
| 49819 | ** Remove an nByte lock starting at offset iOff from HANDLE h. |
| 49820 | */ |
| 49821 | static int winHandleUnlock(HANDLE h, int iOff, int nByte){ |
| 49822 | BOOL ret = winUnlockFile(&h, iOff, 0, nByte, 0); |
| 49823 | return (ret ? SQLITE_OK : SQLITE_IOERR_UNLOCK); |
| 49824 | } |
| 49825 | |
| 49826 | /***************************************************************************** |
| 49827 | ** The next group of routines implement the I/O methods specified |
| 49828 | ** by the sqlite3_io_methods object. |
| 49829 | ******************************************************************************/ |
| @@ -49485,69 +49834,73 @@ | |
| 49834 | #ifndef INVALID_SET_FILE_POINTER |
| 49835 | # define INVALID_SET_FILE_POINTER ((DWORD)-1) |
| 49836 | #endif |
| 49837 | |
| 49838 | /* |
| 49839 | ** Seek the file handle h to offset nByte of the file. |
| 49840 | ** |
| 49841 | ** If successful, return SQLITE_OK. Or, if an error occurs, return an SQLite |
| 49842 | ** error code. |
| 49843 | */ |
| 49844 | static int winHandleSeek(HANDLE h, sqlite3_int64 iOffset){ |
| 49845 | int rc = SQLITE_OK; /* Return value */ |
| 49846 | |
| 49847 | #if !SQLITE_OS_WINRT |
| 49848 | LONG upperBits; /* Most sig. 32 bits of new offset */ |
| 49849 | LONG lowerBits; /* Least sig. 32 bits of new offset */ |
| 49850 | DWORD dwRet; /* Value returned by SetFilePointer() */ |
| 49851 | |
| 49852 | upperBits = (LONG)((iOffset>>32) & 0x7fffffff); |
| 49853 | lowerBits = (LONG)(iOffset & 0xffffffff); |
| 49854 | |
| 49855 | dwRet = osSetFilePointer(h, lowerBits, &upperBits, FILE_BEGIN); |
| 49856 | |
| 49857 | /* API oddity: If successful, SetFilePointer() returns a dword |
| 49858 | ** containing the lower 32-bits of the new file-offset. Or, if it fails, |
| 49859 | ** it returns INVALID_SET_FILE_POINTER. However according to MSDN, |
| 49860 | ** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine |
| 49861 | ** whether an error has actually occurred, it is also necessary to call |
| 49862 | ** GetLastError(). */ |
| 49863 | if( dwRet==INVALID_SET_FILE_POINTER ){ |
| 49864 | DWORD lastErrno = osGetLastError(); |
| 49865 | if( lastErrno!=NO_ERROR ){ |
| 49866 | rc = SQLITE_IOERR_SEEK; |
| 49867 | } |
| 49868 | } |
| 49869 | #else |
| 49870 | /* This implementation works for WinRT. */ |
| 49871 | LARGE_INTEGER x; /* The new offset */ |
| 49872 | BOOL bRet; /* Value returned by SetFilePointerEx() */ |
| 49873 | |
| 49874 | x.QuadPart = iOffset; |
| 49875 | bRet = osSetFilePointerEx(h, x, 0, FILE_BEGIN); |
| 49876 | |
| 49877 | if(!bRet){ |
| 49878 | rc = SQLITE_IOERR_SEEK; |
| 49879 | } |
| 49880 | #endif |
| 49881 | |
| 49882 | OSTRACE(("SEEK file=%p, offset=%lld rc=%s\n", h, iOffset, sqlite3ErrName(rc))); |
| 49883 | return rc; |
| 49884 | } |
| 49885 | |
| 49886 | /* |
| 49887 | ** Move the current position of the file handle passed as the first |
| 49888 | ** argument to offset iOffset within the file. If successful, return 0. |
| 49889 | ** Otherwise, set pFile->lastErrno and return non-zero. |
| 49890 | */ |
| 49891 | static int winSeekFile(winFile *pFile, sqlite3_int64 iOffset){ |
| 49892 | int rc; |
| 49893 | |
| 49894 | rc = winHandleSeek(pFile->h, iOffset); |
| 49895 | if( rc!=SQLITE_OK ){ |
| 49896 | pFile->lastErrno = osGetLastError(); |
| 49897 | winLogError(rc, pFile->lastErrno, "winSeekFile", pFile->zPath); |
| 49898 | } |
| 49899 | return rc; |
| 49900 | } |
| 49901 | |
| 49902 | |
| 49903 | #if SQLITE_MAX_MMAP_SIZE>0 |
| 49904 | /* Forward references to VFS helper methods used for memory mapped files */ |
| 49905 | static int winMapfile(winFile*, sqlite3_int64); |
| 49906 | static int winUnmapfile(winFile*); |
| @@ -49803,10 +50156,64 @@ | |
| 50156 | } |
| 50157 | OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n", |
| 50158 | osGetCurrentProcessId(), pFile, pFile->h)); |
| 50159 | return SQLITE_OK; |
| 50160 | } |
| 50161 | |
| 50162 | /* |
| 50163 | ** Truncate the file opened by handle h to nByte bytes in size. |
| 50164 | */ |
| 50165 | static int winHandleTruncate(HANDLE h, sqlite3_int64 nByte){ |
| 50166 | int rc = SQLITE_OK; /* Return code */ |
| 50167 | rc = winHandleSeek(h, nByte); |
| 50168 | if( rc==SQLITE_OK ){ |
| 50169 | if( 0==osSetEndOfFile(h) ){ |
| 50170 | rc = SQLITE_IOERR_TRUNCATE; |
| 50171 | } |
| 50172 | } |
| 50173 | return rc; |
| 50174 | } |
| 50175 | |
| 50176 | /* |
| 50177 | ** Determine the size in bytes of the file opened by the handle passed as |
| 50178 | ** the first argument. |
| 50179 | */ |
| 50180 | static int winHandleSize(HANDLE h, sqlite3_int64 *pnByte){ |
| 50181 | int rc = SQLITE_OK; |
| 50182 | |
| 50183 | #if SQLITE_OS_WINRT |
| 50184 | FILE_STANDARD_INFO info; |
| 50185 | BOOL b; |
| 50186 | b = osGetFileInformationByHandleEx(h, FileStandardInfo, &info, sizeof(info)); |
| 50187 | if( b ){ |
| 50188 | *pnByte = info.EndOfFile.QuadPart; |
| 50189 | }else{ |
| 50190 | rc = SQLITE_IOERR_FSTAT; |
| 50191 | } |
| 50192 | #else |
| 50193 | DWORD upperBits = 0; |
| 50194 | DWORD lowerBits = 0; |
| 50195 | |
| 50196 | assert( pnByte ); |
| 50197 | lowerBits = osGetFileSize(h, &upperBits); |
| 50198 | *pnByte = (((sqlite3_int64)upperBits)<<32) + lowerBits; |
| 50199 | if( lowerBits==INVALID_FILE_SIZE && osGetLastError()!=NO_ERROR ){ |
| 50200 | rc = SQLITE_IOERR_FSTAT; |
| 50201 | } |
| 50202 | #endif |
| 50203 | |
| 50204 | return rc; |
| 50205 | } |
| 50206 | |
| 50207 | /* |
| 50208 | ** Close the handle passed as the only argument. |
| 50209 | */ |
| 50210 | static void winHandleClose(HANDLE h){ |
| 50211 | if( h!=INVALID_HANDLE_VALUE ){ |
| 50212 | osCloseHandle(h); |
| 50213 | } |
| 50214 | } |
| 50215 | |
| 50216 | /* |
| 50217 | ** Truncate an open file to a specified size |
| 50218 | */ |
| 50219 | static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){ |
| @@ -50059,31 +50466,32 @@ | |
| 50466 | /* |
| 50467 | ** Acquire a reader lock. |
| 50468 | ** Different API routines are called depending on whether or not this |
| 50469 | ** is Win9x or WinNT. |
| 50470 | */ |
| 50471 | static int winGetReadLock(winFile *pFile, int bBlock){ |
| 50472 | int res; |
| 50473 | DWORD mask = ~(bBlock ? LOCKFILE_FAIL_IMMEDIATELY : 0); |
| 50474 | OSTRACE(("READ-LOCK file=%p, lock=%d\n", pFile->h, pFile->locktype)); |
| 50475 | if( osIsNT() ){ |
| 50476 | #if SQLITE_OS_WINCE |
| 50477 | /* |
| 50478 | ** NOTE: Windows CE is handled differently here due its lack of the Win32 |
| 50479 | ** API LockFileEx. |
| 50480 | */ |
| 50481 | res = winceLockFile(&pFile->h, SHARED_FIRST, 0, 1, 0); |
| 50482 | #else |
| 50483 | res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS&mask, SHARED_FIRST, 0, |
| 50484 | SHARED_SIZE, 0); |
| 50485 | #endif |
| 50486 | } |
| 50487 | #ifdef SQLITE_WIN32_HAS_ANSI |
| 50488 | else{ |
| 50489 | int lk; |
| 50490 | sqlite3_randomness(sizeof(lk), &lk); |
| 50491 | pFile->sharedLockByte = (short)((lk & 0x7fffffff)%(SHARED_SIZE - 1)); |
| 50492 | res = winLockFile(&pFile->h, SQLITE_LOCKFILE_FLAGS&mask, |
| 50493 | SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0); |
| 50494 | } |
| 50495 | #endif |
| 50496 | if( res == 0 ){ |
| 50497 | pFile->lastErrno = osGetLastError(); |
| @@ -50174,50 +50582,66 @@ | |
| 50582 | */ |
| 50583 | assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK ); |
| 50584 | assert( locktype!=PENDING_LOCK ); |
| 50585 | assert( locktype!=RESERVED_LOCK || pFile->locktype==SHARED_LOCK ); |
| 50586 | |
| 50587 | /* Lock the PENDING_LOCK byte if we need to acquire an EXCLUSIVE lock or |
| 50588 | ** a SHARED lock. If we are acquiring a SHARED lock, the acquisition of |
| 50589 | ** the PENDING_LOCK byte is temporary. |
| 50590 | */ |
| 50591 | newLocktype = pFile->locktype; |
| 50592 | if( locktype==SHARED_LOCK |
| 50593 | || (locktype==EXCLUSIVE_LOCK && pFile->locktype==RESERVED_LOCK) |
| 50594 | ){ |
| 50595 | int cnt = 3; |
| 50596 | |
| 50597 | /* Flags for the LockFileEx() call. This should be an exclusive lock if |
| 50598 | ** this call is to obtain EXCLUSIVE, or a shared lock if this call is to |
| 50599 | ** obtain SHARED. */ |
| 50600 | int flags = LOCKFILE_FAIL_IMMEDIATELY; |
| 50601 | if( locktype==EXCLUSIVE_LOCK ){ |
| 50602 | flags |= LOCKFILE_EXCLUSIVE_LOCK; |
| 50603 | } |
| 50604 | while( cnt>0 ){ |
| 50605 | /* Try 3 times to get the pending lock. This is needed to work |
| 50606 | ** around problems caused by indexing and/or anti-virus software on |
| 50607 | ** Windows systems. |
| 50608 | ** |
| 50609 | ** If you are using this code as a model for alternative VFSes, do not |
| 50610 | ** copy this retry logic. It is a hack intended for Windows only. */ |
| 50611 | res = winLockFile(&pFile->h, flags, PENDING_BYTE, 0, 1, 0); |
| 50612 | if( res ) break; |
| 50613 | |
| 50614 | lastErrno = osGetLastError(); |
| 50615 | OSTRACE(("LOCK-PENDING-FAIL file=%p, count=%d, result=%d\n", |
| 50616 | pFile->h, cnt, res |
| 50617 | )); |
| 50618 | |
| 50619 | if( lastErrno==ERROR_INVALID_HANDLE ){ |
| 50620 | pFile->lastErrno = lastErrno; |
| 50621 | rc = SQLITE_IOERR_LOCK; |
| 50622 | OSTRACE(("LOCK-FAIL file=%p, count=%d, rc=%s\n", |
| 50623 | pFile->h, cnt, sqlite3ErrName(rc) |
| 50624 | )); |
| 50625 | return rc; |
| 50626 | } |
| 50627 | |
| 50628 | cnt--; |
| 50629 | if( cnt>0 ) sqlite3_win32_sleep(1); |
| 50630 | } |
| 50631 | gotPendingLock = res; |
| 50632 | } |
| 50633 | |
| 50634 | /* Acquire a shared lock |
| 50635 | */ |
| 50636 | if( locktype==SHARED_LOCK && res ){ |
| 50637 | assert( pFile->locktype==NO_LOCK ); |
| 50638 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 50639 | res = winGetReadLock(pFile, pFile->bBlockOnConnect); |
| 50640 | #else |
| 50641 | res = winGetReadLock(pFile, 0); |
| 50642 | #endif |
| 50643 | if( res ){ |
| 50644 | newLocktype = SHARED_LOCK; |
| 50645 | }else{ |
| 50646 | lastErrno = osGetLastError(); |
| 50647 | } |
| @@ -50251,11 +50675,11 @@ | |
| 50675 | SHARED_SIZE, 0); |
| 50676 | if( res ){ |
| 50677 | newLocktype = EXCLUSIVE_LOCK; |
| 50678 | }else{ |
| 50679 | lastErrno = osGetLastError(); |
| 50680 | winGetReadLock(pFile, 0); |
| 50681 | } |
| 50682 | } |
| 50683 | |
| 50684 | /* If we are holding a PENDING lock that ought to be released, then |
| 50685 | ** release it now. |
| @@ -50331,11 +50755,11 @@ | |
| 50755 | OSTRACE(("UNLOCK file=%p, oldLock=%d(%d), newLock=%d\n", |
| 50756 | pFile->h, pFile->locktype, pFile->sharedLockByte, locktype)); |
| 50757 | type = pFile->locktype; |
| 50758 | if( type>=EXCLUSIVE_LOCK ){ |
| 50759 | winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0); |
| 50760 | if( locktype==SHARED_LOCK && !winGetReadLock(pFile, 0) ){ |
| 50761 | /* This should never happen. We should always be able to |
| 50762 | ** reacquire the read lock */ |
| 50763 | rc = winLogError(SQLITE_IOERR_UNLOCK, osGetLastError(), |
| 50764 | "winUnlock", pFile->zPath); |
| 50765 | } |
| @@ -50541,10 +50965,32 @@ | |
| 50965 | } |
| 50966 | OSTRACE(("FCNTL file=%p, rc=%s\n", pFile->h, sqlite3ErrName(rc))); |
| 50967 | return rc; |
| 50968 | } |
| 50969 | #endif |
| 50970 | |
| 50971 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 50972 | case SQLITE_FCNTL_LOCK_TIMEOUT: { |
| 50973 | int iOld = pFile->iBusyTimeout; |
| 50974 | int iNew = *(int*)pArg; |
| 50975 | #if SQLITE_ENABLE_SETLK_TIMEOUT==1 |
| 50976 | pFile->iBusyTimeout = (iNew < 0) ? INFINITE : (DWORD)iNew; |
| 50977 | #elif SQLITE_ENABLE_SETLK_TIMEOUT==2 |
| 50978 | pFile->iBusyTimeout = (DWORD)(!!iNew); |
| 50979 | #else |
| 50980 | # error "SQLITE_ENABLE_SETLK_TIMEOUT must be set to 1 or 2" |
| 50981 | #endif |
| 50982 | *(int*)pArg = iOld; |
| 50983 | return SQLITE_OK; |
| 50984 | } |
| 50985 | case SQLITE_FCNTL_BLOCK_ON_CONNECT: { |
| 50986 | int iNew = *(int*)pArg; |
| 50987 | pFile->bBlockOnConnect = iNew; |
| 50988 | return SQLITE_OK; |
| 50989 | } |
| 50990 | #endif /* SQLITE_ENABLE_SETLK_TIMEOUT */ |
| 50991 | |
| 50992 | } |
| 50993 | OSTRACE(("FCNTL file=%p, rc=SQLITE_NOTFOUND\n", pFile->h)); |
| 50994 | return SQLITE_NOTFOUND; |
| 50995 | } |
| 50996 | |
| @@ -50621,36 +51067,39 @@ | |
| 51067 | ** nRef |
| 51068 | ** pNext |
| 51069 | ** |
| 51070 | ** The following fields are read-only after the object is created: |
| 51071 | ** |
| 51072 | ** zFilename |
| 51073 | ** |
| 51074 | ** Either winShmNode.mutex must be held or winShmNode.nRef==0 and |
| 51075 | ** winShmMutexHeld() is true when reading or writing any other field |
| 51076 | ** in this structure. |
| 51077 | ** |
| 51078 | ** File-handle hSharedShm is used to (a) take the DMS lock, (b) truncate |
| 51079 | ** the *-shm file if the DMS-locking protocol demands it, and (c) map |
| 51080 | ** regions of the *-shm file into memory using MapViewOfFile() or |
| 51081 | ** similar. Other locks are taken by individual clients using the |
| 51082 | ** winShm.hShm handles. |
| 51083 | */ |
| 51084 | struct winShmNode { |
| 51085 | sqlite3_mutex *mutex; /* Mutex to access this object */ |
| 51086 | char *zFilename; /* Name of the file */ |
| 51087 | HANDLE hSharedShm; /* File handle open on zFilename */ |
| 51088 | |
| 51089 | int isUnlocked; /* DMS lock has not yet been obtained */ |
| 51090 | int isReadonly; /* True if read-only */ |
| 51091 | int szRegion; /* Size of shared-memory regions */ |
| 51092 | int nRegion; /* Size of array apRegion */ |
| 51093 | |
| 51094 | struct ShmRegion { |
| 51095 | HANDLE hMap; /* File handle from CreateFileMapping */ |
| 51096 | void *pMap; |
| 51097 | } *aRegion; |
| 51098 | DWORD lastErrno; /* The Windows errno from the last I/O error */ |
| 51099 | |
| 51100 | int nRef; /* Number of winShm objects pointing to this */ |
| 51101 | winShmNode *pNext; /* Next in list of all winShmNode objects */ |
| 51102 | #if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) |
| 51103 | u8 nextShmId; /* Next available winShm.id value */ |
| 51104 | #endif |
| 51105 | }; |
| @@ -50662,27 +51111,19 @@ | |
| 51111 | */ |
| 51112 | static winShmNode *winShmNodeList = 0; |
| 51113 | |
| 51114 | /* |
| 51115 | ** Structure used internally by this VFS to record the state of an |
| 51116 | ** open shared memory connection. There is one such structure for each |
| 51117 | ** winFile open on a wal mode database. |
| 51118 | */ |
| 51119 | struct winShm { |
| 51120 | winShmNode *pShmNode; /* The underlying winShmNode object */ |
| 51121 | u16 sharedMask; /* Mask of shared locks held */ |
| 51122 | u16 exclMask; /* Mask of exclusive locks held */ |
| 51123 | HANDLE hShm; /* File-handle on *-shm file. For locking. */ |
| 51124 | int bReadonly; /* True if hShm is opened read-only */ |
| 51125 | #if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) |
| 51126 | u8 id; /* Id of this connection with its winShmNode */ |
| 51127 | #endif |
| 51128 | }; |
| 51129 | |
| @@ -50690,54 +51131,10 @@ | |
| 51131 | ** Constants used for locking |
| 51132 | */ |
| 51133 | #define WIN_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */ |
| 51134 | #define WIN_SHM_DMS (WIN_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */ |
| 51135 | |
| 51136 | /* Forward references to VFS methods */ |
| 51137 | static int winOpen(sqlite3_vfs*,const char*,sqlite3_file*,int,int*); |
| 51138 | static int winDelete(sqlite3_vfs *,const char*,int); |
| 51139 | |
| 51140 | /* |
| @@ -50765,15 +51162,11 @@ | |
| 51162 | bRc = osCloseHandle(p->aRegion[i].hMap); |
| 51163 | OSTRACE(("SHM-PURGE-CLOSE pid=%lu, region=%d, rc=%s\n", |
| 51164 | osGetCurrentProcessId(), i, bRc ? "ok" : "failed")); |
| 51165 | UNUSED_VARIABLE_VALUE(bRc); |
| 51166 | } |
| 51167 | winHandleClose(p->hSharedShm); |
| 51168 | if( deleteFlag ){ |
| 51169 | SimulateIOErrorBenign(1); |
| 51170 | sqlite3BeginBenignMalloc(); |
| 51171 | winDelete(pVfs, p->zFilename, 0); |
| 51172 | sqlite3EndBenignMalloc(); |
| @@ -50787,46 +51180,166 @@ | |
| 51180 | } |
| 51181 | } |
| 51182 | } |
| 51183 | |
| 51184 | /* |
| 51185 | ** The DMS lock has not yet been taken on the shm file associated with |
| 51186 | ** pShmNode. Take the lock. Truncate the *-shm file if required. |
| 51187 | ** Return SQLITE_OK if successful, or an SQLite error code otherwise. |
| 51188 | */ |
| 51189 | static int winLockSharedMemory(winShmNode *pShmNode, DWORD nMs){ |
| 51190 | HANDLE h = pShmNode->hSharedShm; |
| 51191 | int rc = SQLITE_OK; |
| 51192 | |
| 51193 | assert( sqlite3_mutex_held(pShmNode->mutex) ); |
| 51194 | rc = winHandleLockTimeout(h, WIN_SHM_DMS, 1, 1, 0); |
| 51195 | if( rc==SQLITE_OK ){ |
| 51196 | /* We have an EXCLUSIVE lock on the DMS byte. This means that this |
| 51197 | ** is the first process to open the file. Truncate it to zero bytes |
| 51198 | ** in this case. */ |
| 51199 | if( pShmNode->isReadonly ){ |
| 51200 | rc = SQLITE_READONLY_CANTINIT; |
| 51201 | }else{ |
| 51202 | rc = winHandleTruncate(h, 0); |
| 51203 | } |
| 51204 | |
| 51205 | /* Release the EXCLUSIVE lock acquired above. */ |
| 51206 | winUnlockFile(&h, WIN_SHM_DMS, 0, 1, 0); |
| 51207 | }else if( (rc & 0xFF)==SQLITE_BUSY ){ |
| 51208 | rc = SQLITE_OK; |
| 51209 | } |
| 51210 | |
| 51211 | if( rc==SQLITE_OK ){ |
| 51212 | /* Take a SHARED lock on the DMS byte. */ |
| 51213 | rc = winHandleLockTimeout(h, WIN_SHM_DMS, 1, 0, nMs); |
| 51214 | if( rc==SQLITE_OK ){ |
| 51215 | pShmNode->isUnlocked = 0; |
| 51216 | } |
| 51217 | } |
| 51218 | |
| 51219 | return rc; |
| 51220 | } |
| 51221 | |
| 51222 | |
| 51223 | /* |
| 51224 | ** Convert a UTF-8 filename into whatever form the underlying |
| 51225 | ** operating system wants filenames in. Space to hold the result |
| 51226 | ** is obtained from malloc and must be freed by the calling |
| 51227 | ** function. |
| 51228 | */ |
| 51229 | static void *winConvertFromUtf8Filename(const char *zFilename){ |
| 51230 | void *zConverted = 0; |
| 51231 | if( osIsNT() ){ |
| 51232 | zConverted = winUtf8ToUnicode(zFilename); |
| 51233 | } |
| 51234 | #ifdef SQLITE_WIN32_HAS_ANSI |
| 51235 | else{ |
| 51236 | zConverted = winUtf8ToMbcs(zFilename, osAreFileApisANSI()); |
| 51237 | } |
| 51238 | #endif |
| 51239 | /* caller will handle out of memory */ |
| 51240 | return zConverted; |
| 51241 | } |
| 51242 | |
| 51243 | /* |
| 51244 | ** This function is used to open a handle on a *-shm file. |
| 51245 | ** |
| 51246 | ** If SQLITE_ENABLE_SETLK_TIMEOUT is defined at build time, then the file |
| 51247 | ** is opened with FILE_FLAG_OVERLAPPED specified. If not, it is not. |
| 51248 | */ |
| 51249 | static int winHandleOpen( |
| 51250 | const char *zUtf8, /* File to open */ |
| 51251 | int *pbReadonly, /* IN/OUT: True for readonly handle */ |
| 51252 | HANDLE *ph /* OUT: New HANDLE for file */ |
| 51253 | ){ |
| 51254 | int rc = SQLITE_OK; |
| 51255 | void *zConverted = 0; |
| 51256 | int bReadonly = *pbReadonly; |
| 51257 | HANDLE h = INVALID_HANDLE_VALUE; |
| 51258 | |
| 51259 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 51260 | const DWORD flag_overlapped = FILE_FLAG_OVERLAPPED; |
| 51261 | #else |
| 51262 | const DWORD flag_overlapped = 0; |
| 51263 | #endif |
| 51264 | |
| 51265 | /* Convert the filename to the system encoding. */ |
| 51266 | zConverted = winConvertFromUtf8Filename(zUtf8); |
| 51267 | if( zConverted==0 ){ |
| 51268 | OSTRACE(("OPEN name=%s, rc=SQLITE_IOERR_NOMEM", zUtf8)); |
| 51269 | rc = SQLITE_IOERR_NOMEM_BKPT; |
| 51270 | goto winopenfile_out; |
| 51271 | } |
| 51272 | |
| 51273 | /* Ensure the file we are trying to open is not actually a directory. */ |
| 51274 | if( winIsDir(zConverted) ){ |
| 51275 | OSTRACE(("OPEN name=%s, rc=SQLITE_CANTOPEN_ISDIR", zUtf8)); |
| 51276 | rc = SQLITE_CANTOPEN_ISDIR; |
| 51277 | goto winopenfile_out; |
| 51278 | } |
| 51279 | |
| 51280 | /* TODO: platforms. |
| 51281 | ** TODO: retry-on-ioerr. |
| 51282 | */ |
| 51283 | if( osIsNT() ){ |
| 51284 | #if SQLITE_OS_WINRT |
| 51285 | CREATEFILE2_EXTENDED_PARAMETERS extendedParameters; |
| 51286 | memset(&extendedParameters, 0, sizeof(extendedParameters)); |
| 51287 | extendedParameters.dwSize = sizeof(extendedParameters); |
| 51288 | extendedParameters.dwFileAttributes = FILE_ATTRIBUTE_NORMAL; |
| 51289 | extendedParameters.dwFileFlags = flag_overlapped; |
| 51290 | extendedParameters.dwSecurityQosFlags = SECURITY_ANONYMOUS; |
| 51291 | h = osCreateFile2((LPCWSTR)zConverted, |
| 51292 | (GENERIC_READ | (bReadonly ? 0 : GENERIC_WRITE)),/* dwDesiredAccess */ |
| 51293 | FILE_SHARE_READ | FILE_SHARE_WRITE, /* dwShareMode */ |
| 51294 | OPEN_ALWAYS, /* dwCreationDisposition */ |
| 51295 | &extendedParameters |
| 51296 | ); |
| 51297 | #else |
| 51298 | h = osCreateFileW((LPCWSTR)zConverted, /* lpFileName */ |
| 51299 | (GENERIC_READ | (bReadonly ? 0 : GENERIC_WRITE)), /* dwDesiredAccess */ |
| 51300 | FILE_SHARE_READ | FILE_SHARE_WRITE, /* dwShareMode */ |
| 51301 | NULL, /* lpSecurityAttributes */ |
| 51302 | OPEN_ALWAYS, /* dwCreationDisposition */ |
| 51303 | FILE_ATTRIBUTE_NORMAL|flag_overlapped, |
| 51304 | NULL |
| 51305 | ); |
| 51306 | #endif |
| 51307 | }else{ |
| 51308 | /* Due to pre-processor directives earlier in this file, |
| 51309 | ** SQLITE_WIN32_HAS_ANSI is always defined if osIsNT() is false. */ |
| 51310 | #ifdef SQLITE_WIN32_HAS_ANSI |
| 51311 | h = osCreateFileA((LPCSTR)zConverted, |
| 51312 | (GENERIC_READ | (bReadonly ? 0 : GENERIC_WRITE)), /* dwDesiredAccess */ |
| 51313 | FILE_SHARE_READ | FILE_SHARE_WRITE, /* dwShareMode */ |
| 51314 | NULL, /* lpSecurityAttributes */ |
| 51315 | OPEN_ALWAYS, /* dwCreationDisposition */ |
| 51316 | FILE_ATTRIBUTE_NORMAL|flag_overlapped, |
| 51317 | NULL |
| 51318 | ); |
| 51319 | #endif |
| 51320 | } |
| 51321 | |
| 51322 | if( h==INVALID_HANDLE_VALUE ){ |
| 51323 | if( bReadonly==0 ){ |
| 51324 | bReadonly = 1; |
| 51325 | rc = winHandleOpen(zUtf8, &bReadonly, &h); |
| 51326 | }else{ |
| 51327 | rc = SQLITE_CANTOPEN_BKPT; |
| 51328 | } |
| 51329 | } |
| 51330 | |
| 51331 | winopenfile_out: |
| 51332 | sqlite3_free(zConverted); |
| 51333 | *pbReadonly = bReadonly; |
| 51334 | *ph = h; |
| 51335 | return rc; |
| 51336 | } |
| 51337 | |
| 51338 | |
| 51339 | /* |
| 51340 | ** Open the shared-memory area associated with database file pDbFd. |
| 51341 | */ |
| 51342 | static int winOpenSharedMemory(winFile *pDbFd){ |
| 51343 | struct winShm *p; /* The connection to be opened */ |
| 51344 | winShmNode *pShmNode = 0; /* The underlying mmapped file */ |
| 51345 | int rc = SQLITE_OK; /* Result code */ |
| @@ -50834,102 +51347,87 @@ | |
| 51347 | int nName; /* Size of zName in bytes */ |
| 51348 | |
| 51349 | assert( pDbFd->pShm==0 ); /* Not previously opened */ |
| 51350 | |
| 51351 | /* Allocate space for the new sqlite3_shm object. Also speculatively |
| 51352 | ** allocate space for a new winShmNode and filename. */ |
| 51353 | p = sqlite3MallocZero( sizeof(*p) ); |
| 51354 | if( p==0 ) return SQLITE_IOERR_NOMEM_BKPT; |
| 51355 | nName = sqlite3Strlen30(pDbFd->zPath); |
| 51356 | pNew = sqlite3MallocZero( sizeof(*pShmNode) + (i64)nName + 17 ); |
| 51357 | if( pNew==0 ){ |
| 51358 | sqlite3_free(p); |
| 51359 | return SQLITE_IOERR_NOMEM_BKPT; |
| 51360 | } |
| 51361 | pNew->zFilename = (char*)&pNew[1]; |
| 51362 | pNew->hSharedShm = INVALID_HANDLE_VALUE; |
| 51363 | pNew->isUnlocked = 1; |
| 51364 | sqlite3_snprintf(nName+15, pNew->zFilename, "%s-shm", pDbFd->zPath); |
| 51365 | sqlite3FileSuffix3(pDbFd->zPath, pNew->zFilename); |
| 51366 | |
| 51367 | /* Open a file-handle on the *-shm file for this connection. This file-handle |
| 51368 | ** is only used for locking. The mapping of the *-shm file is created using |
| 51369 | ** the shared file handle in winShmNode.hSharedShm. */ |
| 51370 | p->bReadonly = sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0); |
| 51371 | rc = winHandleOpen(pNew->zFilename, &p->bReadonly, &p->hShm); |
| 51372 | |
| 51373 | /* Look to see if there is an existing winShmNode that can be used. |
| 51374 | ** If no matching winShmNode currently exists, then create a new one. */ |
| 51375 | winShmEnterMutex(); |
| 51376 | for(pShmNode = winShmNodeList; pShmNode; pShmNode=pShmNode->pNext){ |
| 51377 | /* TBD need to come up with better match here. Perhaps |
| 51378 | ** use FILE_ID_BOTH_DIR_INFO Structure. */ |
| 51379 | if( sqlite3StrICmp(pShmNode->zFilename, pNew->zFilename)==0 ) break; |
| 51380 | } |
| 51381 | if( pShmNode==0 ){ |
| 51382 | pShmNode = pNew; |
| 51383 | |
| 51384 | /* Allocate a mutex for this winShmNode object, if one is required. */ |
| 51385 | if( sqlite3GlobalConfig.bCoreMutex ){ |
| 51386 | pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); |
| 51387 | if( pShmNode->mutex==0 ) rc = SQLITE_IOERR_NOMEM_BKPT; |
| 51388 | } |
| 51389 | |
| 51390 | /* Open a file-handle to use for mappings, and for the DMS lock. */ |
| 51391 | if( rc==SQLITE_OK ){ |
| 51392 | HANDLE h = INVALID_HANDLE_VALUE; |
| 51393 | pShmNode->isReadonly = p->bReadonly; |
| 51394 | rc = winHandleOpen(pNew->zFilename, &pShmNode->isReadonly, &h); |
| 51395 | pShmNode->hSharedShm = h; |
| 51396 | } |
| 51397 | |
| 51398 | /* If successful, link the new winShmNode into the global list. If an |
| 51399 | ** error occurred, free the object. */ |
| 51400 | if( rc==SQLITE_OK ){ |
| 51401 | pShmNode->pNext = winShmNodeList; |
| 51402 | winShmNodeList = pShmNode; |
| 51403 | pNew = 0; |
| 51404 | }else{ |
| 51405 | sqlite3_mutex_free(pShmNode->mutex); |
| 51406 | if( pShmNode->hSharedShm!=INVALID_HANDLE_VALUE ){ |
| 51407 | osCloseHandle(pShmNode->hSharedShm); |
| 51408 | } |
| 51409 | } |
| 51410 | } |
| 51411 | |
| 51412 | /* If no error has occurred, link the winShm object to the winShmNode and |
| 51413 | ** the winShm to pDbFd. */ |
| 51414 | if( rc==SQLITE_OK ){ |
| 51415 | p->pShmNode = pShmNode; |
| 51416 | pShmNode->nRef++; |
| 51417 | #if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE) |
| 51418 | p->id = pShmNode->nextShmId++; |
| 51419 | #endif |
| 51420 | pDbFd->pShm = p; |
| 51421 | }else if( p ){ |
| 51422 | winHandleClose(p->hShm); |
| 51423 | sqlite3_free(p); |
| 51424 | } |
| 51425 | |
| 51426 | assert( rc!=SQLITE_OK || pShmNode->isUnlocked==0 || pShmNode->nRegion==0 ); |
| 51427 | winShmLeaveMutex(); |
| 51428 | sqlite3_free(pNew); |
| 51429 | return rc; |
| 51430 | } |
| 51431 | |
| 51432 | /* |
| 51433 | ** Close a connection to shared-memory. Delete the underlying |
| @@ -50940,38 +51438,33 @@ | |
| 51438 | int deleteFlag /* Delete after closing if true */ |
| 51439 | ){ |
| 51440 | winFile *pDbFd; /* Database holding shared-memory */ |
| 51441 | winShm *p; /* The connection to be closed */ |
| 51442 | winShmNode *pShmNode; /* The underlying shared-memory file */ |
| 51443 | |
| 51444 | pDbFd = (winFile*)fd; |
| 51445 | p = pDbFd->pShm; |
| 51446 | if( p==0 ) return SQLITE_OK; |
| 51447 | if( p->hShm!=INVALID_HANDLE_VALUE ){ |
| 51448 | osCloseHandle(p->hShm); |
| 51449 | } |
| 51450 | |
| 51451 | pShmNode = p->pShmNode; |
| 51452 | winShmEnterMutex(); |
| 51453 | |
| 51454 | /* If pShmNode->nRef has reached 0, then close the underlying |
| 51455 | ** shared-memory file, too. */ |
| 51456 | assert( pShmNode->nRef>0 ); |
| 51457 | pShmNode->nRef--; |
| 51458 | if( pShmNode->nRef==0 ){ |
| 51459 | winShmPurge(pDbFd->pVfs, deleteFlag); |
| 51460 | } |
| 51461 | winShmLeaveMutex(); |
| 51462 | |
| 51463 | /* Free the connection p */ |
| 51464 | sqlite3_free(p); |
| 51465 | pDbFd->pShm = 0; |
| 51466 | return SQLITE_OK; |
| 51467 | } |
| 51468 | |
| 51469 | /* |
| 51470 | ** Change the lock state for a shared-memory segment. |
| @@ -50982,14 +51475,13 @@ | |
| 51475 | int n, /* Number of locks to acquire or release */ |
| 51476 | int flags /* What to do with the lock */ |
| 51477 | ){ |
| 51478 | winFile *pDbFd = (winFile*)fd; /* Connection holding shared memory */ |
| 51479 | winShm *p = pDbFd->pShm; /* The shared memory being locked */ |
| 51480 | winShmNode *pShmNode; |
| 51481 | int rc = SQLITE_OK; /* Result code */ |
| 51482 | u16 mask = (u16)((1U<<(ofst+n)) - (1U<<ofst)); /* Mask of locks to [un]take */ |
| 51483 | |
| 51484 | if( p==0 ) return SQLITE_IOERR_SHMLOCK; |
| 51485 | pShmNode = p->pShmNode; |
| 51486 | if( NEVER(pShmNode==0) ) return SQLITE_IOERR_SHMLOCK; |
| 51487 | |
| @@ -50999,89 +51491,86 @@ | |
| 51491 | || flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE) |
| 51492 | || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED) |
| 51493 | || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) ); |
| 51494 | assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 ); |
| 51495 | |
| 51496 | /* Check that, if this to be a blocking lock, no locks that occur later |
| 51497 | ** in the following list than the lock being obtained are already held: |
| 51498 | ** |
| 51499 | ** 1. Checkpointer lock (ofst==1). |
| 51500 | ** 2. Write lock (ofst==0). |
| 51501 | ** 3. Read locks (ofst>=3 && ofst<SQLITE_SHM_NLOCK). |
| 51502 | ** |
| 51503 | ** In other words, if this is a blocking lock, none of the locks that |
| 51504 | ** occur later in the above list than the lock being obtained may be |
| 51505 | ** held. |
| 51506 | ** |
| 51507 | ** It is not permitted to block on the RECOVER lock. |
| 51508 | */ |
| 51509 | #if defined(SQLITE_ENABLE_SETLK_TIMEOUT) && defined(SQLITE_DEBUG) |
| 51510 | { |
| 51511 | u16 lockMask = (p->exclMask|p->sharedMask); |
| 51512 | assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || ( |
| 51513 | (ofst!=2) /* not RECOVER */ |
| 51514 | && (ofst!=1 || lockMask==0 || lockMask==2) |
| 51515 | && (ofst!=0 || lockMask<3) |
| 51516 | && (ofst<3 || lockMask<(1<<ofst)) |
| 51517 | )); |
| 51518 | } |
| 51519 | #endif |
| 51520 | |
| 51521 | /* Check if there is any work to do. There are three cases: |
| 51522 | ** |
| 51523 | ** a) An unlock operation where there are locks to unlock, |
| 51524 | ** b) An shared lock where the requested lock is not already held |
| 51525 | ** c) An exclusive lock where the requested lock is not already held |
| 51526 | ** |
| 51527 | ** The SQLite core never requests an exclusive lock that it already holds. |
| 51528 | ** This is assert()ed immediately below. */ |
| 51529 | assert( flags!=(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK) |
| 51530 | || 0==(p->exclMask & mask) |
| 51531 | ); |
| 51532 | if( ((flags & SQLITE_SHM_UNLOCK) && ((p->exclMask|p->sharedMask) & mask)) |
| 51533 | || (flags==(SQLITE_SHM_SHARED|SQLITE_SHM_LOCK) && 0==(p->sharedMask & mask)) |
| 51534 | || (flags==(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK)) |
| 51535 | ){ |
| 51536 | |
| 51537 | if( flags & SQLITE_SHM_UNLOCK ){ |
| 51538 | /* Case (a) - unlock. */ |
| 51539 | |
| 51540 | assert( (p->exclMask & p->sharedMask)==0 ); |
| 51541 | assert( !(flags & SQLITE_SHM_EXCLUSIVE) || (p->exclMask & mask)==mask ); |
| 51542 | assert( !(flags & SQLITE_SHM_SHARED) || (p->sharedMask & mask)==mask ); |
| 51543 | |
| 51544 | rc = winHandleUnlock(p->hShm, ofst+WIN_SHM_BASE, n); |
| 51545 | |
| 51546 | /* If successful, also clear the bits in sharedMask/exclMask */ |
| 51547 | if( rc==SQLITE_OK ){ |
| 51548 | p->exclMask = (p->exclMask & ~mask); |
| 51549 | p->sharedMask = (p->sharedMask & ~mask); |
| 51550 | } |
| 51551 | }else{ |
| 51552 | int bExcl = ((flags & SQLITE_SHM_EXCLUSIVE) ? 1 : 0); |
| 51553 | DWORD nMs = winFileBusyTimeout(pDbFd); |
| 51554 | rc = winHandleLockTimeout(p->hShm, ofst+WIN_SHM_BASE, n, bExcl, nMs); |
| 51555 | if( rc==SQLITE_OK ){ |
| 51556 | if( bExcl ){ |
| 51557 | p->exclMask = (p->exclMask | mask); |
| 51558 | }else{ |
| 51559 | p->sharedMask = (p->sharedMask | mask); |
| 51560 | } |
| 51561 | } |
| 51562 | } |
| 51563 | } |
| 51564 | |
| 51565 | OSTRACE(( |
| 51566 | "SHM-LOCK(%d,%d,%d) pid=%lu, id=%d, sharedMask=%03x, exclMask=%03x," |
| 51567 | " rc=%s\n", |
| 51568 | ofst, n, flags, |
| 51569 | osGetCurrentProcessId(), p->id, p->sharedMask, p->exclMask, |
| 51570 | sqlite3ErrName(rc)) |
| 51571 | ); |
| 51572 | return rc; |
| 51573 | } |
| 51574 | |
| 51575 | /* |
| 51576 | ** Implement a memory barrier or memory fence on shared memory. |
| @@ -51139,17 +51628,19 @@ | |
| 51628 | } |
| 51629 | pShmNode = pShm->pShmNode; |
| 51630 | |
| 51631 | sqlite3_mutex_enter(pShmNode->mutex); |
| 51632 | if( pShmNode->isUnlocked ){ |
| 51633 | /* Take the DMS lock. */ |
| 51634 | assert( pShmNode->nRegion==0 ); |
| 51635 | rc = winLockSharedMemory(pShmNode, winFileBusyTimeout(pDbFd)); |
| 51636 | if( rc!=SQLITE_OK ) goto shmpage_out; |
| 51637 | } |
| 51638 | |
| 51639 | assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 ); |
| 51640 | if( pShmNode->nRegion<=iRegion ){ |
| 51641 | HANDLE hShared = pShmNode->hSharedShm; |
| 51642 | struct ShmRegion *apNew; /* New aRegion[] array */ |
| 51643 | int nByte = (iRegion+1)*szRegion; /* Minimum required file size */ |
| 51644 | sqlite3_int64 sz; /* Current size of wal-index file */ |
| 51645 | |
| 51646 | pShmNode->szRegion = szRegion; |
| @@ -51156,35 +51647,32 @@ | |
| 51647 | |
| 51648 | /* The requested region is not mapped into this processes address space. |
| 51649 | ** Check to see if it has been allocated (i.e. if the wal-index file is |
| 51650 | ** large enough to contain the requested region). |
| 51651 | */ |
| 51652 | rc = winHandleSize(hShared, &sz); |
| 51653 | if( rc!=SQLITE_OK ){ |
| 51654 | rc = winLogError(rc, osGetLastError(), "winShmMap1", pDbFd->zPath); |
| 51655 | goto shmpage_out; |
| 51656 | } |
| 51657 | |
| 51658 | if( sz<nByte ){ |
| 51659 | /* The requested memory region does not exist. If isWrite is set to |
| 51660 | ** zero, exit early. *pp will be set to NULL and SQLITE_OK returned. |
| 51661 | ** |
| 51662 | ** Alternatively, if isWrite is non-zero, use ftruncate() to allocate |
| 51663 | ** the requested memory region. */ |
| 51664 | if( !isWrite ) goto shmpage_out; |
| 51665 | rc = winHandleTruncate(hShared, nByte); |
| 51666 | if( rc!=SQLITE_OK ){ |
| 51667 | rc = winLogError(rc, osGetLastError(), "winShmMap2", pDbFd->zPath); |
| 51668 | goto shmpage_out; |
| 51669 | } |
| 51670 | } |
| 51671 | |
| 51672 | /* Map the requested memory region into this processes address space. */ |
| 51673 | apNew = (struct ShmRegion*)sqlite3_realloc64( |
| 51674 | pShmNode->aRegion, (iRegion+1)*sizeof(apNew[0]) |
| 51675 | ); |
| 51676 | if( !apNew ){ |
| 51677 | rc = SQLITE_IOERR_NOMEM_BKPT; |
| 51678 | goto shmpage_out; |
| @@ -51199,22 +51687,17 @@ | |
| 51687 | while( pShmNode->nRegion<=iRegion ){ |
| 51688 | HANDLE hMap = NULL; /* file-mapping handle */ |
| 51689 | void *pMap = 0; /* Mapped memory region */ |
| 51690 | |
| 51691 | #if SQLITE_OS_WINRT |
| 51692 | hMap = osCreateFileMappingFromApp(hShared, NULL, protect, nByte, NULL); |
| 51693 | #elif defined(SQLITE_WIN32_HAS_WIDE) |
| 51694 | hMap = osCreateFileMappingW(hShared, NULL, protect, 0, nByte, NULL); |
| 51695 | #elif defined(SQLITE_WIN32_HAS_ANSI) && SQLITE_WIN32_CREATEFILEMAPPINGA |
| 51696 | hMap = osCreateFileMappingA(hShared, NULL, protect, 0, nByte, NULL); |
| 51697 | #endif |
| 51698 | |
| 51699 | OSTRACE(("SHM-MAP-CREATE pid=%lu, region=%d, size=%d, rc=%s\n", |
| 51700 | osGetCurrentProcessId(), pShmNode->nRegion, nByte, |
| 51701 | hMap ? "ok" : "failed")); |
| 51702 | if( hMap ){ |
| 51703 | int iOffset = pShmNode->nRegion*szRegion; |
| @@ -51253,11 +51736,13 @@ | |
| 51736 | char *p = (char *)pShmNode->aRegion[iRegion].pMap; |
| 51737 | *pp = (void *)&p[iOffsetShift]; |
| 51738 | }else{ |
| 51739 | *pp = 0; |
| 51740 | } |
| 51741 | if( pShmNode->isReadonly && rc==SQLITE_OK ){ |
| 51742 | rc = SQLITE_READONLY; |
| 51743 | } |
| 51744 | sqlite3_mutex_leave(pShmNode->mutex); |
| 51745 | return rc; |
| 51746 | } |
| 51747 | |
| 51748 | #else |
| @@ -51594,30 +52079,10 @@ | |
| 52079 | /* caller will handle out of memory */ |
| 52080 | return zConverted; |
| 52081 | } |
| 52082 | #endif |
| 52083 | |
| 52084 | /* |
| 52085 | ** This function returns non-zero if the specified UTF-8 string buffer |
| 52086 | ** ends with a directory separator character or one was successfully |
| 52087 | ** added to it. |
| 52088 | */ |
| @@ -53067,11 +53532,11 @@ | |
| 53532 | }; |
| 53533 | #endif |
| 53534 | |
| 53535 | /* Double-check that the aSyscall[] array has been constructed |
| 53536 | ** correctly. See ticket [bb3a86e890c8e96ab] */ |
| 53537 | assert( ArraySize(aSyscall)==82 ); |
| 53538 | |
| 53539 | /* get memory map allocation granularity */ |
| 53540 | memset(&winSysInfo, 0, sizeof(SYSTEM_INFO)); |
| 53541 | #if SQLITE_OS_WINRT |
| 53542 | osGetNativeSystemInfo(&winSysInfo); |
| @@ -54125,11 +54590,11 @@ | |
| 54590 | ** Empirical testing showed that the *37 multiplier |
| 54591 | ** (an arbitrary prime)in the hash function provided |
| 54592 | ** no fewer collisions than the no-op *1. */ |
| 54593 | #define BITVEC_HASH(X) (((X)*1)%BITVEC_NINT) |
| 54594 | |
| 54595 | #define BITVEC_NPTR ((u32)(BITVEC_USIZE/sizeof(Bitvec *))) |
| 54596 | |
| 54597 | |
| 54598 | /* |
| 54599 | ** A bitmap is an instance of the following structure. |
| 54600 | ** |
| @@ -54308,11 +54773,11 @@ | |
| 54773 | if (!p) { |
| 54774 | return; |
| 54775 | } |
| 54776 | } |
| 54777 | if( p->iSize<=BITVEC_NBIT ){ |
| 54778 | p->u.aBitmap[i/BITVEC_SZELEM] &= ~(BITVEC_TELEM)(1<<(i&(BITVEC_SZELEM-1))); |
| 54779 | }else{ |
| 54780 | unsigned int j; |
| 54781 | u32 *aiValues = pBuf; |
| 54782 | memcpy(aiValues, p->u.aHash, sizeof(p->u.aHash)); |
| 54783 | memset(p->u.aHash, 0, sizeof(p->u.aHash)); |
| @@ -54359,11 +54824,11 @@ | |
| 54824 | ** up to N bits. Let I be an integer between 0 and N. 0<=I<N. |
| 54825 | ** Then the following macros can be used to set, clear, or test |
| 54826 | ** individual bits within V. |
| 54827 | */ |
| 54828 | #define SETBIT(V,I) V[I>>3] |= (1<<(I&7)) |
| 54829 | #define CLEARBIT(V,I) V[I>>3] &= ~(BITVEC_TELEM)(1<<(I&7)) |
| 54830 | #define TESTBIT(V,I) (V[I>>3]&(1<<(I&7)))!=0 |
| 54831 | |
| 54832 | /* |
| 54833 | ** This routine runs an extensive test of the Bitvec code. |
| 54834 | ** |
| @@ -55643,14 +56108,10 @@ | |
| 56108 | void *pStart, *pEnd; /* Bounds of global page cache memory */ |
| 56109 | /* Above requires no mutex. Use mutex below for variable that follow. */ |
| 56110 | sqlite3_mutex *mutex; /* Mutex for accessing the following: */ |
| 56111 | PgFreeslot *pFree; /* Free page blocks */ |
| 56112 | int nFreeSlot; /* Number of unused pcache slots */ |
| 56113 | int bUnderPressure; /* True if low on PAGECACHE memory */ |
| 56114 | } pcache1_g; |
| 56115 | |
| 56116 | /* |
| 56117 | ** All code in this file should access the global structure above via the |
| @@ -55694,11 +56155,11 @@ | |
| 56155 | pcache1.szSlot = sz; |
| 56156 | pcache1.nSlot = pcache1.nFreeSlot = n; |
| 56157 | pcache1.nReserve = n>90 ? 10 : (n/10 + 1); |
| 56158 | pcache1.pStart = pBuf; |
| 56159 | pcache1.pFree = 0; |
| 56160 | AtomicStore(&pcache1.bUnderPressure,0); |
| 56161 | while( n-- ){ |
| 56162 | p = (PgFreeslot*)pBuf; |
| 56163 | p->pNext = pcache1.pFree; |
| 56164 | pcache1.pFree = p; |
| 56165 | pBuf = (void*)&((char*)pBuf)[sz]; |
| @@ -55762,11 +56223,11 @@ | |
| 56223 | sqlite3_mutex_enter(pcache1.mutex); |
| 56224 | p = (PgHdr1 *)pcache1.pFree; |
| 56225 | if( p ){ |
| 56226 | pcache1.pFree = pcache1.pFree->pNext; |
| 56227 | pcache1.nFreeSlot--; |
| 56228 | AtomicStore(&pcache1.bUnderPressure,pcache1.nFreeSlot<pcache1.nReserve); |
| 56229 | assert( pcache1.nFreeSlot>=0 ); |
| 56230 | sqlite3StatusHighwater(SQLITE_STATUS_PAGECACHE_SIZE, nByte); |
| 56231 | sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_USED, 1); |
| 56232 | } |
| 56233 | sqlite3_mutex_leave(pcache1.mutex); |
| @@ -55801,11 +56262,11 @@ | |
| 56262 | sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_USED, 1); |
| 56263 | pSlot = (PgFreeslot*)p; |
| 56264 | pSlot->pNext = pcache1.pFree; |
| 56265 | pcache1.pFree = pSlot; |
| 56266 | pcache1.nFreeSlot++; |
| 56267 | AtomicStore(&pcache1.bUnderPressure,pcache1.nFreeSlot<pcache1.nReserve); |
| 56268 | assert( pcache1.nFreeSlot<=pcache1.nSlot ); |
| 56269 | sqlite3_mutex_leave(pcache1.mutex); |
| 56270 | }else{ |
| 56271 | assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) ); |
| 56272 | sqlite3MemdebugSetType(p, MEMTYPE_HEAP); |
| @@ -55932,11 +56393,11 @@ | |
| 56393 | ** allocating a new page cache entry in order to avoid stressing |
| 56394 | ** the heap even further. |
| 56395 | */ |
| 56396 | static int pcache1UnderMemoryPressure(PCache1 *pCache){ |
| 56397 | if( pcache1.nSlot && (pCache->szPage+pCache->szExtra)<=pcache1.szSlot ){ |
| 56398 | return AtomicLoad(&pcache1.bUnderPressure); |
| 56399 | }else{ |
| 56400 | return sqlite3HeapNearlyFull(); |
| 56401 | } |
| 56402 | } |
| 56403 | |
| @@ -59211,10 +59672,19 @@ | |
| 59672 | pPager->pInJournal = 0; |
| 59673 | releaseAllSavepoints(pPager); |
| 59674 | |
| 59675 | if( pagerUseWal(pPager) ){ |
| 59676 | assert( !isOpen(pPager->jfd) ); |
| 59677 | if( pPager->eState==PAGER_ERROR ){ |
| 59678 | /* If an IO error occurs in wal.c while attempting to wrap the wal file, |
| 59679 | ** then the Wal object may be holding a write-lock but no read-lock. |
| 59680 | ** This call ensures that the write-lock is dropped as well. We cannot |
| 59681 | ** have sqlite3WalEndReadTransaction() drop the write-lock, as it once |
| 59682 | ** did, because this would break "BEGIN EXCLUSIVE" handling for |
| 59683 | ** SQLITE_ENABLE_SETLK_TIMEOUT builds. */ |
| 59684 | sqlite3WalEndWriteTransaction(pPager->pWal); |
| 59685 | } |
| 59686 | sqlite3WalEndReadTransaction(pPager->pWal); |
| 59687 | pPager->eState = PAGER_OPEN; |
| 59688 | }else if( !pPager->exclusiveMode ){ |
| 59689 | int rc; /* Error code returned by pagerUnlockDb() */ |
| 59690 | int iDc = isOpen(pPager->fd)?sqlite3OsDeviceCharacteristics(pPager->fd):0; |
| @@ -65669,10 +66139,15 @@ | |
| 66139 | ) |
| 66140 | |
| 66141 | /* |
| 66142 | ** An open write-ahead log file is represented by an instance of the |
| 66143 | ** following object. |
| 66144 | ** |
| 66145 | ** writeLock: |
| 66146 | ** This is usually set to 1 whenever the WRITER lock is held. However, |
| 66147 | ** if it is set to 2, then the WRITER lock is held but must be released |
| 66148 | ** by walHandleException() if a SEH exception is thrown. |
| 66149 | */ |
| 66150 | struct Wal { |
| 66151 | sqlite3_vfs *pVfs; /* The VFS used to create pDbFd */ |
| 66152 | sqlite3_file *pDbFd; /* File handle for the database file */ |
| 66153 | sqlite3_file *pWalFd; /* File handle for WAL file */ |
| @@ -65759,12 +66234,16 @@ | |
| 66234 | int iNext; /* Next slot in aIndex[] not yet returned */ |
| 66235 | ht_slot *aIndex; /* i0, i1, i2... such that aPgno[iN] ascend */ |
| 66236 | u32 *aPgno; /* Array of page numbers. */ |
| 66237 | int nEntry; /* Nr. of entries in aPgno[] and aIndex[] */ |
| 66238 | int iZero; /* Frame number associated with aPgno[0] */ |
| 66239 | } aSegment[FLEXARRAY]; /* One for every 32KB page in the wal-index */ |
| 66240 | }; |
| 66241 | |
| 66242 | /* Size (in bytes) of a WalIterator object suitable for N or fewer segments */ |
| 66243 | #define SZ_WALITERATOR(N) \ |
| 66244 | (offsetof(WalIterator,aSegment)*(N)*sizeof(struct WalSegment)) |
| 66245 | |
| 66246 | /* |
| 66247 | ** Define the parameters of the hash tables in the wal-index file. There |
| 66248 | ** is a hash-table following every HASHTABLE_NPAGE page numbers in the |
| 66249 | ** wal-index. |
| @@ -67122,12 +67601,11 @@ | |
| 67601 | assert( pWal->ckptLock && pWal->hdr.mxFrame>0 ); |
| 67602 | iLast = pWal->hdr.mxFrame; |
| 67603 | |
| 67604 | /* Allocate space for the WalIterator object. */ |
| 67605 | nSegment = walFramePage(iLast) + 1; |
| 67606 | nByte = SZ_WALITERATOR(nSegment) |
| 67607 | + iLast*sizeof(ht_slot); |
| 67608 | p = (WalIterator *)sqlite3_malloc64(nByte |
| 67609 | + sizeof(ht_slot) * (iLast>HASHTABLE_NPAGE?HASHTABLE_NPAGE:iLast) |
| 67610 | ); |
| 67611 | if( !p ){ |
| @@ -67194,11 +67672,11 @@ | |
| 67672 | ** or 0 otherwise. |
| 67673 | */ |
| 67674 | static int walEnableBlocking(Wal *pWal){ |
| 67675 | int res = 0; |
| 67676 | if( pWal->db ){ |
| 67677 | int tmout = pWal->db->setlkTimeout; |
| 67678 | if( tmout ){ |
| 67679 | res = walEnableBlockingMs(pWal, tmout); |
| 67680 | } |
| 67681 | } |
| 67682 | return res; |
| @@ -67580,11 +68058,13 @@ | |
| 68058 | static int walHandleException(Wal *pWal){ |
| 68059 | if( pWal->exclusiveMode==0 ){ |
| 68060 | static const int S = 1; |
| 68061 | static const int E = (1<<SQLITE_SHM_NLOCK); |
| 68062 | int ii; |
| 68063 | u32 mUnlock; |
| 68064 | if( pWal->writeLock==2 ) pWal->writeLock = 0; |
| 68065 | mUnlock = pWal->lockMask & ~( |
| 68066 | (pWal->readLock<0 ? 0 : (S << WAL_READ_LOCK(pWal->readLock))) |
| 68067 | | (pWal->writeLock ? (E << WAL_WRITE_LOCK) : 0) |
| 68068 | | (pWal->ckptLock ? (E << WAL_CKPT_LOCK) : 0) |
| 68069 | ); |
| 68070 | for(ii=0; ii<SQLITE_SHM_NLOCK; ii++){ |
| @@ -67852,11 +68332,16 @@ | |
| 68332 | }else{ |
| 68333 | int bWriteLock = pWal->writeLock; |
| 68334 | if( bWriteLock |
| 68335 | || SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1)) |
| 68336 | ){ |
| 68337 | /* If the write-lock was just obtained, set writeLock to 2 instead of |
| 68338 | ** the usual 1. This causes walIndexPage() to behave as if the |
| 68339 | ** write-lock were held (so that it allocates new pages as required), |
| 68340 | ** and walHandleException() to unlock the write-lock if a SEH exception |
| 68341 | ** is thrown. */ |
| 68342 | if( !bWriteLock ) pWal->writeLock = 2; |
| 68343 | if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){ |
| 68344 | badHdr = walIndexTryHdr(pWal, pChanged); |
| 68345 | if( badHdr ){ |
| 68346 | /* If the wal-index header is still malformed even while holding |
| 68347 | ** a WRITE lock, it can only mean that the header is corrupted and |
| @@ -68637,12 +69122,15 @@ | |
| 69122 | /* |
| 69123 | ** Finish with a read transaction. All this does is release the |
| 69124 | ** read-lock. |
| 69125 | */ |
| 69126 | SQLITE_PRIVATE void sqlite3WalEndReadTransaction(Wal *pWal){ |
| 69127 | #ifndef SQLITE_ENABLE_SETLK_TIMEOUT |
| 69128 | assert( pWal->writeLock==0 || pWal->readLock<0 ); |
| 69129 | #endif |
| 69130 | if( pWal->readLock>=0 ){ |
| 69131 | sqlite3WalEndWriteTransaction(pWal); |
| 69132 | walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock)); |
| 69133 | pWal->readLock = -1; |
| 69134 | } |
| 69135 | } |
| 69136 | |
| @@ -68831,11 +69319,11 @@ | |
| 69319 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 69320 | /* If the write-lock is already held, then it was obtained before the |
| 69321 | ** read-transaction was even opened, making this call a no-op. |
| 69322 | ** Return early. */ |
| 69323 | if( pWal->writeLock ){ |
| 69324 | assert( !memcmp(&pWal->hdr,(void*)pWal->apWiData[0],sizeof(WalIndexHdr)) ); |
| 69325 | return SQLITE_OK; |
| 69326 | } |
| 69327 | #endif |
| 69328 | |
| 69329 | /* Cannot start a write transaction without first holding a read |
| @@ -70280,10 +70768,16 @@ | |
| 70768 | ** If a tree that appears to be taller than this is encountered, it is |
| 70769 | ** assumed that the database is corrupt. |
| 70770 | */ |
| 70771 | #define BTCURSOR_MAX_DEPTH 20 |
| 70772 | |
| 70773 | /* |
| 70774 | ** Maximum amount of storage local to a database page, regardless of |
| 70775 | ** page size. |
| 70776 | */ |
| 70777 | #define BT_MAX_LOCAL 65501 /* 65536 - 35 */ |
| 70778 | |
| 70779 | /* |
| 70780 | ** A cursor is a pointer to a particular entry within a particular |
| 70781 | ** b-tree within a database file. |
| 70782 | ** |
| 70783 | ** The entry is identified by its MemPage and the index in |
| @@ -70688,11 +71182,11 @@ | |
| 71182 | ** two or more btrees in common both try to lock all their btrees |
| 71183 | ** at the same instant. |
| 71184 | */ |
| 71185 | static void SQLITE_NOINLINE btreeEnterAll(sqlite3 *db){ |
| 71186 | int i; |
| 71187 | u8 skipOk = 1; |
| 71188 | Btree *p; |
| 71189 | assert( sqlite3_mutex_held(db->mutex) ); |
| 71190 | for(i=0; i<db->nDb; i++){ |
| 71191 | p = db->aDb[i].pBt; |
| 71192 | if( p && p->sharable ){ |
| @@ -71834,11 +72328,11 @@ | |
| 72328 | /* |
| 72329 | ** Provide flag hints to the cursor. |
| 72330 | */ |
| 72331 | SQLITE_PRIVATE void sqlite3BtreeCursorHintFlags(BtCursor *pCur, unsigned x){ |
| 72332 | assert( x==BTREE_SEEK_EQ || x==BTREE_BULKLOAD || x==0 ); |
| 72333 | pCur->hints = (u8)x; |
| 72334 | } |
| 72335 | |
| 72336 | |
| 72337 | #ifndef SQLITE_OMIT_AUTOVACUUM |
| 72338 | /* |
| @@ -72028,18 +72522,19 @@ | |
| 72522 | ** page pPage, return the number of bytes of payload stored locally. |
| 72523 | */ |
| 72524 | static int btreePayloadToLocal(MemPage *pPage, i64 nPayload){ |
| 72525 | int maxLocal; /* Maximum amount of payload held locally */ |
| 72526 | maxLocal = pPage->maxLocal; |
| 72527 | assert( nPayload>=0 ); |
| 72528 | if( nPayload<=maxLocal ){ |
| 72529 | return (int)nPayload; |
| 72530 | }else{ |
| 72531 | int minLocal; /* Minimum amount of payload held locally */ |
| 72532 | int surplus; /* Overflow payload available for local storage */ |
| 72533 | minLocal = pPage->minLocal; |
| 72534 | surplus = (int)(minLocal +(nPayload - minLocal)%(pPage->pBt->usableSize-4)); |
| 72535 | return (surplus <= maxLocal) ? surplus : minLocal; |
| 72536 | } |
| 72537 | } |
| 72538 | |
| 72539 | /* |
| 72540 | ** The following routines are implementations of the MemPage.xParseCell() |
| @@ -72145,15 +72640,17 @@ | |
| 72640 | pInfo->nKey = *(i64*)&iKey; |
| 72641 | pInfo->nPayload = nPayload; |
| 72642 | pInfo->pPayload = pIter; |
| 72643 | testcase( nPayload==pPage->maxLocal ); |
| 72644 | testcase( nPayload==(u32)pPage->maxLocal+1 ); |
| 72645 | assert( nPayload>=0 ); |
| 72646 | assert( pPage->maxLocal <= BT_MAX_LOCAL ); |
| 72647 | if( nPayload<=pPage->maxLocal ){ |
| 72648 | /* This is the (easy) common case where the entire payload fits |
| 72649 | ** on the local page. No overflow is required. |
| 72650 | */ |
| 72651 | pInfo->nSize = (u16)nPayload + (u16)(pIter - pCell); |
| 72652 | if( pInfo->nSize<4 ) pInfo->nSize = 4; |
| 72653 | pInfo->nLocal = (u16)nPayload; |
| 72654 | }else{ |
| 72655 | btreeParseCellAdjustSizeForOverflow(pPage, pCell, pInfo); |
| 72656 | } |
| @@ -72182,15 +72679,17 @@ | |
| 72679 | pInfo->nKey = nPayload; |
| 72680 | pInfo->nPayload = nPayload; |
| 72681 | pInfo->pPayload = pIter; |
| 72682 | testcase( nPayload==pPage->maxLocal ); |
| 72683 | testcase( nPayload==(u32)pPage->maxLocal+1 ); |
| 72684 | assert( nPayload>=0 ); |
| 72685 | assert( pPage->maxLocal <= BT_MAX_LOCAL ); |
| 72686 | if( nPayload<=pPage->maxLocal ){ |
| 72687 | /* This is the (easy) common case where the entire payload fits |
| 72688 | ** on the local page. No overflow is required. |
| 72689 | */ |
| 72690 | pInfo->nSize = (u16)nPayload + (u16)(pIter - pCell); |
| 72691 | if( pInfo->nSize<4 ) pInfo->nSize = 4; |
| 72692 | pInfo->nLocal = (u16)nPayload; |
| 72693 | }else{ |
| 72694 | btreeParseCellAdjustSizeForOverflow(pPage, pCell, pInfo); |
| 72695 | } |
| @@ -72725,18 +73224,18 @@ | |
| 73224 | ** that routine will not detect overlap between cells or freeblocks. Nor |
| 73225 | ** does it detect cells or freeblocks that encroach into the reserved bytes |
| 73226 | ** at the end of the page. So do additional corruption checks inside this |
| 73227 | ** routine and return SQLITE_CORRUPT if any problems are found. |
| 73228 | */ |
| 73229 | static int freeSpace(MemPage *pPage, int iStart, int iSize){ |
| 73230 | int iPtr; /* Address of ptr to next freeblock */ |
| 73231 | int iFreeBlk; /* Address of the next freeblock */ |
| 73232 | u8 hdr; /* Page header size. 0 or 100 */ |
| 73233 | int nFrag = 0; /* Reduction in fragmentation */ |
| 73234 | int iOrigSize = iSize; /* Original value of iSize */ |
| 73235 | int x; /* Offset to cell content area */ |
| 73236 | int iEnd = iStart + iSize; /* First byte past the iStart buffer */ |
| 73237 | unsigned char *data = pPage->aData; /* Page content */ |
| 73238 | u8 *pTmp; /* Temporary ptr into data[] */ |
| 73239 | |
| 73240 | assert( pPage->pBt!=0 ); |
| 73241 | assert( sqlite3PagerIswriteable(pPage->pDbPage) ); |
| @@ -72759,11 +73258,11 @@ | |
| 73258 | if( iFreeBlk==0 ) break; /* TH3: corrupt082.100 */ |
| 73259 | return SQLITE_CORRUPT_PAGE(pPage); |
| 73260 | } |
| 73261 | iPtr = iFreeBlk; |
| 73262 | } |
| 73263 | if( iFreeBlk>(int)pPage->pBt->usableSize-4 ){ /* TH3: corrupt081.100 */ |
| 73264 | return SQLITE_CORRUPT_PAGE(pPage); |
| 73265 | } |
| 73266 | assert( iFreeBlk>iPtr || iFreeBlk==0 || CORRUPT_DB ); |
| 73267 | |
| 73268 | /* At this point: |
| @@ -72774,11 +73273,11 @@ | |
| 73273 | */ |
| 73274 | if( iFreeBlk && iEnd+3>=iFreeBlk ){ |
| 73275 | nFrag = iFreeBlk - iEnd; |
| 73276 | if( iEnd>iFreeBlk ) return SQLITE_CORRUPT_PAGE(pPage); |
| 73277 | iEnd = iFreeBlk + get2byte(&data[iFreeBlk+2]); |
| 73278 | if( iEnd > (int)pPage->pBt->usableSize ){ |
| 73279 | return SQLITE_CORRUPT_PAGE(pPage); |
| 73280 | } |
| 73281 | iSize = iEnd - iStart; |
| 73282 | iFreeBlk = get2byte(&data[iFreeBlk]); |
| 73283 | } |
| @@ -72795,11 +73294,11 @@ | |
| 73294 | iSize = iEnd - iPtr; |
| 73295 | iStart = iPtr; |
| 73296 | } |
| 73297 | } |
| 73298 | if( nFrag>data[hdr+7] ) return SQLITE_CORRUPT_PAGE(pPage); |
| 73299 | data[hdr+7] -= (u8)nFrag; |
| 73300 | } |
| 73301 | pTmp = &data[hdr+5]; |
| 73302 | x = get2byte(pTmp); |
| 73303 | if( pPage->pBt->btsFlags & BTS_FAST_SECURE ){ |
| 73304 | /* Overwrite deleted information with zeros when the secure_delete |
| @@ -72816,11 +73315,12 @@ | |
| 73315 | put2byte(&data[hdr+5], iEnd); |
| 73316 | }else{ |
| 73317 | /* Insert the new freeblock into the freelist */ |
| 73318 | put2byte(&data[iPtr], iStart); |
| 73319 | put2byte(&data[iStart], iFreeBlk); |
| 73320 | assert( iSize>=0 && iSize<=0xffff ); |
| 73321 | put2byte(&data[iStart+2], (u16)iSize); |
| 73322 | } |
| 73323 | pPage->nFree += iOrigSize; |
| 73324 | return SQLITE_OK; |
| 73325 | } |
| 73326 | |
| @@ -73042,11 +73542,11 @@ | |
| 73542 | return SQLITE_CORRUPT_PAGE(pPage); |
| 73543 | } |
| 73544 | assert( pBt->pageSize>=512 && pBt->pageSize<=65536 ); |
| 73545 | pPage->maskPage = (u16)(pBt->pageSize - 1); |
| 73546 | pPage->nOverflow = 0; |
| 73547 | pPage->cellOffset = (u16)(pPage->hdrOffset + 8 + pPage->childPtrSize); |
| 73548 | pPage->aCellIdx = data + pPage->childPtrSize + 8; |
| 73549 | pPage->aDataEnd = pPage->aData + pBt->pageSize; |
| 73550 | pPage->aDataOfst = pPage->aData + pPage->childPtrSize; |
| 73551 | /* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the |
| 73552 | ** number of cells on the page. */ |
| @@ -73076,12 +73576,12 @@ | |
| 73576 | ** no entries. |
| 73577 | */ |
| 73578 | static void zeroPage(MemPage *pPage, int flags){ |
| 73579 | unsigned char *data = pPage->aData; |
| 73580 | BtShared *pBt = pPage->pBt; |
| 73581 | int hdr = pPage->hdrOffset; |
| 73582 | int first; |
| 73583 | |
| 73584 | assert( sqlite3PagerPagenumber(pPage->pDbPage)==pPage->pgno || CORRUPT_DB ); |
| 73585 | assert( sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage ); |
| 73586 | assert( sqlite3PagerGetData(pPage->pDbPage) == data ); |
| 73587 | assert( sqlite3PagerIswriteable(pPage->pDbPage) ); |
| @@ -73094,11 +73594,11 @@ | |
| 73594 | memset(&data[hdr+1], 0, 4); |
| 73595 | data[hdr+7] = 0; |
| 73596 | put2byte(&data[hdr+5], pBt->usableSize); |
| 73597 | pPage->nFree = (u16)(pBt->usableSize - first); |
| 73598 | decodeFlags(pPage, flags); |
| 73599 | pPage->cellOffset = (u16)first; |
| 73600 | pPage->aDataEnd = &data[pBt->pageSize]; |
| 73601 | pPage->aCellIdx = &data[first]; |
| 73602 | pPage->aDataOfst = &data[pPage->childPtrSize]; |
| 73603 | pPage->nOverflow = 0; |
| 73604 | assert( pBt->pageSize>=512 && pBt->pageSize<=65536 ); |
| @@ -73880,11 +74380,11 @@ | |
| 74380 | int rc = SQLITE_OK; |
| 74381 | int x; |
| 74382 | BtShared *pBt = p->pBt; |
| 74383 | assert( nReserve>=0 && nReserve<=255 ); |
| 74384 | sqlite3BtreeEnter(p); |
| 74385 | pBt->nReserveWanted = (u8)nReserve; |
| 74386 | x = pBt->pageSize - pBt->usableSize; |
| 74387 | if( nReserve<x ) nReserve = x; |
| 74388 | if( pBt->btsFlags & BTS_PAGESIZE_FIXED ){ |
| 74389 | sqlite3BtreeLeave(p); |
| 74390 | return SQLITE_READONLY; |
| @@ -73986,11 +74486,11 @@ | |
| 74486 | sqlite3BtreeEnter(p); |
| 74487 | assert( BTS_OVERWRITE==BTS_SECURE_DELETE*2 ); |
| 74488 | assert( BTS_FAST_SECURE==(BTS_OVERWRITE|BTS_SECURE_DELETE) ); |
| 74489 | if( newFlag>=0 ){ |
| 74490 | p->pBt->btsFlags &= ~BTS_FAST_SECURE; |
| 74491 | p->pBt->btsFlags |= (u16)(BTS_SECURE_DELETE*newFlag); |
| 74492 | } |
| 74493 | b = (p->pBt->btsFlags & BTS_FAST_SECURE)/BTS_SECURE_DELETE; |
| 74494 | sqlite3BtreeLeave(p); |
| 74495 | return b; |
| 74496 | } |
| @@ -78434,11 +78934,12 @@ | |
| 78934 | pSrcEnd = pCArray->apEnd[k]; |
| 78935 | } |
| 78936 | } |
| 78937 | |
| 78938 | /* The pPg->nFree field is now set incorrectly. The caller will fix it. */ |
| 78939 | assert( nCell < 10922 ); |
| 78940 | pPg->nCell = (u16)nCell; |
| 78941 | pPg->nOverflow = 0; |
| 78942 | |
| 78943 | put2byte(&aData[hdr+1], 0); |
| 78944 | put2byte(&aData[hdr+3], pPg->nCell); |
| 78945 | put2byte(&aData[hdr+5], pData - aData); |
| @@ -78681,13 +79182,17 @@ | |
| 79182 | assert( nCell>=0 ); |
| 79183 | pCellptr = &pPg->aCellIdx[nCell*2]; |
| 79184 | if( pageInsertArray( |
| 79185 | pPg, pBegin, &pData, pCellptr, |
| 79186 | iNew+nCell, nNew-nCell, pCArray |
| 79187 | ) |
| 79188 | ){ |
| 79189 | goto editpage_fail; |
| 79190 | } |
| 79191 | |
| 79192 | assert( nNew < 10922 ); |
| 79193 | pPg->nCell = (u16)nNew; |
| 79194 | pPg->nOverflow = 0; |
| 79195 | |
| 79196 | put2byte(&aData[hdr+3], pPg->nCell); |
| 79197 | put2byte(&aData[hdr+5], pData - aData); |
| 79198 | |
| @@ -78992,11 +79497,11 @@ | |
| 79497 | int leafData; /* True if pPage is a leaf of a LEAFDATA tree */ |
| 79498 | int usableSpace; /* Bytes in pPage beyond the header */ |
| 79499 | int pageFlags; /* Value of pPage->aData[0] */ |
| 79500 | int iSpace1 = 0; /* First unused byte of aSpace1[] */ |
| 79501 | int iOvflSpace = 0; /* First unused byte of aOvflSpace[] */ |
| 79502 | u64 szScratch; /* Size of scratch memory requested */ |
| 79503 | MemPage *apOld[NB]; /* pPage and up to two siblings */ |
| 79504 | MemPage *apNew[NB+2]; /* pPage and up to NB siblings after balancing */ |
| 79505 | u8 *pRight; /* Location in parent of right-sibling pointer */ |
| 79506 | u8 *apDiv[NB-1]; /* Divider cells in pParent */ |
| 79507 | int cntNew[NB+2]; /* Index in b.paCell[] of cell after i-th page */ |
| @@ -80277,11 +80782,11 @@ | |
| 80782 | if( loc==0 ){ |
| 80783 | getCellInfo(pCur); |
| 80784 | if( pCur->info.nKey==pX->nKey ){ |
| 80785 | BtreePayload x2; |
| 80786 | x2.pData = pX->pKey; |
| 80787 | x2.nData = (int)pX->nKey; assert( pX->nKey<=0x7fffffff ); |
| 80788 | x2.nZero = 0; |
| 80789 | return btreeOverwriteCell(pCur, &x2); |
| 80790 | } |
| 80791 | } |
| 80792 | } |
| @@ -80458,11 +80963,11 @@ | |
| 80963 | u32 nIn; /* Size of input buffer aIn[] */ |
| 80964 | u32 nRem; /* Bytes of data still to copy */ |
| 80965 | |
| 80966 | getCellInfo(pSrc); |
| 80967 | if( pSrc->info.nPayload<0x80 ){ |
| 80968 | *(aOut++) = (u8)pSrc->info.nPayload; |
| 80969 | }else{ |
| 80970 | aOut += sqlite3PutVarint(aOut, pSrc->info.nPayload); |
| 80971 | } |
| 80972 | if( pDest->pKeyInfo==0 ) aOut += putVarint(aOut, iKey); |
| 80973 | nIn = pSrc->info.nLocal; |
| @@ -80471,11 +80976,11 @@ | |
| 80976 | return SQLITE_CORRUPT_PAGE(pSrc->pPage); |
| 80977 | } |
| 80978 | nRem = pSrc->info.nPayload; |
| 80979 | if( nIn==nRem && nIn<pDest->pPage->maxLocal ){ |
| 80980 | memcpy(aOut, aIn, nIn); |
| 80981 | pBt->nPreformatSize = nIn + (int)(aOut - pBt->pTmpSpace); |
| 80982 | return SQLITE_OK; |
| 80983 | }else{ |
| 80984 | int rc = SQLITE_OK; |
| 80985 | Pager *pSrcPager = pSrc->pBt->pPager; |
| 80986 | u8 *pPgnoOut = 0; |
| @@ -80483,11 +80988,11 @@ | |
| 80988 | DbPage *pPageIn = 0; |
| 80989 | MemPage *pPageOut = 0; |
| 80990 | u32 nOut; /* Size of output buffer aOut[] */ |
| 80991 | |
| 80992 | nOut = btreePayloadToLocal(pDest->pPage, pSrc->info.nPayload); |
| 80993 | pBt->nPreformatSize = (int)nOut + (int)(aOut - pBt->pTmpSpace); |
| 80994 | if( nOut<pSrc->info.nPayload ){ |
| 80995 | pPgnoOut = &aOut[nOut]; |
| 80996 | pBt->nPreformatSize += 4; |
| 80997 | } |
| 80998 | |
| @@ -85584,16 +86089,14 @@ | |
| 86089 | int nArg, /* Number of argument */ |
| 86090 | const FuncDef *pFunc, /* The function to be invoked */ |
| 86091 | int eCallCtx /* Calling context */ |
| 86092 | ){ |
| 86093 | Vdbe *v = pParse->pVdbe; |
| 86094 | int addr; |
| 86095 | sqlite3_context *pCtx; |
| 86096 | assert( v ); |
| 86097 | pCtx = sqlite3DbMallocRawNN(pParse->db, SZ_CONTEXT(nArg)); |
| 86098 | if( pCtx==0 ){ |
| 86099 | assert( pParse->db->mallocFailed ); |
| 86100 | freeEphemeralFunction(pParse->db, (FuncDef*)pFunc); |
| 86101 | return 0; |
| 86102 | } |
| @@ -90665,25 +91168,26 @@ | |
| 91168 | |
| 91169 | preupdate.v = v; |
| 91170 | preupdate.pCsr = pCsr; |
| 91171 | preupdate.op = op; |
| 91172 | preupdate.iNewReg = iReg; |
| 91173 | preupdate.pKeyinfo = (KeyInfo*)&preupdate.keyinfoSpace; |
| 91174 | preupdate.pKeyinfo->db = db; |
| 91175 | preupdate.pKeyinfo->enc = ENC(db); |
| 91176 | preupdate.pKeyinfo->nKeyField = pTab->nCol; |
| 91177 | preupdate.pKeyinfo->aSortFlags = (u8*)&fakeSortOrder; |
| 91178 | preupdate.iKey1 = iKey1; |
| 91179 | preupdate.iKey2 = iKey2; |
| 91180 | preupdate.pTab = pTab; |
| 91181 | preupdate.iBlobWrite = iBlobWrite; |
| 91182 | |
| 91183 | db->pPreUpdate = &preupdate; |
| 91184 | db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2); |
| 91185 | db->pPreUpdate = 0; |
| 91186 | sqlite3DbFree(db, preupdate.aRecord); |
| 91187 | vdbeFreeUnpacked(db, preupdate.pKeyinfo->nKeyField+1,preupdate.pUnpacked); |
| 91188 | vdbeFreeUnpacked(db, preupdate.pKeyinfo->nKeyField+1,preupdate.pNewUnpacked); |
| 91189 | sqlite3VdbeMemRelease(&preupdate.oldipk); |
| 91190 | if( preupdate.aNew ){ |
| 91191 | int i; |
| 91192 | for(i=0; i<pCsr->nField; i++){ |
| 91193 | sqlite3VdbeMemRelease(&preupdate.aNew[i]); |
| @@ -92918,11 +93422,11 @@ | |
| 93422 | nRec = sqlite3BtreePayloadSize(p->pCsr->uc.pCursor); |
| 93423 | aRec = sqlite3DbMallocRaw(db, nRec); |
| 93424 | if( !aRec ) goto preupdate_old_out; |
| 93425 | rc = sqlite3BtreePayload(p->pCsr->uc.pCursor, 0, nRec, aRec); |
| 93426 | if( rc==SQLITE_OK ){ |
| 93427 | p->pUnpacked = vdbeUnpackRecord(p->pKeyinfo, nRec, aRec); |
| 93428 | if( !p->pUnpacked ) rc = SQLITE_NOMEM; |
| 93429 | } |
| 93430 | if( rc!=SQLITE_OK ){ |
| 93431 | sqlite3DbFree(db, aRec); |
| 93432 | goto preupdate_old_out; |
| @@ -92983,11 +93487,11 @@ | |
| 93487 | #ifdef SQLITE_ENABLE_API_ARMOR |
| 93488 | p = db!=0 ? db->pPreUpdate : 0; |
| 93489 | #else |
| 93490 | p = db->pPreUpdate; |
| 93491 | #endif |
| 93492 | return (p ? p->pKeyinfo->nKeyField : 0); |
| 93493 | } |
| 93494 | #endif /* SQLITE_ENABLE_PREUPDATE_HOOK */ |
| 93495 | |
| 93496 | #ifdef SQLITE_ENABLE_PREUPDATE_HOOK |
| 93497 | /* |
| @@ -93066,11 +93570,11 @@ | |
| 93570 | UnpackedRecord *pUnpack = p->pNewUnpacked; |
| 93571 | if( !pUnpack ){ |
| 93572 | Mem *pData = &p->v->aMem[p->iNewReg]; |
| 93573 | rc = ExpandBlob(pData); |
| 93574 | if( rc!=SQLITE_OK ) goto preupdate_new_out; |
| 93575 | pUnpack = vdbeUnpackRecord(p->pKeyinfo, pData->n, pData->z); |
| 93576 | if( !pUnpack ){ |
| 93577 | rc = SQLITE_NOMEM; |
| 93578 | goto preupdate_new_out; |
| 93579 | } |
| 93580 | p->pNewUnpacked = pUnpack; |
| @@ -93860,13 +94364,13 @@ | |
| 94364 | */ |
| 94365 | Mem *pMem = iCur>0 ? &p->aMem[p->nMem-iCur] : p->aMem; |
| 94366 | |
| 94367 | i64 nByte; |
| 94368 | VdbeCursor *pCx = 0; |
| 94369 | nByte = SZ_VDBECURSOR(nField); |
| 94370 | assert( ROUND8(nByte)==nByte ); |
| 94371 | if( eCurType==CURTYPE_BTREE ) nByte += sqlite3BtreeCursorSize(); |
| 94372 | |
| 94373 | assert( iCur>=0 && iCur<p->nCursor ); |
| 94374 | if( p->apCsr[iCur] ){ /*OPTIMIZATION-IF-FALSE*/ |
| 94375 | sqlite3VdbeFreeCursorNN(p, p->apCsr[iCur]); |
| 94376 | p->apCsr[iCur] = 0; |
| @@ -93895,12 +94399,12 @@ | |
| 94399 | memset(pCx, 0, offsetof(VdbeCursor,pAltCursor)); |
| 94400 | pCx->eCurType = eCurType; |
| 94401 | pCx->nField = nField; |
| 94402 | pCx->aOffset = &pCx->aType[nField]; |
| 94403 | if( eCurType==CURTYPE_BTREE ){ |
| 94404 | assert( ROUND8(SZ_VDBECURSOR(nField))==SZ_VDBECURSOR(nField) ); |
| 94405 | pCx->uc.pCursor = (BtCursor*)&pMem->z[SZ_VDBECURSOR(nField)]; |
| 94406 | sqlite3BtreeCursorZero(pCx->uc.pCursor); |
| 94407 | } |
| 94408 | return pCx; |
| 94409 | } |
| 94410 | |
| @@ -99638,11 +100142,11 @@ | |
| 100142 | pCrsr = pC->uc.pCursor; |
| 100143 | |
| 100144 | /* The OP_RowData opcodes always follow OP_NotExists or |
| 100145 | ** OP_SeekRowid or OP_Rewind/Op_Next with no intervening instructions |
| 100146 | ** that might invalidate the cursor. |
| 100147 | ** If this were not the case, one of the following assert()s |
| 100148 | ** would fail. Should this ever change (because of changes in the code |
| 100149 | ** generator) then the fix would be to insert a call to |
| 100150 | ** sqlite3VdbeCursorMoveto(). |
| 100151 | */ |
| 100152 | assert( pC->deferredMoveto==0 ); |
| @@ -101287,11 +101791,11 @@ | |
| 101791 | ** cell in which to store the accumulation. Be careful that the memory |
| 101792 | ** cell is 8-byte aligned, even on platforms where a pointer is 32-bits. |
| 101793 | ** |
| 101794 | ** Note: We could avoid this by using a regular memory cell from aMem[] for |
| 101795 | ** the accumulator, instead of allocating one here. */ |
| 101796 | nAlloc = ROUND8P( SZ_CONTEXT(n) ); |
| 101797 | pCtx = sqlite3DbMallocRawNN(db, nAlloc + sizeof(Mem)); |
| 101798 | if( pCtx==0 ) goto no_mem; |
| 101799 | pCtx->pOut = (Mem*)((u8*)pCtx + nAlloc); |
| 101800 | assert( EIGHT_BYTE_ALIGNMENT(pCtx->pOut) ); |
| 101801 | |
| @@ -102945,10 +103449,11 @@ | |
| 103449 | int iCol; /* Index of zColumn in row-record */ |
| 103450 | int rc = SQLITE_OK; |
| 103451 | char *zErr = 0; |
| 103452 | Table *pTab; |
| 103453 | Incrblob *pBlob = 0; |
| 103454 | int iDb; |
| 103455 | Parse sParse; |
| 103456 | |
| 103457 | #ifdef SQLITE_ENABLE_API_ARMOR |
| 103458 | if( ppBlob==0 ){ |
| 103459 | return SQLITE_MISUSE_BKPT; |
| @@ -102990,11 +103495,14 @@ | |
| 103495 | if( pTab && IsView(pTab) ){ |
| 103496 | pTab = 0; |
| 103497 | sqlite3ErrorMsg(&sParse, "cannot open view: %s", zTable); |
| 103498 | } |
| 103499 | #endif |
| 103500 | if( pTab==0 |
| 103501 | || ((iDb = sqlite3SchemaToIndex(db, pTab->pSchema))==1 && |
| 103502 | sqlite3OpenTempDatabase(&sParse)) |
| 103503 | ){ |
| 103504 | if( sParse.zErrMsg ){ |
| 103505 | sqlite3DbFree(db, zErr); |
| 103506 | zErr = sParse.zErrMsg; |
| 103507 | sParse.zErrMsg = 0; |
| 103508 | } |
| @@ -103001,11 +103509,11 @@ | |
| 103509 | rc = SQLITE_ERROR; |
| 103510 | sqlite3BtreeLeaveAll(db); |
| 103511 | goto blob_open_out; |
| 103512 | } |
| 103513 | pBlob->pTab = pTab; |
| 103514 | pBlob->zDb = db->aDb[iDb].zDbSName; |
| 103515 | |
| 103516 | /* Now search pTab for the exact column. */ |
| 103517 | iCol = sqlite3ColumnIndex(pTab, zColumn); |
| 103518 | if( iCol<0 ){ |
| 103519 | sqlite3DbFree(db, zErr); |
| @@ -103085,11 +103593,10 @@ | |
| 103593 | {OP_Column, 0, 0, 1}, /* 3 */ |
| 103594 | {OP_ResultRow, 1, 0, 0}, /* 4 */ |
| 103595 | {OP_Halt, 0, 0, 0}, /* 5 */ |
| 103596 | }; |
| 103597 | Vdbe *v = (Vdbe *)pBlob->pStmt; |
| 103598 | VdbeOp *aOp; |
| 103599 | |
| 103600 | sqlite3VdbeAddOp4Int(v, OP_Transaction, iDb, wrFlag, |
| 103601 | pTab->pSchema->schema_cookie, |
| 103602 | pTab->pSchema->iGeneration); |
| @@ -103663,12 +104170,15 @@ | |
| 104170 | u8 bUsePMA; /* True if one or more PMAs created */ |
| 104171 | u8 bUseThreads; /* True to use background threads */ |
| 104172 | u8 iPrev; /* Previous thread used to flush PMA */ |
| 104173 | u8 nTask; /* Size of aTask[] array */ |
| 104174 | u8 typeMask; |
| 104175 | SortSubtask aTask[FLEXARRAY]; /* One or more subtasks */ |
| 104176 | }; |
| 104177 | |
| 104178 | /* Size (in bytes) of a VdbeSorter object that works with N or fewer subtasks */ |
| 104179 | #define SZ_VDBESORTER(N) (offsetof(VdbeSorter,aTask)+(N)*sizeof(SortSubtask)) |
| 104180 | |
| 104181 | #define SORTER_TYPE_INTEGER 0x01 |
| 104182 | #define SORTER_TYPE_TEXT 0x02 |
| 104183 | |
| 104184 | /* |
| @@ -104297,12 +104807,12 @@ | |
| 104807 | assert( pCsr->pKeyInfo ); |
| 104808 | assert( !pCsr->isEphemeral ); |
| 104809 | assert( pCsr->eCurType==CURTYPE_SORTER ); |
| 104810 | assert( sizeof(KeyInfo) + UMXV(pCsr->pKeyInfo->nKeyField)*sizeof(CollSeq*) |
| 104811 | < 0x7fffffff ); |
| 104812 | szKeyInfo = SZ_KEYINFO(pCsr->pKeyInfo->nKeyField+1); |
| 104813 | sz = SZ_VDBESORTER(nWorker+1); |
| 104814 | |
| 104815 | pSorter = (VdbeSorter*)sqlite3DbMallocZero(db, sz + szKeyInfo); |
| 104816 | pCsr->uc.pSorter = pSorter; |
| 104817 | if( pSorter==0 ){ |
| 104818 | rc = SQLITE_NOMEM_BKPT; |
| @@ -104762,10 +105272,14 @@ | |
| 105272 | } |
| 105273 | |
| 105274 | p->u.pNext = 0; |
| 105275 | for(i=0; aSlot[i]; i++){ |
| 105276 | p = vdbeSorterMerge(pTask, p, aSlot[i]); |
| 105277 | /* ,--Each aSlot[] holds twice as much as the previous. So we cannot use |
| 105278 | ** | up all 64 aSlots[] with only a 64-bit address space. |
| 105279 | ** v */ |
| 105280 | assert( i<ArraySize(aSlot) ); |
| 105281 | aSlot[i] = 0; |
| 105282 | } |
| 105283 | aSlot[i] = p; |
| 105284 | p = pNext; |
| 105285 | } |
| @@ -109536,32 +110050,34 @@ | |
| 110050 | Table *pTab, /* The table being referenced, or NULL */ |
| 110051 | int type, /* NC_IsCheck, NC_PartIdx, NC_IdxExpr, NC_GenCol, or 0 */ |
| 110052 | Expr *pExpr, /* Expression to resolve. May be NULL. */ |
| 110053 | ExprList *pList /* Expression list to resolve. May be NULL. */ |
| 110054 | ){ |
| 110055 | SrcList *pSrc; /* Fake SrcList for pParse->pNewTable */ |
| 110056 | NameContext sNC; /* Name context for pParse->pNewTable */ |
| 110057 | int rc; |
| 110058 | u8 srcSpace[SZ_SRCLIST_1]; /* Memory space for the fake SrcList */ |
| 110059 | |
| 110060 | assert( type==0 || pTab!=0 ); |
| 110061 | assert( type==NC_IsCheck || type==NC_PartIdx || type==NC_IdxExpr |
| 110062 | || type==NC_GenCol || pTab==0 ); |
| 110063 | memset(&sNC, 0, sizeof(sNC)); |
| 110064 | pSrc = (SrcList*)srcSpace; |
| 110065 | memset(pSrc, 0, SZ_SRCLIST_1); |
| 110066 | if( pTab ){ |
| 110067 | pSrc->nSrc = 1; |
| 110068 | pSrc->a[0].zName = pTab->zName; |
| 110069 | pSrc->a[0].pSTab = pTab; |
| 110070 | pSrc->a[0].iCursor = -1; |
| 110071 | if( pTab->pSchema!=pParse->db->aDb[1].pSchema ){ |
| 110072 | /* Cause EP_FromDDL to be set on TK_FUNCTION nodes of non-TEMP |
| 110073 | ** schema elements */ |
| 110074 | type |= NC_FromDDL; |
| 110075 | } |
| 110076 | } |
| 110077 | sNC.pParse = pParse; |
| 110078 | sNC.pSrcList = pSrc; |
| 110079 | sNC.ncFlags = type | NC_IsDDL; |
| 110080 | if( (rc = sqlite3ResolveExprNames(&sNC, pExpr))!=SQLITE_OK ) return rc; |
| 110081 | if( pList ) rc = sqlite3ResolveExprListNames(&sNC, pList); |
| 110082 | return rc; |
| 110083 | } |
| @@ -111306,11 +111822,11 @@ | |
| 111822 | */ |
| 111823 | #ifndef SQLITE_OMIT_CTE |
| 111824 | SQLITE_PRIVATE With *sqlite3WithDup(sqlite3 *db, With *p){ |
| 111825 | With *pRet = 0; |
| 111826 | if( p ){ |
| 111827 | sqlite3_int64 nByte = SZ_WITH(p->nCte); |
| 111828 | pRet = sqlite3DbMallocZero(db, nByte); |
| 111829 | if( pRet ){ |
| 111830 | int i; |
| 111831 | pRet->nCte = p->nCte; |
| 111832 | for(i=0; i<p->nCte; i++){ |
| @@ -111433,15 +111949,13 @@ | |
| 111949 | #if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) \ |
| 111950 | || !defined(SQLITE_OMIT_SUBQUERY) |
| 111951 | SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3 *db, const SrcList *p, int flags){ |
| 111952 | SrcList *pNew; |
| 111953 | int i; |
| 111954 | assert( db!=0 ); |
| 111955 | if( p==0 ) return 0; |
| 111956 | pNew = sqlite3DbMallocRawNN(db, SZ_SRCLIST(p->nSrc) ); |
| 111957 | if( pNew==0 ) return 0; |
| 111958 | pNew->nSrc = pNew->nAlloc = p->nSrc; |
| 111959 | for(i=0; i<p->nSrc; i++){ |
| 111960 | SrcItem *pNewItem = &pNew->a[i]; |
| 111961 | const SrcItem *pOldItem = &p->a[i]; |
| @@ -111499,11 +112013,11 @@ | |
| 112013 | SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3 *db, const IdList *p){ |
| 112014 | IdList *pNew; |
| 112015 | int i; |
| 112016 | assert( db!=0 ); |
| 112017 | if( p==0 ) return 0; |
| 112018 | pNew = sqlite3DbMallocRawNN(db, SZ_IDLIST(p->nId)); |
| 112019 | if( pNew==0 ) return 0; |
| 112020 | pNew->nId = p->nId; |
| 112021 | for(i=0; i<p->nId; i++){ |
| 112022 | struct IdList_item *pNewItem = &pNew->a[i]; |
| 112023 | const struct IdList_item *pOldItem = &p->a[i]; |
| @@ -111531,11 +112045,11 @@ | |
| 112045 | pNew->pNext = pNext; |
| 112046 | pNew->pPrior = 0; |
| 112047 | pNew->pLimit = sqlite3ExprDup(db, p->pLimit, flags); |
| 112048 | pNew->iLimit = 0; |
| 112049 | pNew->iOffset = 0; |
| 112050 | pNew->selFlags = p->selFlags & ~(u32)SF_UsesEphemeral; |
| 112051 | pNew->addrOpenEphm[0] = -1; |
| 112052 | pNew->addrOpenEphm[1] = -1; |
| 112053 | pNew->nSelectRow = p->nSelectRow; |
| 112054 | pNew->pWith = sqlite3WithDup(db, p->pWith); |
| 112055 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| @@ -111583,11 +112097,11 @@ | |
| 112097 | Expr *pExpr /* Expression to be appended. Might be NULL */ |
| 112098 | ){ |
| 112099 | struct ExprList_item *pItem; |
| 112100 | ExprList *pList; |
| 112101 | |
| 112102 | pList = sqlite3DbMallocRawNN(db, SZ_EXPRLIST(4)); |
| 112103 | if( pList==0 ){ |
| 112104 | sqlite3ExprDelete(db, pExpr); |
| 112105 | return 0; |
| 112106 | } |
| 112107 | pList->nAlloc = 4; |
| @@ -111603,12 +112117,11 @@ | |
| 112117 | Expr *pExpr /* Expression to be appended. Might be NULL */ |
| 112118 | ){ |
| 112119 | struct ExprList_item *pItem; |
| 112120 | ExprList *pNew; |
| 112121 | pList->nAlloc *= 2; |
| 112122 | pNew = sqlite3DbRealloc(db, pList, SZ_EXPRLIST(pList->nAlloc)); |
| 112123 | if( pNew==0 ){ |
| 112124 | sqlite3ExprListDelete(db, pList); |
| 112125 | sqlite3ExprDelete(db, pExpr); |
| 112126 | return 0; |
| 112127 | }else{ |
| @@ -114240,11 +114753,11 @@ | |
| 114753 | return -1; /* Not found */ |
| 114754 | } |
| 114755 | |
| 114756 | |
| 114757 | /* |
| 114758 | ** Expression pExpr is guaranteed to be a TK_COLUMN or equivalent. This |
| 114759 | ** function checks the Parse.pIdxPartExpr list to see if this column |
| 114760 | ** can be replaced with a constant value. If so, it generates code to |
| 114761 | ** put the constant value in a register (ideally, but not necessarily, |
| 114762 | ** register iTarget) and returns the register number. |
| 114763 | ** |
| @@ -117481,17 +117994,17 @@ | |
| 117994 | pNew->nTabRef = 1; |
| 117995 | pNew->nCol = pTab->nCol; |
| 117996 | assert( pNew->nCol>0 ); |
| 117997 | nAlloc = (((pNew->nCol-1)/8)*8)+8; |
| 117998 | assert( nAlloc>=pNew->nCol && nAlloc%8==0 && nAlloc-pNew->nCol<8 ); |
| 117999 | pNew->aCol = (Column*)sqlite3DbMallocZero(db, sizeof(Column)*(u32)nAlloc); |
| 118000 | pNew->zName = sqlite3MPrintf(db, "sqlite_altertab_%s", pTab->zName); |
| 118001 | if( !pNew->aCol || !pNew->zName ){ |
| 118002 | assert( db->mallocFailed ); |
| 118003 | goto exit_begin_add_column; |
| 118004 | } |
| 118005 | memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*(size_t)pNew->nCol); |
| 118006 | for(i=0; i<pNew->nCol; i++){ |
| 118007 | Column *pCol = &pNew->aCol[i]; |
| 118008 | pCol->zCnName = sqlite3DbStrDup(db, pCol->zCnName); |
| 118009 | pCol->hName = sqlite3StrIHash(pCol->zCnName); |
| 118010 | } |
| @@ -118086,23 +118599,34 @@ | |
| 118599 | sqlite3 *db, /* Database handle */ |
| 118600 | const char *zSql, /* SQL to parse */ |
| 118601 | int bTemp /* True if SQL is from temp schema */ |
| 118602 | ){ |
| 118603 | int rc; |
| 118604 | u64 flags; |
| 118605 | |
| 118606 | sqlite3ParseObjectInit(p, db); |
| 118607 | if( zSql==0 ){ |
| 118608 | return SQLITE_NOMEM; |
| 118609 | } |
| 118610 | if( sqlite3StrNICmp(zSql,"CREATE ",7)!=0 ){ |
| 118611 | return SQLITE_CORRUPT_BKPT; |
| 118612 | } |
| 118613 | if( bTemp ){ |
| 118614 | db->init.iDb = 1; |
| 118615 | }else{ |
| 118616 | int iDb = sqlite3FindDbName(db, zDb); |
| 118617 | assert( iDb>=0 && iDb<=0xff ); |
| 118618 | db->init.iDb = (u8)iDb; |
| 118619 | } |
| 118620 | p->eParseMode = PARSE_MODE_RENAME; |
| 118621 | p->db = db; |
| 118622 | p->nQueryLoop = 1; |
| 118623 | flags = db->flags; |
| 118624 | testcase( (db->flags & SQLITE_Comments)==0 && strstr(zSql," /* ")!=0 ); |
| 118625 | db->flags |= SQLITE_Comments; |
| 118626 | rc = sqlite3RunParser(p, zSql); |
| 118627 | db->flags = flags; |
| 118628 | if( db->mallocFailed ) rc = SQLITE_NOMEM; |
| 118629 | if( rc==SQLITE_OK |
| 118630 | && NEVER(p->pNewTable==0 && p->pNewIndex==0 && p->pNewTrigger==0) |
| 118631 | ){ |
| 118632 | rc = SQLITE_CORRUPT_BKPT; |
| @@ -118161,14 +118685,15 @@ | |
| 118685 | return SQLITE_NOMEM; |
| 118686 | }else{ |
| 118687 | nQuot = sqlite3Strlen30(zQuot)-1; |
| 118688 | } |
| 118689 | |
| 118690 | assert( nQuot>=nNew && nSql>=0 && nNew>=0 ); |
| 118691 | zOut = sqlite3DbMallocZero(db, (u64)(nSql + pRename->nList*nQuot + 1)); |
| 118692 | }else{ |
| 118693 | assert( nSql>0 ); |
| 118694 | zOut = (char*)sqlite3DbMallocZero(db, (u64)(nSql*2+1) * 3); |
| 118695 | if( zOut ){ |
| 118696 | zBuf1 = &zOut[nSql*2+1]; |
| 118697 | zBuf2 = &zOut[nSql*4+2]; |
| 118698 | } |
| 118699 | } |
| @@ -118176,20 +118701,21 @@ | |
| 118701 | /* At this point pRename->pList contains a list of RenameToken objects |
| 118702 | ** corresponding to all tokens in the input SQL that must be replaced |
| 118703 | ** with the new column name, or with single-quoted versions of themselves. |
| 118704 | ** All that remains is to construct and return the edited SQL string. */ |
| 118705 | if( zOut ){ |
| 118706 | i64 nOut = nSql; |
| 118707 | assert( nSql>0 ); |
| 118708 | memcpy(zOut, zSql, (size_t)nSql); |
| 118709 | while( pRename->pList ){ |
| 118710 | int iOff; /* Offset of token to replace in zOut */ |
| 118711 | i64 nReplace; |
| 118712 | const char *zReplace; |
| 118713 | RenameToken *pBest = renameColumnTokenNext(pRename); |
| 118714 | |
| 118715 | if( zNew ){ |
| 118716 | if( bQuote==0 && sqlite3IsIdChar(*(u8*)pBest->t.z) ){ |
| 118717 | nReplace = nNew; |
| 118718 | zReplace = zNew; |
| 118719 | }else{ |
| 118720 | nReplace = nQuot; |
| 118721 | zReplace = zQuot; |
| @@ -118203,18 +118729,19 @@ | |
| 118729 | ** token. This is so that (SELECT "string"'alias') maps to |
| 118730 | ** (SELECT 'string' 'alias'), and not (SELECT 'string''alias'). */ |
| 118731 | memcpy(zBuf1, pBest->t.z, pBest->t.n); |
| 118732 | zBuf1[pBest->t.n] = 0; |
| 118733 | sqlite3Dequote(zBuf1); |
| 118734 | assert( nSql < 0x15555554 /* otherwise malloc would have failed */ ); |
| 118735 | sqlite3_snprintf((int)(nSql*2), zBuf2, "%Q%s", zBuf1, |
| 118736 | pBest->t.z[pBest->t.n]=='\'' ? " " : "" |
| 118737 | ); |
| 118738 | zReplace = zBuf2; |
| 118739 | nReplace = sqlite3Strlen30(zReplace); |
| 118740 | } |
| 118741 | |
| 118742 | iOff = (int)(pBest->t.z - zSql); |
| 118743 | if( pBest->t.n!=nReplace ){ |
| 118744 | memmove(&zOut[iOff + nReplace], &zOut[iOff + pBest->t.n], |
| 118745 | nOut - (iOff + pBest->t.n) |
| 118746 | ); |
| 118747 | nOut += nReplace - pBest->t.n; |
| @@ -118236,15 +118763,16 @@ | |
| 118763 | |
| 118764 | /* |
| 118765 | ** Set all pEList->a[].fg.eEName fields in the expression-list to val. |
| 118766 | */ |
| 118767 | static void renameSetENames(ExprList *pEList, int val){ |
| 118768 | assert( val==ENAME_NAME || val==ENAME_TAB || val==ENAME_SPAN ); |
| 118769 | if( pEList ){ |
| 118770 | int i; |
| 118771 | for(i=0; i<pEList->nExpr; i++){ |
| 118772 | assert( val==ENAME_NAME || pEList->a[i].fg.eEName==ENAME_NAME ); |
| 118773 | pEList->a[i].fg.eEName = val&0x3; |
| 118774 | } |
| 118775 | } |
| 118776 | } |
| 118777 | |
| 118778 | /* |
| @@ -118497,11 +119025,11 @@ | |
| 119025 | sCtx.pTab = pTab; |
| 119026 | if( rc!=SQLITE_OK ) goto renameColumnFunc_done; |
| 119027 | if( sParse.pNewTable ){ |
| 119028 | if( IsView(sParse.pNewTable) ){ |
| 119029 | Select *pSelect = sParse.pNewTable->u.view.pSelect; |
| 119030 | pSelect->selFlags &= ~(u32)SF_View; |
| 119031 | sParse.rc = SQLITE_OK; |
| 119032 | sqlite3SelectPrep(&sParse, pSelect, 0); |
| 119033 | rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc); |
| 119034 | if( rc==SQLITE_OK ){ |
| 119035 | sqlite3WalkSelect(&sWalker, pSelect); |
| @@ -118715,11 +119243,11 @@ | |
| 119243 | NameContext sNC; |
| 119244 | memset(&sNC, 0, sizeof(sNC)); |
| 119245 | sNC.pParse = &sParse; |
| 119246 | |
| 119247 | assert( pSelect->selFlags & SF_View ); |
| 119248 | pSelect->selFlags &= ~(u32)SF_View; |
| 119249 | sqlite3SelectPrep(&sParse, pTab->u.view.pSelect, &sNC); |
| 119250 | if( sParse.nErr ){ |
| 119251 | rc = sParse.rc; |
| 119252 | }else{ |
| 119253 | sqlite3WalkSelect(&sWalker, pTab->u.view.pSelect); |
| @@ -118888,11 +119416,11 @@ | |
| 119416 | sWalker.u.pRename = &sCtx; |
| 119417 | |
| 119418 | if( sParse.pNewTable ){ |
| 119419 | if( IsView(sParse.pNewTable) ){ |
| 119420 | Select *pSelect = sParse.pNewTable->u.view.pSelect; |
| 119421 | pSelect->selFlags &= ~(u32)SF_View; |
| 119422 | sParse.rc = SQLITE_OK; |
| 119423 | sqlite3SelectPrep(&sParse, pSelect, 0); |
| 119424 | rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc); |
| 119425 | if( rc==SQLITE_OK ){ |
| 119426 | sqlite3WalkSelect(&sWalker, pSelect); |
| @@ -118987,14 +119515,14 @@ | |
| 119515 | UNUSED_PARAMETER(NotUsed); |
| 119516 | |
| 119517 | if( zDb && zInput ){ |
| 119518 | int rc; |
| 119519 | Parse sParse; |
| 119520 | u64 flags = db->flags; |
| 119521 | if( bNoDQS ) db->flags &= ~(SQLITE_DqsDML|SQLITE_DqsDDL); |
| 119522 | rc = renameParseSql(&sParse, zDb, db, zInput, bTemp); |
| 119523 | db->flags = flags; |
| 119524 | if( rc==SQLITE_OK ){ |
| 119525 | if( isLegacy==0 && sParse.pNewTable && IsView(sParse.pNewTable) ){ |
| 119526 | NameContext sNC; |
| 119527 | memset(&sNC, 0, sizeof(sNC)); |
| 119528 | sNC.pParse = &sParse; |
| @@ -119710,11 +120238,11 @@ | |
| 120238 | } |
| 120239 | |
| 120240 | p->db = db; |
| 120241 | p->nEst = sqlite3_value_int64(argv[2]); |
| 120242 | p->nRow = 0; |
| 120243 | p->nLimit = sqlite3_value_int(argv[3]); |
| 120244 | p->nCol = nCol; |
| 120245 | p->nKeyCol = nKeyCol; |
| 120246 | p->nSkipAhead = 0; |
| 120247 | p->current.anDLt = (tRowcnt*)&p[1]; |
| 120248 | |
| @@ -121519,10 +122047,17 @@ | |
| 122047 | */ |
| 122048 | if( rc==SQLITE_OK ){ |
| 122049 | sqlite3BtreeEnterAll(db); |
| 122050 | db->init.iDb = 0; |
| 122051 | db->mDbFlags &= ~(DBFLAG_SchemaKnownOk); |
| 122052 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 122053 | if( db->setlkFlags & SQLITE_SETLK_BLOCK_ON_CONNECT ){ |
| 122054 | int val = 1; |
| 122055 | sqlite3_file *fd = sqlite3PagerFile(sqlite3BtreePager(pNew->pBt)); |
| 122056 | sqlite3OsFileControlHint(fd, SQLITE_FCNTL_BLOCK_ON_CONNECT, &val); |
| 122057 | } |
| 122058 | #endif |
| 122059 | if( !REOPEN_AS_MEMDB(db) ){ |
| 122060 | rc = sqlite3Init(db, &zErrDyn); |
| 122061 | } |
| 122062 | sqlite3BtreeLeaveAll(db); |
| 122063 | assert( zErrDyn==0 || rc!=SQLITE_OK ); |
| @@ -123240,14 +123775,20 @@ | |
| 123775 | ** Convert an table column number into a index column number. That is, |
| 123776 | ** for the column iCol in the table (as defined by the CREATE TABLE statement) |
| 123777 | ** find the (first) offset of that column in index pIdx. Or return -1 |
| 123778 | ** if column iCol is not used in index pIdx. |
| 123779 | */ |
| 123780 | SQLITE_PRIVATE int sqlite3TableColumnToIndex(Index *pIdx, int iCol){ |
| 123781 | int i; |
| 123782 | i16 iCol16; |
| 123783 | assert( iCol>=(-1) && iCol<=SQLITE_MAX_COLUMN ); |
| 123784 | assert( pIdx->nColumn<=SQLITE_MAX_COLUMN ); |
| 123785 | iCol16 = iCol; |
| 123786 | for(i=0; i<pIdx->nColumn; i++){ |
| 123787 | if( iCol16==pIdx->aiColumn[i] ){ |
| 123788 | return i; |
| 123789 | } |
| 123790 | } |
| 123791 | return -1; |
| 123792 | } |
| 123793 | |
| 123794 | #ifndef SQLITE_OMIT_GENERATED_COLUMNS |
| @@ -124340,16 +124881,21 @@ | |
| 124881 | |
| 124882 | /* |
| 124883 | ** Resize an Index object to hold N columns total. Return SQLITE_OK |
| 124884 | ** on success and SQLITE_NOMEM on an OOM error. |
| 124885 | */ |
| 124886 | static int resizeIndexObject(Parse *pParse, Index *pIdx, int N){ |
| 124887 | char *zExtra; |
| 124888 | u64 nByte; |
| 124889 | sqlite3 *db; |
| 124890 | if( pIdx->nColumn>=N ) return SQLITE_OK; |
| 124891 | db = pParse->db; |
| 124892 | assert( N>0 ); |
| 124893 | assert( N <= SQLITE_MAX_COLUMN*2 /* tag-20250221-1 */ ); |
| 124894 | testcase( N==2*pParse->db->aLimit[SQLITE_LIMIT_COLUMN] ); |
| 124895 | assert( pIdx->isResized==0 ); |
| 124896 | nByte = (sizeof(char*) + sizeof(LogEst) + sizeof(i16) + 1)*(u64)N; |
| 124897 | zExtra = sqlite3DbMallocZero(db, nByte); |
| 124898 | if( zExtra==0 ) return SQLITE_NOMEM_BKPT; |
| 124899 | memcpy(zExtra, pIdx->azColl, sizeof(char*)*pIdx->nColumn); |
| 124900 | pIdx->azColl = (const char**)zExtra; |
| 124901 | zExtra += sizeof(char*)*N; |
| @@ -124359,11 +124905,11 @@ | |
| 124905 | memcpy(zExtra, pIdx->aiColumn, sizeof(i16)*pIdx->nColumn); |
| 124906 | pIdx->aiColumn = (i16*)zExtra; |
| 124907 | zExtra += sizeof(i16)*N; |
| 124908 | memcpy(zExtra, pIdx->aSortOrder, pIdx->nColumn); |
| 124909 | pIdx->aSortOrder = (u8*)zExtra; |
| 124910 | pIdx->nColumn = (u16)N; /* See tag-20250221-1 above for proof of safety */ |
| 124911 | pIdx->isResized = 1; |
| 124912 | return SQLITE_OK; |
| 124913 | } |
| 124914 | |
| 124915 | /* |
| @@ -124613,11 +125159,11 @@ | |
| 125159 | if( n==0 ){ |
| 125160 | /* This index is a superset of the primary key */ |
| 125161 | pIdx->nColumn = pIdx->nKeyCol; |
| 125162 | continue; |
| 125163 | } |
| 125164 | if( resizeIndexObject(pParse, pIdx, pIdx->nKeyCol+n) ) return; |
| 125165 | for(i=0, j=pIdx->nKeyCol; i<nPk; i++){ |
| 125166 | if( !isDupColumn(pIdx, pIdx->nKeyCol, pPk, i) ){ |
| 125167 | testcase( hasColumn(pIdx->aiColumn, pIdx->nKeyCol, pPk->aiColumn[i]) ); |
| 125168 | pIdx->aiColumn[j] = pPk->aiColumn[i]; |
| 125169 | pIdx->azColl[j] = pPk->azColl[i]; |
| @@ -124637,11 +125183,11 @@ | |
| 125183 | nExtra = 0; |
| 125184 | for(i=0; i<pTab->nCol; i++){ |
| 125185 | if( !hasColumn(pPk->aiColumn, nPk, i) |
| 125186 | && (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 ) nExtra++; |
| 125187 | } |
| 125188 | if( resizeIndexObject(pParse, pPk, nPk+nExtra) ) return; |
| 125189 | for(i=0, j=nPk; i<pTab->nCol; i++){ |
| 125190 | if( !hasColumn(pPk->aiColumn, j, i) |
| 125191 | && (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 |
| 125192 | ){ |
| 125193 | assert( j<pPk->nColumn ); |
| @@ -125795,11 +126341,11 @@ | |
| 126341 | "columns in the referenced table"); |
| 126342 | goto fk_end; |
| 126343 | }else{ |
| 126344 | nCol = pFromCol->nExpr; |
| 126345 | } |
| 126346 | nByte = SZ_FKEY(nCol) + pTo->n + 1; |
| 126347 | if( pToCol ){ |
| 126348 | for(i=0; i<pToCol->nExpr; i++){ |
| 126349 | nByte += sqlite3Strlen30(pToCol->a[i].zEName) + 1; |
| 126350 | } |
| 126351 | } |
| @@ -126021,17 +126567,18 @@ | |
| 126567 | ** of 8-byte aligned space after the Index object and return a |
| 126568 | ** pointer to this extra space in *ppExtra. |
| 126569 | */ |
| 126570 | SQLITE_PRIVATE Index *sqlite3AllocateIndexObject( |
| 126571 | sqlite3 *db, /* Database connection */ |
| 126572 | int nCol, /* Total number of columns in the index */ |
| 126573 | int nExtra, /* Number of bytes of extra space to alloc */ |
| 126574 | char **ppExtra /* Pointer to the "extra" space */ |
| 126575 | ){ |
| 126576 | Index *p; /* Allocated index object */ |
| 126577 | i64 nByte; /* Bytes of space for Index object + arrays */ |
| 126578 | |
| 126579 | assert( nCol <= 2*db->aLimit[SQLITE_LIMIT_COLUMN] ); |
| 126580 | nByte = ROUND8(sizeof(Index)) + /* Index structure */ |
| 126581 | ROUND8(sizeof(char*)*nCol) + /* Index.azColl */ |
| 126582 | ROUND8(sizeof(LogEst)*(nCol+1) + /* Index.aiRowLogEst */ |
| 126583 | sizeof(i16)*nCol + /* Index.aiColumn */ |
| 126584 | sizeof(u8)*nCol); /* Index.aSortOrder */ |
| @@ -126040,12 +126587,13 @@ | |
| 126587 | char *pExtra = ((char*)p)+ROUND8(sizeof(Index)); |
| 126588 | p->azColl = (const char**)pExtra; pExtra += ROUND8(sizeof(char*)*nCol); |
| 126589 | p->aiRowLogEst = (LogEst*)pExtra; pExtra += sizeof(LogEst)*(nCol+1); |
| 126590 | p->aiColumn = (i16*)pExtra; pExtra += sizeof(i16)*nCol; |
| 126591 | p->aSortOrder = (u8*)pExtra; |
| 126592 | assert( nCol>0 ); |
| 126593 | p->nColumn = (u16)nCol; |
| 126594 | p->nKeyCol = (u16)(nCol - 1); |
| 126595 | *ppExtra = ((char*)p) + nByte; |
| 126596 | } |
| 126597 | return p; |
| 126598 | } |
| 126599 | |
| @@ -126852,16 +127400,15 @@ | |
| 127400 | */ |
| 127401 | SQLITE_PRIVATE IdList *sqlite3IdListAppend(Parse *pParse, IdList *pList, Token *pToken){ |
| 127402 | sqlite3 *db = pParse->db; |
| 127403 | int i; |
| 127404 | if( pList==0 ){ |
| 127405 | pList = sqlite3DbMallocZero(db, SZ_IDLIST(1)); |
| 127406 | if( pList==0 ) return 0; |
| 127407 | }else{ |
| 127408 | IdList *pNew; |
| 127409 | pNew = sqlite3DbRealloc(db, pList, SZ_IDLIST(pList->nId+1)); |
| 127410 | if( pNew==0 ){ |
| 127411 | sqlite3IdListDelete(db, pList); |
| 127412 | return 0; |
| 127413 | } |
| 127414 | pList = pNew; |
| @@ -126956,12 +127503,11 @@ | |
| 127503 | sqlite3ErrorMsg(pParse, "too many FROM clause terms, max: %d", |
| 127504 | SQLITE_MAX_SRCLIST); |
| 127505 | return 0; |
| 127506 | } |
| 127507 | if( nAlloc>SQLITE_MAX_SRCLIST ) nAlloc = SQLITE_MAX_SRCLIST; |
| 127508 | pNew = sqlite3DbRealloc(db, pSrc, SZ_SRCLIST(nAlloc)); |
| 127509 | if( pNew==0 ){ |
| 127510 | assert( db->mallocFailed ); |
| 127511 | return 0; |
| 127512 | } |
| 127513 | pSrc = pNew; |
| @@ -127032,11 +127578,11 @@ | |
| 127578 | assert( pDatabase==0 || pTable!=0 ); /* Cannot have C without B */ |
| 127579 | assert( pParse!=0 ); |
| 127580 | assert( pParse->db!=0 ); |
| 127581 | db = pParse->db; |
| 127582 | if( pList==0 ){ |
| 127583 | pList = sqlite3DbMallocRawNN(pParse->db, SZ_SRCLIST(1)); |
| 127584 | if( pList==0 ) return 0; |
| 127585 | pList->nAlloc = 1; |
| 127586 | pList->nSrc = 1; |
| 127587 | memset(&pList->a[0], 0, sizeof(pList->a[0])); |
| 127588 | pList->a[0].iCursor = -1; |
| @@ -127918,14 +128464,13 @@ | |
| 128464 | } |
| 128465 | } |
| 128466 | } |
| 128467 | |
| 128468 | if( pWith ){ |
| 128469 | pNew = sqlite3DbRealloc(db, pWith, SZ_WITH(pWith->nCte+1)); |
| 128470 | }else{ |
| 128471 | pNew = sqlite3DbMallocZero(db, SZ_WITH(1)); |
| 128472 | } |
| 128473 | assert( (pNew!=0 && zName!=0) || db->mallocFailed ); |
| 128474 | |
| 128475 | if( db->mallocFailed ){ |
| 128476 | sqlite3CteDelete(db, pCte); |
| @@ -130629,11 +131174,11 @@ | |
| 131174 | |
| 131175 | /* |
| 131176 | ** Append to pStr text that is the SQL literal representation of the |
| 131177 | ** value contained in pValue. |
| 131178 | */ |
| 131179 | SQLITE_PRIVATE void sqlite3QuoteValue(StrAccum *pStr, sqlite3_value *pValue, int bEscape){ |
| 131180 | /* As currently implemented, the string must be initially empty. |
| 131181 | ** we might relax this requirement in the future, but that will |
| 131182 | ** require enhancements to the implementation. */ |
| 131183 | assert( pStr!=0 && pStr->nChar==0 ); |
| 131184 | |
| @@ -130677,20 +131222,119 @@ | |
| 131222 | } |
| 131223 | break; |
| 131224 | } |
| 131225 | case SQLITE_TEXT: { |
| 131226 | const unsigned char *zArg = sqlite3_value_text(pValue); |
| 131227 | sqlite3_str_appendf(pStr, bEscape ? "%#Q" : "%Q", zArg); |
| 131228 | break; |
| 131229 | } |
| 131230 | default: { |
| 131231 | assert( sqlite3_value_type(pValue)==SQLITE_NULL ); |
| 131232 | sqlite3_str_append(pStr, "NULL", 4); |
| 131233 | break; |
| 131234 | } |
| 131235 | } |
| 131236 | } |
| 131237 | |
| 131238 | /* |
| 131239 | ** Return true if z[] begins with N hexadecimal digits, and write |
| 131240 | ** a decoding of those digits into *pVal. Or return false if any |
| 131241 | ** one of the first N characters in z[] is not a hexadecimal digit. |
| 131242 | */ |
| 131243 | static int isNHex(const char *z, int N, u32 *pVal){ |
| 131244 | int i; |
| 131245 | int v = 0; |
| 131246 | for(i=0; i<N; i++){ |
| 131247 | if( !sqlite3Isxdigit(z[i]) ) return 0; |
| 131248 | v = (v<<4) + sqlite3HexToInt(z[i]); |
| 131249 | } |
| 131250 | *pVal = v; |
| 131251 | return 1; |
| 131252 | } |
| 131253 | |
| 131254 | /* |
| 131255 | ** Implementation of the UNISTR() function. |
| 131256 | ** |
| 131257 | ** This is intended to be a work-alike of the UNISTR() function in |
| 131258 | ** PostgreSQL. Quoting from the PG documentation (PostgreSQL 17 - |
| 131259 | ** scraped on 2025-02-22): |
| 131260 | ** |
| 131261 | ** Evaluate escaped Unicode characters in the argument. Unicode |
| 131262 | ** characters can be specified as \XXXX (4 hexadecimal digits), |
| 131263 | ** \+XXXXXX (6 hexadecimal digits), \uXXXX (4 hexadecimal digits), |
| 131264 | ** or \UXXXXXXXX (8 hexadecimal digits). To specify a backslash, |
| 131265 | ** write two backslashes. All other characters are taken literally. |
| 131266 | */ |
| 131267 | static void unistrFunc( |
| 131268 | sqlite3_context *context, |
| 131269 | int argc, |
| 131270 | sqlite3_value **argv |
| 131271 | ){ |
| 131272 | char *zOut; |
| 131273 | const char *zIn; |
| 131274 | int nIn; |
| 131275 | int i, j, n; |
| 131276 | u32 v; |
| 131277 | |
| 131278 | assert( argc==1 ); |
| 131279 | UNUSED_PARAMETER( argc ); |
| 131280 | zIn = (const char*)sqlite3_value_text(argv[0]); |
| 131281 | if( zIn==0 ) return; |
| 131282 | nIn = sqlite3_value_bytes(argv[0]); |
| 131283 | zOut = sqlite3_malloc64(nIn+1); |
| 131284 | if( zOut==0 ){ |
| 131285 | sqlite3_result_error_nomem(context); |
| 131286 | return; |
| 131287 | } |
| 131288 | i = j = 0; |
| 131289 | while( i<nIn ){ |
| 131290 | char *z = strchr(&zIn[i],'\\'); |
| 131291 | if( z==0 ){ |
| 131292 | n = nIn - i; |
| 131293 | memmove(&zOut[j], &zIn[i], n); |
| 131294 | j += n; |
| 131295 | break; |
| 131296 | } |
| 131297 | n = z - &zIn[i]; |
| 131298 | if( n>0 ){ |
| 131299 | memmove(&zOut[j], &zIn[i], n); |
| 131300 | j += n; |
| 131301 | i += n; |
| 131302 | } |
| 131303 | if( zIn[i+1]=='\\' ){ |
| 131304 | i += 2; |
| 131305 | zOut[j++] = '\\'; |
| 131306 | }else if( sqlite3Isxdigit(zIn[i+1]) ){ |
| 131307 | if( !isNHex(&zIn[i+1], 4, &v) ) goto unistr_error; |
| 131308 | i += 5; |
| 131309 | j += sqlite3AppendOneUtf8Character(&zOut[j], v); |
| 131310 | }else if( zIn[i+1]=='+' ){ |
| 131311 | if( !isNHex(&zIn[i+2], 6, &v) ) goto unistr_error; |
| 131312 | i += 8; |
| 131313 | j += sqlite3AppendOneUtf8Character(&zOut[j], v); |
| 131314 | }else if( zIn[i+1]=='u' ){ |
| 131315 | if( !isNHex(&zIn[i+2], 4, &v) ) goto unistr_error; |
| 131316 | i += 6; |
| 131317 | j += sqlite3AppendOneUtf8Character(&zOut[j], v); |
| 131318 | }else if( zIn[i+1]=='U' ){ |
| 131319 | if( !isNHex(&zIn[i+2], 8, &v) ) goto unistr_error; |
| 131320 | i += 10; |
| 131321 | j += sqlite3AppendOneUtf8Character(&zOut[j], v); |
| 131322 | }else{ |
| 131323 | goto unistr_error; |
| 131324 | } |
| 131325 | } |
| 131326 | zOut[j] = 0; |
| 131327 | sqlite3_result_text64(context, zOut, j, sqlite3_free, SQLITE_UTF8); |
| 131328 | return; |
| 131329 | |
| 131330 | unistr_error: |
| 131331 | sqlite3_free(zOut); |
| 131332 | sqlite3_result_error(context, "invalid Unicode escape", -1); |
| 131333 | return; |
| 131334 | } |
| 131335 | |
| 131336 | |
| 131337 | /* |
| 131338 | ** Implementation of the QUOTE() function. |
| 131339 | ** |
| 131340 | ** The quote(X) function returns the text of an SQL literal which is the |
| @@ -130697,18 +131341,22 @@ | |
| 131341 | ** value of its argument suitable for inclusion into an SQL statement. |
| 131342 | ** Strings are surrounded by single-quotes with escapes on interior quotes |
| 131343 | ** as needed. BLOBs are encoded as hexadecimal literals. Strings with |
| 131344 | ** embedded NUL characters cannot be represented as string literals in SQL |
| 131345 | ** and hence the returned string literal is truncated prior to the first NUL. |
| 131346 | ** |
| 131347 | ** If sqlite3_user_data() is non-zero, then the UNISTR_QUOTE() function is |
| 131348 | ** implemented instead. The difference is that UNISTR_QUOTE() uses the |
| 131349 | ** UNISTR() function to escape control characters. |
| 131350 | */ |
| 131351 | static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ |
| 131352 | sqlite3_str str; |
| 131353 | sqlite3 *db = sqlite3_context_db_handle(context); |
| 131354 | assert( argc==1 ); |
| 131355 | UNUSED_PARAMETER(argc); |
| 131356 | sqlite3StrAccumInit(&str, db, 0, 0, db->aLimit[SQLITE_LIMIT_LENGTH]); |
| 131357 | sqlite3QuoteValue(&str,argv[0],SQLITE_PTR_TO_INT(sqlite3_user_data(context))); |
| 131358 | sqlite3_result_text(context, sqlite3StrAccumFinish(&str), str.nChar, |
| 131359 | SQLITE_DYNAMIC); |
| 131360 | if( str.accError!=SQLITE_OK ){ |
| 131361 | sqlite3_result_null(context); |
| 131362 | sqlite3_result_error_code(context, str.accError); |
| @@ -131355,11 +132003,11 @@ | |
| 132003 | ** |
| 132004 | ** The SUM() function follows the (broken) SQL standard which means |
| 132005 | ** that it returns NULL if it sums over no inputs. TOTAL returns |
| 132006 | ** 0.0 in that case. In addition, TOTAL always returns a float where |
| 132007 | ** SUM might return an integer if it never encounters a floating point |
| 132008 | ** value. TOTAL never fails, but SUM might throw an exception if |
| 132009 | ** it overflows an integer. |
| 132010 | */ |
| 132011 | static void sumStep(sqlite3_context *context, int argc, sqlite3_value **argv){ |
| 132012 | SumCtx *p; |
| 132013 | int type; |
| @@ -132275,11 +132923,13 @@ | |
| 132923 | VFUNCTION(randomblob, 1, 0, 0, randomBlob ), |
| 132924 | FUNCTION(nullif, 2, 0, 1, nullifFunc ), |
| 132925 | DFUNCTION(sqlite_version, 0, 0, 0, versionFunc ), |
| 132926 | DFUNCTION(sqlite_source_id, 0, 0, 0, sourceidFunc ), |
| 132927 | FUNCTION(sqlite_log, 2, 0, 0, errlogFunc ), |
| 132928 | FUNCTION(unistr, 1, 0, 0, unistrFunc ), |
| 132929 | FUNCTION(quote, 1, 0, 0, quoteFunc ), |
| 132930 | FUNCTION(unistr_quote, 1, 1, 0, quoteFunc ), |
| 132931 | VFUNCTION(last_insert_rowid, 0, 0, 0, last_insert_rowid), |
| 132932 | VFUNCTION(changes, 0, 0, 0, changes ), |
| 132933 | VFUNCTION(total_changes, 0, 0, 0, total_changes ), |
| 132934 | FUNCTION(replace, 3, 0, 0, replaceFunc ), |
| 132935 | FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc ), |
| @@ -134562,11 +135212,11 @@ | |
| 135212 | }else if( pLeft->pPrior ){ |
| 135213 | /* In this case set the SF_MultiValue flag only if it was set on pLeft */ |
| 135214 | f = (f & pLeft->selFlags); |
| 135215 | } |
| 135216 | pSelect = sqlite3SelectNew(pParse, pRow, 0, 0, 0, 0, 0, f, 0); |
| 135217 | pLeft->selFlags &= ~(u32)SF_MultiValue; |
| 135218 | if( pSelect ){ |
| 135219 | pSelect->op = TK_ALL; |
| 135220 | pSelect->pPrior = pLeft; |
| 135221 | pLeft = pSelect; |
| 135222 | } |
| @@ -139168,52 +139818,52 @@ | |
| 139818 | /* 11 */ "notnull", |
| 139819 | /* 12 */ "dflt_value", |
| 139820 | /* 13 */ "pk", |
| 139821 | /* 14 */ "hidden", |
| 139822 | /* table_info reuses 8 */ |
| 139823 | /* 15 */ "name", /* Used by: function_list */ |
| 139824 | /* 16 */ "builtin", |
| 139825 | /* 17 */ "type", |
| 139826 | /* 18 */ "enc", |
| 139827 | /* 19 */ "narg", |
| 139828 | /* 20 */ "flags", |
| 139829 | /* 21 */ "schema", /* Used by: table_list */ |
| 139830 | /* 22 */ "name", |
| 139831 | /* 23 */ "type", |
| 139832 | /* 24 */ "ncol", |
| 139833 | /* 25 */ "wr", |
| 139834 | /* 26 */ "strict", |
| 139835 | /* 27 */ "seqno", /* Used by: index_xinfo */ |
| 139836 | /* 28 */ "cid", |
| 139837 | /* 29 */ "name", |
| 139838 | /* 30 */ "desc", |
| 139839 | /* 31 */ "coll", |
| 139840 | /* 32 */ "key", |
| 139841 | /* 33 */ "seq", /* Used by: index_list */ |
| 139842 | /* 34 */ "name", |
| 139843 | /* 35 */ "unique", |
| 139844 | /* 36 */ "origin", |
| 139845 | /* 37 */ "partial", |
| 139846 | /* 38 */ "tbl", /* Used by: stats */ |
| 139847 | /* 39 */ "idx", |
| 139848 | /* 40 */ "wdth", |
| 139849 | /* 41 */ "hght", |
| 139850 | /* 42 */ "flgs", |
| 139851 | /* 43 */ "table", /* Used by: foreign_key_check */ |
| 139852 | /* 44 */ "rowid", |
| 139853 | /* 45 */ "parent", |
| 139854 | /* 46 */ "fkid", |
| 139855 | /* 47 */ "busy", /* Used by: wal_checkpoint */ |
| 139856 | /* 48 */ "log", |
| 139857 | /* 49 */ "checkpointed", |
| 139858 | /* 50 */ "seq", /* Used by: database_list */ |
| 139859 | /* 51 */ "name", |
| 139860 | /* 52 */ "file", |
| 139861 | /* index_info reuses 27 */ |
| 139862 | /* 53 */ "database", /* Used by: lock_status */ |
| 139863 | /* 54 */ "status", |
| 139864 | /* collation_list reuses 33 */ |
| 139865 | /* 55 */ "cache_size", /* Used by: default_cache_size */ |
| 139866 | /* module_list pragma_list reuses 9 */ |
| 139867 | /* 56 */ "timeout", /* Used by: busy_timeout */ |
| 139868 | }; |
| 139869 | |
| @@ -139302,11 +139952,11 @@ | |
| 139952 | #endif |
| 139953 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) |
| 139954 | {/* zName: */ "collation_list", |
| 139955 | /* ePragTyp: */ PragTyp_COLLATION_LIST, |
| 139956 | /* ePragFlg: */ PragFlg_Result0, |
| 139957 | /* ColNames: */ 33, 2, |
| 139958 | /* iArg: */ 0 }, |
| 139959 | #endif |
| 139960 | #if !defined(SQLITE_OMIT_COMPILEOPTION_DIAGS) |
| 139961 | {/* zName: */ "compile_options", |
| 139962 | /* ePragTyp: */ PragTyp_COMPILE_OPTIONS, |
| @@ -139337,11 +139987,11 @@ | |
| 139987 | #endif |
| 139988 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) |
| 139989 | {/* zName: */ "database_list", |
| 139990 | /* ePragTyp: */ PragTyp_DATABASE_LIST, |
| 139991 | /* ePragFlg: */ PragFlg_Result0, |
| 139992 | /* ColNames: */ 50, 3, |
| 139993 | /* iArg: */ 0 }, |
| 139994 | #endif |
| 139995 | #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED) |
| 139996 | {/* zName: */ "default_cache_size", |
| 139997 | /* ePragTyp: */ PragTyp_DEFAULT_CACHE_SIZE, |
| @@ -139417,11 +140067,11 @@ | |
| 140067 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) |
| 140068 | #if !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS) |
| 140069 | {/* zName: */ "function_list", |
| 140070 | /* ePragTyp: */ PragTyp_FUNCTION_LIST, |
| 140071 | /* ePragFlg: */ PragFlg_Result0, |
| 140072 | /* ColNames: */ 15, 6, |
| 140073 | /* iArg: */ 0 }, |
| 140074 | #endif |
| 140075 | #endif |
| 140076 | {/* zName: */ "hard_heap_limit", |
| 140077 | /* ePragTyp: */ PragTyp_HARD_HEAP_LIMIT, |
| @@ -139446,21 +140096,21 @@ | |
| 140096 | #endif |
| 140097 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) |
| 140098 | {/* zName: */ "index_info", |
| 140099 | /* ePragTyp: */ PragTyp_INDEX_INFO, |
| 140100 | /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, |
| 140101 | /* ColNames: */ 27, 3, |
| 140102 | /* iArg: */ 0 }, |
| 140103 | {/* zName: */ "index_list", |
| 140104 | /* ePragTyp: */ PragTyp_INDEX_LIST, |
| 140105 | /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, |
| 140106 | /* ColNames: */ 33, 5, |
| 140107 | /* iArg: */ 0 }, |
| 140108 | {/* zName: */ "index_xinfo", |
| 140109 | /* ePragTyp: */ PragTyp_INDEX_INFO, |
| 140110 | /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, |
| 140111 | /* ColNames: */ 27, 6, |
| 140112 | /* iArg: */ 1 }, |
| 140113 | #endif |
| 140114 | #if !defined(SQLITE_OMIT_INTEGRITY_CHECK) |
| 140115 | {/* zName: */ "integrity_check", |
| 140116 | /* ePragTyp: */ PragTyp_INTEGRITY_CHECK, |
| @@ -139635,11 +140285,11 @@ | |
| 140285 | #endif |
| 140286 | #if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) && defined(SQLITE_DEBUG) |
| 140287 | {/* zName: */ "stats", |
| 140288 | /* ePragTyp: */ PragTyp_STATS, |
| 140289 | /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq, |
| 140290 | /* ColNames: */ 38, 5, |
| 140291 | /* iArg: */ 0 }, |
| 140292 | #endif |
| 140293 | #if !defined(SQLITE_OMIT_PAGER_PRAGMAS) |
| 140294 | {/* zName: */ "synchronous", |
| 140295 | /* ePragTyp: */ PragTyp_SYNCHRONOUS, |
| @@ -139654,11 +140304,11 @@ | |
| 140304 | /* ColNames: */ 8, 6, |
| 140305 | /* iArg: */ 0 }, |
| 140306 | {/* zName: */ "table_list", |
| 140307 | /* ePragTyp: */ PragTyp_TABLE_LIST, |
| 140308 | /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1, |
| 140309 | /* ColNames: */ 21, 6, |
| 140310 | /* iArg: */ 0 }, |
| 140311 | {/* zName: */ "table_xinfo", |
| 140312 | /* ePragTyp: */ PragTyp_TABLE_INFO, |
| 140313 | /* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt, |
| 140314 | /* ColNames: */ 8, 7, |
| @@ -139731,11 +140381,11 @@ | |
| 140381 | /* ColNames: */ 0, 0, |
| 140382 | /* iArg: */ 0 }, |
| 140383 | {/* zName: */ "wal_checkpoint", |
| 140384 | /* ePragTyp: */ PragTyp_WAL_CHECKPOINT, |
| 140385 | /* ePragFlg: */ PragFlg_NeedSchema, |
| 140386 | /* ColNames: */ 47, 3, |
| 140387 | /* iArg: */ 0 }, |
| 140388 | #endif |
| 140389 | #if !defined(SQLITE_OMIT_FLAG_PRAGMAS) |
| 140390 | {/* zName: */ "writable_schema", |
| 140391 | /* ePragTyp: */ PragTyp_FLAG, |
| @@ -139753,11 +140403,11 @@ | |
| 140403 | ** When the 0x10 bit of PRAGMA optimize is set, any ANALYZE commands |
| 140404 | ** will be run with an analysis_limit set to the lessor of the value of |
| 140405 | ** the following macro or to the actual analysis_limit if it is non-zero, |
| 140406 | ** in order to prevent PRAGMA optimize from running for too long. |
| 140407 | ** |
| 140408 | ** The value of 2000 is chosen empirically so that the worst-case run-time |
| 140409 | ** for PRAGMA optimize does not exceed 100 milliseconds against a variety |
| 140410 | ** of test databases on a RaspberryPI-4 compiled using -Os and without |
| 140411 | ** -DSQLITE_DEBUG. Of course, your mileage may vary. For the purpose of |
| 140412 | ** this paragraph, "worst-case" means that ANALYZE ends up being |
| 140413 | ** run on every table in the database. The worst case typically only |
| @@ -144042,11 +144692,11 @@ | |
| 144692 | pNew->iOffset = 0; |
| 144693 | pNew->selId = ++pParse->nSelect; |
| 144694 | pNew->addrOpenEphm[0] = -1; |
| 144695 | pNew->addrOpenEphm[1] = -1; |
| 144696 | pNew->nSelectRow = 0; |
| 144697 | if( pSrc==0 ) pSrc = sqlite3DbMallocZero(pParse->db, SZ_SRCLIST_1); |
| 144698 | pNew->pSrc = pSrc; |
| 144699 | pNew->pWhere = pWhere; |
| 144700 | pNew->pGroupBy = pGroupBy; |
| 144701 | pNew->pHaving = pHaving; |
| 144702 | pNew->pOrderBy = pOrderBy; |
| @@ -145425,20 +146075,20 @@ | |
| 146075 | /* |
| 146076 | ** Allocate a KeyInfo object sufficient for an index of N key columns and |
| 146077 | ** X extra columns. |
| 146078 | */ |
| 146079 | SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){ |
| 146080 | int nExtra = (N+X)*(sizeof(CollSeq*)+1); |
| 146081 | KeyInfo *p = sqlite3DbMallocRawNN(db, SZ_KEYINFO(0) + nExtra); |
| 146082 | if( p ){ |
| 146083 | p->aSortFlags = (u8*)&p->aColl[N+X]; |
| 146084 | p->nKeyField = (u16)N; |
| 146085 | p->nAllField = (u16)(N+X); |
| 146086 | p->enc = ENC(db); |
| 146087 | p->db = db; |
| 146088 | p->nRef = 1; |
| 146089 | memset(p->aColl, 0, nExtra); |
| 146090 | }else{ |
| 146091 | return (KeyInfo*)sqlite3OomFault(db); |
| 146092 | } |
| 146093 | return p; |
| 146094 | } |
| @@ -149534,11 +150184,11 @@ | |
| 150184 | p->pNext = 0; |
| 150185 | p->pWith = 0; |
| 150186 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| 150187 | p->pWinDefn = 0; |
| 150188 | #endif |
| 150189 | p->selFlags &= ~(u32)SF_Compound; |
| 150190 | assert( (p->selFlags & SF_Converted)==0 ); |
| 150191 | p->selFlags |= SF_Converted; |
| 150192 | assert( pNew->pPrior!=0 ); |
| 150193 | pNew->pPrior->pNext = pNew; |
| 150194 | pNew->pLimit = 0; |
| @@ -149950,11 +150600,11 @@ | |
| 150600 | } |
| 150601 | pTabList = p->pSrc; |
| 150602 | pEList = p->pEList; |
| 150603 | if( pParse->pWith && (p->selFlags & SF_View) ){ |
| 150604 | if( p->pWith==0 ){ |
| 150605 | p->pWith = (With*)sqlite3DbMallocZero(db, SZ_WITH(1) ); |
| 150606 | if( p->pWith==0 ){ |
| 150607 | return WRC_Abort; |
| 150608 | } |
| 150609 | } |
| 150610 | p->pWith->bView = 1; |
| @@ -151089,10 +151739,11 @@ | |
| 151739 | ** * The subquery is a UNION ALL of two or more terms |
| 151740 | ** * The subquery does not have a LIMIT clause |
| 151741 | ** * There is no WHERE or GROUP BY or HAVING clauses on the subqueries |
| 151742 | ** * The outer query is a simple count(*) with no WHERE clause or other |
| 151743 | ** extraneous syntax. |
| 151744 | ** * None of the subqueries are DISTINCT (forumpost/a860f5fb2e 2025-03-10) |
| 151745 | ** |
| 151746 | ** Return TRUE if the optimization is undertaken. |
| 151747 | */ |
| 151748 | static int countOfViewOptimization(Parse *pParse, Select *p){ |
| 151749 | Select *pSub, *pPrior; |
| @@ -151121,11 +151772,15 @@ | |
| 151772 | if( pSub->selFlags & SF_CopyCte ) return 0; /* Not a CTE */ |
| 151773 | do{ |
| 151774 | if( pSub->op!=TK_ALL && pSub->pPrior ) return 0; /* Must be UNION ALL */ |
| 151775 | if( pSub->pWhere ) return 0; /* No WHERE clause */ |
| 151776 | if( pSub->pLimit ) return 0; /* No LIMIT clause */ |
| 151777 | if( pSub->selFlags & (SF_Aggregate|SF_Distinct) ){ |
| 151778 | testcase( pSub->selFlags & SF_Aggregate ); |
| 151779 | testcase( pSub->selFlags & SF_Distinct ); |
| 151780 | return 0; /* Not an aggregate nor DISTINCT */ |
| 151781 | } |
| 151782 | assert( pSub->pHaving==0 ); /* Due to the previous */ |
| 151783 | pSub = pSub->pPrior; /* Repeat over compound */ |
| 151784 | }while( pSub ); |
| 151785 | |
| 151786 | /* If we reach this point then it is OK to perform the transformation */ |
| @@ -151133,18 +151788,18 @@ | |
| 151788 | db = pParse->db; |
| 151789 | pCount = pExpr; |
| 151790 | pExpr = 0; |
| 151791 | pSub = sqlite3SubqueryDetach(db, pFrom); |
| 151792 | sqlite3SrcListDelete(db, p->pSrc); |
| 151793 | p->pSrc = sqlite3DbMallocZero(pParse->db, SZ_SRCLIST_1); |
| 151794 | while( pSub ){ |
| 151795 | Expr *pTerm; |
| 151796 | pPrior = pSub->pPrior; |
| 151797 | pSub->pPrior = 0; |
| 151798 | pSub->pNext = 0; |
| 151799 | pSub->selFlags |= SF_Aggregate; |
| 151800 | pSub->selFlags &= ~(u32)SF_Compound; |
| 151801 | pSub->nSelectRow = 0; |
| 151802 | sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, pSub->pEList); |
| 151803 | pTerm = pPrior ? sqlite3ExprDup(db, pCount, 0) : pCount; |
| 151804 | pSub->pEList = sqlite3ExprListAppend(pParse, 0, pTerm); |
| 151805 | pTerm = sqlite3PExpr(pParse, TK_SELECT, 0, 0); |
| @@ -151155,11 +151810,11 @@ | |
| 151810 | pExpr = sqlite3PExpr(pParse, TK_PLUS, pTerm, pExpr); |
| 151811 | } |
| 151812 | pSub = pPrior; |
| 151813 | } |
| 151814 | p->pEList->a[0].pExpr = pExpr; |
| 151815 | p->selFlags &= ~(u32)SF_Aggregate; |
| 151816 | |
| 151817 | #if TREETRACE_ENABLED |
| 151818 | if( sqlite3TreeTrace & 0x200 ){ |
| 151819 | TREETRACE(0x200,pParse,p,("After count-of-view optimization:\n")); |
| 151820 | sqlite3TreeViewSelect(0, p, 0); |
| @@ -151362,11 +152017,11 @@ | |
| 152017 | sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, |
| 152018 | p->pOrderBy); |
| 152019 | testcase( pParse->earlyCleanup ); |
| 152020 | p->pOrderBy = 0; |
| 152021 | } |
| 152022 | p->selFlags &= ~(u32)SF_Distinct; |
| 152023 | p->selFlags |= SF_NoopOrderBy; |
| 152024 | } |
| 152025 | sqlite3SelectPrep(pParse, p, 0); |
| 152026 | if( pParse->nErr ){ |
| 152027 | goto select_end; |
| @@ -151401,11 +152056,11 @@ | |
| 152056 | |
| 152057 | /* Clear the SF_UFSrcCheck flag. The check has already been performed, |
| 152058 | ** and leaving this flag set can cause errors if a compound sub-query |
| 152059 | ** in p->pSrc is flattened into this query and this function called |
| 152060 | ** again as part of compound SELECT processing. */ |
| 152061 | p->selFlags &= ~(u32)SF_UFSrcCheck; |
| 152062 | } |
| 152063 | |
| 152064 | if( pDest->eDest==SRT_Output ){ |
| 152065 | sqlite3GenerateColumnNames(pParse, p); |
| 152066 | } |
| @@ -151890,11 +152545,11 @@ | |
| 152545 | && OptimizationEnabled(db, SQLITE_GroupByOrder) |
| 152546 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| 152547 | && p->pWin==0 |
| 152548 | #endif |
| 152549 | ){ |
| 152550 | p->selFlags &= ~(u32)SF_Distinct; |
| 152551 | pGroupBy = p->pGroupBy = sqlite3ExprListDup(db, pEList, 0); |
| 152552 | if( pGroupBy ){ |
| 152553 | for(i=0; i<pGroupBy->nExpr; i++){ |
| 152554 | pGroupBy->a[i].u.x.iOrderByCol = i+1; |
| 152555 | } |
| @@ -153924,11 +154579,12 @@ | |
| 154579 | Vdbe *v = pParse->pVdbe; |
| 154580 | sqlite3 *db = pParse->db; |
| 154581 | ExprList *pNew; |
| 154582 | Returning *pReturning; |
| 154583 | Select sSelect; |
| 154584 | SrcList *pFrom; |
| 154585 | u8 fromSpace[SZ_SRCLIST_1]; |
| 154586 | |
| 154587 | assert( v!=0 ); |
| 154588 | if( !pParse->bReturning ){ |
| 154589 | /* This RETURNING trigger must be for a different statement as |
| 154590 | ** this statement lacks a RETURNING clause. */ |
| @@ -153940,17 +154596,18 @@ | |
| 154596 | if( pTrigger != &(pReturning->retTrig) ){ |
| 154597 | /* This RETURNING trigger is for a different statement */ |
| 154598 | return; |
| 154599 | } |
| 154600 | memset(&sSelect, 0, sizeof(sSelect)); |
| 154601 | pFrom = (SrcList*)fromSpace; |
| 154602 | memset(pFrom, 0, SZ_SRCLIST_1); |
| 154603 | sSelect.pEList = sqlite3ExprListDup(db, pReturning->pReturnEL, 0); |
| 154604 | sSelect.pSrc = pFrom; |
| 154605 | pFrom->nSrc = 1; |
| 154606 | pFrom->a[0].pSTab = pTab; |
| 154607 | pFrom->a[0].zName = pTab->zName; /* tag-20240424-1 */ |
| 154608 | pFrom->a[0].iCursor = -1; |
| 154609 | sqlite3SelectPrep(pParse, &sSelect, 0); |
| 154610 | if( pParse->nErr==0 ){ |
| 154611 | assert( db->mallocFailed==0 ); |
| 154612 | sqlite3GenerateColumnNames(pParse, &sSelect); |
| 154613 | } |
| @@ -156347,11 +157004,11 @@ | |
| 157004 | saved_flags = db->flags; |
| 157005 | saved_mDbFlags = db->mDbFlags; |
| 157006 | saved_nChange = db->nChange; |
| 157007 | saved_nTotalChange = db->nTotalChange; |
| 157008 | saved_mTrace = db->mTrace; |
| 157009 | db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks | SQLITE_Comments; |
| 157010 | db->mDbFlags |= DBFLAG_PreferBuiltin | DBFLAG_Vacuum; |
| 157011 | db->flags &= ~(u64)(SQLITE_ForeignKeys | SQLITE_ReverseOrder |
| 157012 | | SQLITE_Defensive | SQLITE_CountRows); |
| 157013 | db->mTrace = 0; |
| 157014 | |
| @@ -158476,13 +159133,18 @@ | |
| 159133 | WhereLoop *pLoops; /* List of all WhereLoop objects */ |
| 159134 | WhereMemBlock *pMemToFree;/* Memory to free when this object destroyed */ |
| 159135 | Bitmask revMask; /* Mask of ORDER BY terms that need reversing */ |
| 159136 | WhereClause sWC; /* Decomposition of the WHERE clause */ |
| 159137 | WhereMaskSet sMaskSet; /* Map cursor numbers to bitmasks */ |
| 159138 | WhereLevel a[FLEXARRAY]; /* Information about each nest loop in WHERE */ |
| 159139 | }; |
| 159140 | |
| 159141 | /* |
| 159142 | ** The size (in bytes) of a WhereInfo object that holds N WhereLevels. |
| 159143 | */ |
| 159144 | #define SZ_WHEREINFO(N) ROUND8(offsetof(WhereInfo,a)+(N)*sizeof(WhereLevel)) |
| 159145 | |
| 159146 | /* |
| 159147 | ** Private interfaces - callable only by other where.c routines. |
| 159148 | ** |
| 159149 | ** where.c: |
| 159150 | */ |
| @@ -160929,12 +161591,11 @@ | |
| 161591 | */ |
| 161592 | if( pWInfo->nLevel>1 ){ |
| 161593 | int nNotReady; /* The number of notReady tables */ |
| 161594 | SrcItem *origSrc; /* Original list of tables */ |
| 161595 | nNotReady = pWInfo->nLevel - iLevel - 1; |
| 161596 | pOrTab = sqlite3DbMallocRawNN(db, SZ_SRCLIST(nNotReady+1)); |
| 161597 | if( pOrTab==0 ) return notReady; |
| 161598 | pOrTab->nAlloc = (u8)(nNotReady + 1); |
| 161599 | pOrTab->nSrc = pOrTab->nAlloc; |
| 161600 | memcpy(pOrTab->a, pTabItem, sizeof(*pTabItem)); |
| 161601 | origSrc = pWInfo->pTabList->a; |
| @@ -161473,11 +162134,12 @@ | |
| 162134 | Expr *pSubWhere = 0; |
| 162135 | WhereClause *pWC = &pWInfo->sWC; |
| 162136 | WhereInfo *pSubWInfo; |
| 162137 | WhereLoop *pLoop = pLevel->pWLoop; |
| 162138 | SrcItem *pTabItem = &pWInfo->pTabList->a[pLevel->iFrom]; |
| 162139 | SrcList *pFrom; |
| 162140 | u8 fromSpace[SZ_SRCLIST_1]; |
| 162141 | Bitmask mAll = 0; |
| 162142 | int k; |
| 162143 | |
| 162144 | ExplainQueryPlan((pParse, 1, "RIGHT-JOIN %s", pTabItem->pSTab->zName)); |
| 162145 | sqlite3VdbeNoJumpsOutsideSubrtn(v, pRJ->addrSubrtn, pRJ->endSubrtn, |
| @@ -161517,17 +162179,18 @@ | |
| 162179 | if( ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) ) continue; |
| 162180 | pSubWhere = sqlite3ExprAnd(pParse, pSubWhere, |
| 162181 | sqlite3ExprDup(pParse->db, pTerm->pExpr, 0)); |
| 162182 | } |
| 162183 | } |
| 162184 | pFrom = (SrcList*)fromSpace; |
| 162185 | pFrom->nSrc = 1; |
| 162186 | pFrom->nAlloc = 1; |
| 162187 | memcpy(&pFrom->a[0], pTabItem, sizeof(SrcItem)); |
| 162188 | pFrom->a[0].fg.jointype = 0; |
| 162189 | assert( pParse->withinRJSubrtn < 100 ); |
| 162190 | pParse->withinRJSubrtn++; |
| 162191 | pSubWInfo = sqlite3WhereBegin(pParse, pFrom, pSubWhere, 0, 0, 0, |
| 162192 | WHERE_RIGHT_JOIN, 0); |
| 162193 | if( pSubWInfo ){ |
| 162194 | int iCur = pLevel->iTabCur; |
| 162195 | int r = ++pParse->nMem; |
| 162196 | int nPk; |
| @@ -163511,15 +164174,20 @@ | |
| 164174 | WhereClause *pWC; /* The Where clause being analyzed */ |
| 164175 | Parse *pParse; /* The parsing context */ |
| 164176 | int eDistinct; /* Value to return from sqlite3_vtab_distinct() */ |
| 164177 | u32 mIn; /* Mask of terms that are <col> IN (...) */ |
| 164178 | u32 mHandleIn; /* Terms that vtab will handle as <col> IN (...) */ |
| 164179 | sqlite3_value *aRhs[FLEXARRAY]; /* RHS values for constraints. MUST BE LAST |
| 164180 | ** Extra space is allocated to hold up |
| 164181 | ** to nTerm such values */ |
| 164182 | }; |
| 164183 | |
| 164184 | /* Size (in bytes) of a HiddenIndeInfo object sufficient to hold as |
| 164185 | ** many as N constraints */ |
| 164186 | #define SZ_HIDDENINDEXINFO(N) \ |
| 164187 | (offsetof(HiddenIndexInfo,aRhs) + (N)*sizeof(sqlite3_value*)) |
| 164188 | |
| 164189 | /* Forward declaration of methods */ |
| 164190 | static int whereLoopResize(sqlite3*, WhereLoop*, int); |
| 164191 | |
| 164192 | /* |
| 164193 | ** Return the estimated number of output rows from a WHERE clause |
| @@ -164580,10 +165248,12 @@ | |
| 165248 | if( pSrc->colUsed & MASKBIT(BMS-1) ){ |
| 165249 | nKeyCol += pTable->nCol - BMS + 1; |
| 165250 | } |
| 165251 | |
| 165252 | /* Construct the Index object to describe this index */ |
| 165253 | assert( nKeyCol <= pTable->nCol + MAX(0, pTable->nCol - BMS + 1) ); |
| 165254 | /* ^-- This guarantees that the number of index columns will fit in the u16 */ |
| 165255 | pIdx = sqlite3AllocateIndexObject(pParse->db, nKeyCol+HasRowid(pTable), |
| 165256 | 0, &zNotUsed); |
| 165257 | if( pIdx==0 ) goto end_auto_index_create; |
| 165258 | pLoop->u.btree.pIndex = pIdx; |
| 165259 | pIdx->zName = "auto-index"; |
| @@ -164991,12 +165661,12 @@ | |
| 165661 | |
| 165662 | /* Allocate the sqlite3_index_info structure |
| 165663 | */ |
| 165664 | pIdxInfo = sqlite3DbMallocZero(pParse->db, sizeof(*pIdxInfo) |
| 165665 | + (sizeof(*pIdxCons) + sizeof(*pUsage))*nTerm |
| 165666 | + sizeof(*pIdxOrderBy)*nOrderBy |
| 165667 | + SZ_HIDDENINDEXINFO(nTerm) ); |
| 165668 | if( pIdxInfo==0 ){ |
| 165669 | sqlite3ErrorMsg(pParse, "out of memory"); |
| 165670 | return 0; |
| 165671 | } |
| 165672 | pHidden = (struct HiddenIndexInfo*)&pIdxInfo[1]; |
| @@ -170186,14 +170856,11 @@ | |
| 170856 | ** struct, the contents of WhereInfo.a[], the WhereClause structure |
| 170857 | ** and the WhereMaskSet structure. Since WhereClause contains an 8-byte |
| 170858 | ** field (type Bitmask) it must be aligned on an 8-byte boundary on |
| 170859 | ** some architectures. Hence the ROUND8() below. |
| 170860 | */ |
| 170861 | nByteWInfo = SZ_WHEREINFO(nTabList); |
| 170862 | pWInfo = sqlite3DbMallocRawNN(db, nByteWInfo + sizeof(WhereLoop)); |
| 170863 | if( db->mallocFailed ){ |
| 170864 | sqlite3DbFree(db, pWInfo); |
| 170865 | pWInfo = 0; |
| 170866 | goto whereBeginError; |
| @@ -172141,11 +172808,11 @@ | |
| 172808 | |
| 172809 | p->pSrc = 0; |
| 172810 | p->pWhere = 0; |
| 172811 | p->pGroupBy = 0; |
| 172812 | p->pHaving = 0; |
| 172813 | p->selFlags &= ~(u32)SF_Aggregate; |
| 172814 | p->selFlags |= SF_WinRewrite; |
| 172815 | |
| 172816 | /* Create the ORDER BY clause for the sub-select. This is the concatenation |
| 172817 | ** of the window PARTITION and ORDER BY clauses. Then, if this makes it |
| 172818 | ** redundant, remove the ORDER BY from the parent SELECT. */ |
| @@ -178280,12 +178947,12 @@ | |
| 178947 | pRhs = sqlite3SelectNew(pParse,0,pFrom,0,0,0,0,0,0); |
| 178948 | } |
| 178949 | if( pRhs ){ |
| 178950 | pRhs->op = (u8)yymsp[-1].minor.yy502; |
| 178951 | pRhs->pPrior = pLhs; |
| 178952 | if( ALWAYS(pLhs) ) pLhs->selFlags &= ~(u32)SF_MultiValue; |
| 178953 | pRhs->selFlags &= ~(u32)SF_MultiValue; |
| 178954 | if( yymsp[-1].minor.yy502!=TK_ALL ) pParse->hasCompound = 1; |
| 178955 | }else{ |
| 178956 | sqlite3SelectDelete(pParse->db, pLhs); |
| 178957 | } |
| 178958 | yymsp[-2].minor.yy637 = pRhs; |
| @@ -181025,11 +181692,15 @@ | |
| 181692 | tokenType = analyzeOverKeyword((const u8*)&zSql[4], lastTokenParsed); |
| 181693 | }else if( tokenType==TK_FILTER ){ |
| 181694 | assert( n==6 ); |
| 181695 | tokenType = analyzeFilterKeyword((const u8*)&zSql[6], lastTokenParsed); |
| 181696 | #endif /* SQLITE_OMIT_WINDOWFUNC */ |
| 181697 | }else if( tokenType==TK_COMMENT |
| 181698 | && (db->init.busy || (db->flags & SQLITE_Comments)!=0) |
| 181699 | ){ |
| 181700 | /* Ignore SQL comments if either (1) we are reparsing the schema or |
| 181701 | ** (2) SQLITE_DBCONFIG_ENABLE_COMMENTS is turned on (the default). */ |
| 181702 | zSql += n; |
| 181703 | continue; |
| 181704 | }else if( tokenType!=TK_QNUMBER ){ |
| 181705 | Token x; |
| 181706 | x.z = zSql; |
| @@ -181920,10 +182591,18 @@ | |
| 182591 | } |
| 182592 | #endif |
| 182593 | if( rc==SQLITE_OK ){ |
| 182594 | sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage, |
| 182595 | sqlite3GlobalConfig.szPage, sqlite3GlobalConfig.nPage); |
| 182596 | #ifdef SQLITE_EXTRA_INIT_MUTEXED |
| 182597 | { |
| 182598 | int SQLITE_EXTRA_INIT_MUTEXED(const char*); |
| 182599 | rc = SQLITE_EXTRA_INIT_MUTEXED(0); |
| 182600 | } |
| 182601 | #endif |
| 182602 | } |
| 182603 | if( rc==SQLITE_OK ){ |
| 182604 | sqlite3MemoryBarrier(); |
| 182605 | sqlite3GlobalConfig.isInit = 1; |
| 182606 | #ifdef SQLITE_EXTRA_INIT |
| 182607 | bRunExtraInit = 1; |
| 182608 | #endif |
| @@ -183396,10 +184075,13 @@ | |
| 184075 | sqlite3_mutex_enter(db->mutex); |
| 184076 | db->busyHandler.xBusyHandler = xBusy; |
| 184077 | db->busyHandler.pBusyArg = pArg; |
| 184078 | db->busyHandler.nBusy = 0; |
| 184079 | db->busyTimeout = 0; |
| 184080 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 184081 | db->setlkTimeout = 0; |
| 184082 | #endif |
| 184083 | sqlite3_mutex_leave(db->mutex); |
| 184084 | return SQLITE_OK; |
| 184085 | } |
| 184086 | |
| 184087 | #ifndef SQLITE_OMIT_PROGRESS_CALLBACK |
| @@ -183445,15 +184127,50 @@ | |
| 184127 | #endif |
| 184128 | if( ms>0 ){ |
| 184129 | sqlite3_busy_handler(db, (int(*)(void*,int))sqliteDefaultBusyCallback, |
| 184130 | (void*)db); |
| 184131 | db->busyTimeout = ms; |
| 184132 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 184133 | db->setlkTimeout = ms; |
| 184134 | #endif |
| 184135 | }else{ |
| 184136 | sqlite3_busy_handler(db, 0, 0); |
| 184137 | } |
| 184138 | return SQLITE_OK; |
| 184139 | } |
| 184140 | |
| 184141 | /* |
| 184142 | ** Set the setlk timeout value. |
| 184143 | */ |
| 184144 | SQLITE_API int sqlite3_setlk_timeout(sqlite3 *db, int ms, int flags){ |
| 184145 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 184146 | int iDb; |
| 184147 | int bBOC = ((flags & SQLITE_SETLK_BLOCK_ON_CONNECT) ? 1 : 0); |
| 184148 | #endif |
| 184149 | #ifdef SQLITE_ENABLE_API_ARMOR |
| 184150 | if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT; |
| 184151 | #endif |
| 184152 | if( ms<-1 ) return SQLITE_RANGE; |
| 184153 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 184154 | db->setlkTimeout = ms; |
| 184155 | db->setlkFlags = flags; |
| 184156 | sqlite3BtreeEnterAll(db); |
| 184157 | for(iDb=0; iDb<db->nDb; iDb++){ |
| 184158 | Btree *pBt = db->aDb[iDb].pBt; |
| 184159 | if( pBt ){ |
| 184160 | sqlite3_file *fd = sqlite3PagerFile(sqlite3BtreePager(pBt)); |
| 184161 | sqlite3OsFileControlHint(fd, SQLITE_FCNTL_BLOCK_ON_CONNECT, (void*)&bBOC); |
| 184162 | } |
| 184163 | } |
| 184164 | sqlite3BtreeLeaveAll(db); |
| 184165 | #endif |
| 184166 | #if !defined(SQLITE_ENABLE_API_ARMOR) && !defined(SQLITE_ENABLE_SETLK_TIMEOUT) |
| 184167 | UNUSED_PARAMETER(db); |
| 184168 | UNUSED_PARAMETER(flags); |
| 184169 | #endif |
| 184170 | return SQLITE_OK; |
| 184171 | } |
| 184172 | |
| 184173 | /* |
| 184174 | ** Cause any pending operation to stop at its earliest opportunity. |
| 184175 | */ |
| 184176 | SQLITE_API void sqlite3_interrupt(sqlite3 *db){ |
| @@ -185416,11 +186133,11 @@ | |
| 186133 | }else if( pData==0 ){ |
| 186134 | sqlite3_mutex_leave(db->mutex); |
| 186135 | return SQLITE_OK; |
| 186136 | }else{ |
| 186137 | size_t n = strlen(zName); |
| 186138 | p = sqlite3_malloc64( SZ_DBCLIENTDATA(n+1) ); |
| 186139 | if( p==0 ){ |
| 186140 | if( xDestructor ) xDestructor(pData); |
| 186141 | sqlite3_mutex_leave(db->mutex); |
| 186142 | return SQLITE_NOMEM; |
| 186143 | } |
| @@ -185782,12 +186499,12 @@ | |
| 186499 | #endif |
| 186500 | |
| 186501 | /* sqlite3_test_control(SQLITE_TESTCTRL_FK_NO_ACTION, sqlite3 *db, int b); |
| 186502 | ** |
| 186503 | ** If b is true, then activate the SQLITE_FkNoAction setting. If b is |
| 186504 | ** false then clear that setting. If the SQLITE_FkNoAction setting is |
| 186505 | ** enabled, all foreign key ON DELETE and ON UPDATE actions behave as if |
| 186506 | ** they were NO ACTION, regardless of how they are defined. |
| 186507 | ** |
| 186508 | ** NB: One must usually run "PRAGMA writable_schema=RESET" after |
| 186509 | ** using this test-control, before it will take full effect. failing |
| 186510 | ** to reset the schema can result in some unexpected behavior. |
| @@ -187130,11 +187847,11 @@ | |
| 187847 | ** } |
| 187848 | ** |
| 187849 | ** Here, array { X } means zero or more occurrences of X, adjacent in |
| 187850 | ** memory. A "position" is an index of a token in the token stream |
| 187851 | ** generated by the tokenizer. Note that POS_END and POS_COLUMN occur |
| 187852 | ** in the same logical place as the position element, and act as sentinels |
| 187853 | ** ending a position list array. POS_END is 0. POS_COLUMN is 1. |
| 187854 | ** The positions numbers are not stored literally but rather as two more |
| 187855 | ** than the difference from the prior position, or the just the position plus |
| 187856 | ** 2 for the first position. Example: |
| 187857 | ** |
| @@ -187817,10 +188534,23 @@ | |
| 188534 | |
| 188535 | #define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32)) |
| 188536 | #define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64) |
| 188537 | |
| 188538 | #define deliberate_fall_through |
| 188539 | |
| 188540 | /* |
| 188541 | ** Macros needed to provide flexible arrays in a portable way |
| 188542 | */ |
| 188543 | #ifndef offsetof |
| 188544 | # define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD)) |
| 188545 | #endif |
| 188546 | #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) |
| 188547 | # define FLEXARRAY |
| 188548 | #else |
| 188549 | # define FLEXARRAY 1 |
| 188550 | #endif |
| 188551 | |
| 188552 | |
| 188553 | #endif /* SQLITE_AMALGAMATION */ |
| 188554 | |
| 188555 | #ifdef SQLITE_DEBUG |
| 188556 | SQLITE_PRIVATE int sqlite3Fts3Corrupt(void); |
| @@ -187922,11 +188652,11 @@ | |
| 188652 | int inTransaction; /* True after xBegin but before xCommit/xRollback */ |
| 188653 | int mxSavepoint; /* Largest valid xSavepoint integer */ |
| 188654 | #endif |
| 188655 | |
| 188656 | #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST) |
| 188657 | /* True to disable the incremental doclist optimization. This is controlled |
| 188658 | ** by special insert command 'test-no-incr-doclist'. */ |
| 188659 | int bNoIncrDoclist; |
| 188660 | |
| 188661 | /* Number of segments in a level */ |
| 188662 | int nMergeCount; |
| @@ -187974,11 +188704,11 @@ | |
| 188704 | #define FTS3_EVAL_NEXT 1 |
| 188705 | #define FTS3_EVAL_MATCHINFO 2 |
| 188706 | |
| 188707 | /* |
| 188708 | ** The Fts3Cursor.eSearch member is always set to one of the following. |
| 188709 | ** Actually, Fts3Cursor.eSearch can be greater than or equal to |
| 188710 | ** FTS3_FULLTEXT_SEARCH. If so, then Fts3Cursor.eSearch - 2 is the index |
| 188711 | ** of the column to be searched. For example, in |
| 188712 | ** |
| 188713 | ** CREATE VIRTUAL TABLE ex1 USING fts3(a,b,c,d); |
| 188714 | ** SELECT docid FROM ex1 WHERE b MATCH 'one two three'; |
| @@ -188047,12 +188777,16 @@ | |
| 188777 | /* Variables below this point are populated by fts3_expr.c when parsing |
| 188778 | ** a MATCH expression. Everything above is part of the evaluation phase. |
| 188779 | */ |
| 188780 | int nToken; /* Number of tokens in the phrase */ |
| 188781 | int iColumn; /* Index of column this phrase must match */ |
| 188782 | Fts3PhraseToken aToken[FLEXARRAY]; /* One for each token in the phrase */ |
| 188783 | }; |
| 188784 | |
| 188785 | /* Size (in bytes) of an Fts3Phrase object large enough to hold N tokens */ |
| 188786 | #define SZ_FTS3PHRASE(N) \ |
| 188787 | (offsetof(Fts3Phrase,aToken)+(N)*sizeof(Fts3PhraseToken)) |
| 188788 | |
| 188789 | /* |
| 188790 | ** A tree of these objects forms the RHS of a MATCH operator. |
| 188791 | ** |
| 188792 | ** If Fts3Expr.eType is FTSQUERY_PHRASE and isLoaded is true, then aDoclist |
| @@ -190627,11 +191361,11 @@ | |
| 191361 | ** |
| 191362 | ** The space required to store the output is therefore the sum of the |
| 191363 | ** sizes of the two inputs, plus enough space for exactly one of the input |
| 191364 | ** docids to grow. |
| 191365 | ** |
| 191366 | ** A symmetric argument may be made if the doclists are in descending |
| 191367 | ** order. |
| 191368 | */ |
| 191369 | aOut = sqlite3_malloc64((i64)n1+n2+FTS3_VARINT_MAX-1+FTS3_BUFFER_PADDING); |
| 191370 | if( !aOut ) return SQLITE_NOMEM; |
| 191371 | |
| @@ -192725,11 +193459,11 @@ | |
| 193459 | ** |
| 193460 | ** * features at least one token that uses an incremental doclist, and |
| 193461 | ** |
| 193462 | ** * does not contain any deferred tokens. |
| 193463 | ** |
| 193464 | ** Advance it to the next matching document in the database and populate |
| 193465 | ** the Fts3Doclist.pList and nList fields. |
| 193466 | ** |
| 193467 | ** If there is no "next" entry and no error occurs, then *pbEof is set to |
| 193468 | ** 1 before returning. Otherwise, if no error occurs and the iterator is |
| 193469 | ** successfully advanced, *pbEof is set to 0. |
| @@ -193732,11 +194466,11 @@ | |
| 194466 | |
| 194467 | return rc; |
| 194468 | } |
| 194469 | |
| 194470 | /* |
| 194471 | ** Restart iteration for expression pExpr so that the next call to |
| 194472 | ** fts3EvalNext() visits the first row. Do not allow incremental |
| 194473 | ** loading or merging of phrase doclists for this iteration. |
| 194474 | ** |
| 194475 | ** If *pRc is other than SQLITE_OK when this function is called, it is |
| 194476 | ** a no-op. If an error occurs within this function, *pRc is set to an |
| @@ -194923,10 +195657,27 @@ | |
| 195657 | /* |
| 195658 | ** Function getNextNode(), which is called by fts3ExprParse(), may itself |
| 195659 | ** call fts3ExprParse(). So this forward declaration is required. |
| 195660 | */ |
| 195661 | static int fts3ExprParse(ParseContext *, const char *, int, Fts3Expr **, int *); |
| 195662 | |
| 195663 | /* |
| 195664 | ** Search buffer z[], size n, for a '"' character. Or, if enable_parenthesis |
| 195665 | ** is defined, search for '(' and ')' as well. Return the index of the first |
| 195666 | ** such character in the buffer. If there is no such character, return -1. |
| 195667 | */ |
| 195668 | static int findBarredChar(const char *z, int n){ |
| 195669 | int ii; |
| 195670 | for(ii=0; ii<n; ii++){ |
| 195671 | if( (z[ii]=='"') |
| 195672 | || (sqlite3_fts3_enable_parentheses && (z[ii]=='(' || z[ii]==')')) |
| 195673 | ){ |
| 195674 | return ii; |
| 195675 | } |
| 195676 | } |
| 195677 | return -1; |
| 195678 | } |
| 195679 | |
| 195680 | /* |
| 195681 | ** Extract the next token from buffer z (length n) using the tokenizer |
| 195682 | ** and other information (column names etc.) in pParse. Create an Fts3Expr |
| 195683 | ** structure of type FTSQUERY_PHRASE containing a phrase consisting of this |
| @@ -194948,38 +195699,42 @@ | |
| 195699 | sqlite3_tokenizer *pTokenizer = pParse->pTokenizer; |
| 195700 | sqlite3_tokenizer_module const *pModule = pTokenizer->pModule; |
| 195701 | int rc; |
| 195702 | sqlite3_tokenizer_cursor *pCursor; |
| 195703 | Fts3Expr *pRet = 0; |
| 195704 | |
| 195705 | *pnConsumed = n; |
| 195706 | rc = sqlite3Fts3OpenTokenizer(pTokenizer, pParse->iLangid, z, n, &pCursor); |
| 195707 | if( rc==SQLITE_OK ){ |
| 195708 | const char *zToken; |
| 195709 | int nToken = 0, iStart = 0, iEnd = 0, iPosition = 0; |
| 195710 | sqlite3_int64 nByte; /* total space to allocate */ |
| 195711 | |
| 195712 | rc = pModule->xNext(pCursor, &zToken, &nToken, &iStart, &iEnd, &iPosition); |
| 195713 | if( rc==SQLITE_OK ){ |
| 195714 | /* Check that this tokenization did not gobble up any " characters. Or, |
| 195715 | ** if enable_parenthesis is true, that it did not gobble up any |
| 195716 | ** open or close parenthesis characters either. If it did, call |
| 195717 | ** getNextToken() again, but pass only that part of the input buffer |
| 195718 | ** up to the first such character. */ |
| 195719 | int iBarred = findBarredChar(z, iEnd); |
| 195720 | if( iBarred>=0 ){ |
| 195721 | pModule->xClose(pCursor); |
| 195722 | return getNextToken(pParse, iCol, z, iBarred, ppExpr, pnConsumed); |
| 195723 | } |
| 195724 | |
| 195725 | nByte = sizeof(Fts3Expr) + SZ_FTS3PHRASE(1) + nToken; |
| 195726 | pRet = (Fts3Expr *)sqlite3Fts3MallocZero(nByte); |
| 195727 | if( !pRet ){ |
| 195728 | rc = SQLITE_NOMEM; |
| 195729 | }else{ |
| 195730 | pRet->eType = FTSQUERY_PHRASE; |
| 195731 | pRet->pPhrase = (Fts3Phrase *)&pRet[1]; |
| 195732 | pRet->pPhrase->nToken = 1; |
| 195733 | pRet->pPhrase->iColumn = iCol; |
| 195734 | pRet->pPhrase->aToken[0].n = nToken; |
| 195735 | pRet->pPhrase->aToken[0].z = (char*)&pRet->pPhrase->aToken[1]; |
| 195736 | memcpy(pRet->pPhrase->aToken[0].z, zToken, nToken); |
| 195737 | |
| 195738 | if( iEnd<n && z[iEnd]=='*' ){ |
| 195739 | pRet->pPhrase->aToken[0].isPrefix = 1; |
| 195740 | iEnd++; |
| @@ -194999,11 +195754,15 @@ | |
| 195754 | } |
| 195755 | } |
| 195756 | |
| 195757 | } |
| 195758 | *pnConsumed = iEnd; |
| 195759 | }else if( n && rc==SQLITE_DONE ){ |
| 195760 | int iBarred = findBarredChar(z, n); |
| 195761 | if( iBarred>=0 ){ |
| 195762 | *pnConsumed = iBarred; |
| 195763 | } |
| 195764 | rc = SQLITE_OK; |
| 195765 | } |
| 195766 | |
| 195767 | pModule->xClose(pCursor); |
| 195768 | } |
| @@ -195048,11 +195807,11 @@ | |
| 195807 | Fts3Expr *p = 0; |
| 195808 | sqlite3_tokenizer_cursor *pCursor = 0; |
| 195809 | char *zTemp = 0; |
| 195810 | i64 nTemp = 0; |
| 195811 | |
| 195812 | const int nSpace = sizeof(Fts3Expr) + SZ_FTS3PHRASE(1); |
| 195813 | int nToken = 0; |
| 195814 | |
| 195815 | /* The final Fts3Expr data structure, including the Fts3Phrase, |
| 195816 | ** Fts3PhraseToken structures token buffers are all stored as a single |
| 195817 | ** allocation so that the expression can be freed with a single call to |
| @@ -195420,11 +196179,11 @@ | |
| 196179 | int eType = p->eType; |
| 196180 | isPhrase = (eType==FTSQUERY_PHRASE || p->pLeft); |
| 196181 | |
| 196182 | /* The isRequirePhrase variable is set to true if a phrase or |
| 196183 | ** an expression contained in parenthesis is required. If a |
| 196184 | ** binary operator (AND, OR, NOT or NEAR) is encountered when |
| 196185 | ** isRequirePhrase is set, this is a syntax error. |
| 196186 | */ |
| 196187 | if( !isPhrase && isRequirePhrase ){ |
| 196188 | sqlite3Fts3ExprFree(p); |
| 196189 | rc = SQLITE_ERROR; |
| @@ -196002,11 +196761,10 @@ | |
| 196761 | pTokenizer, 0, azCol, 0, nCol, nCol, zExpr, nExpr, &pExpr |
| 196762 | ); |
| 196763 | } |
| 196764 | |
| 196765 | if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM ){ |
| 196766 | sqlite3_result_error(context, "Error parsing expression", -1); |
| 196767 | }else if( rc==SQLITE_NOMEM || !(zBuf = exprToString(pExpr, 0)) ){ |
| 196768 | sqlite3_result_error_nomem(context); |
| 196769 | }else{ |
| 196770 | sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); |
| @@ -196245,11 +197003,11 @@ | |
| 197003 | pEntry->count++; |
| 197004 | pEntry->chain = pNew; |
| 197005 | } |
| 197006 | |
| 197007 | |
| 197008 | /* Resize the hash table so that it contains "new_size" buckets. |
| 197009 | ** "new_size" must be a power of 2. The hash table might fail |
| 197010 | ** to resize if sqliteMalloc() fails. |
| 197011 | ** |
| 197012 | ** Return non-zero if a memory allocation error occurs. |
| 197013 | */ |
| @@ -196700,11 +197458,11 @@ | |
| 197458 | isConsonant(z+2); |
| 197459 | } |
| 197460 | |
| 197461 | /* |
| 197462 | ** If the word ends with zFrom and xCond() is true for the stem |
| 197463 | ** of the word that precedes the zFrom ending, then change the |
| 197464 | ** ending to zTo. |
| 197465 | ** |
| 197466 | ** The input word *pz and zFrom are both in reverse order. zTo |
| 197467 | ** is in normal order. |
| 197468 | ** |
| @@ -202283,11 +203041,11 @@ | |
| 203041 | ** previous term. Before this function returns, it is updated to contain a |
| 203042 | ** copy of zTerm/nTerm. |
| 203043 | ** |
| 203044 | ** It is assumed that the buffer associated with pNode is already large |
| 203045 | ** enough to accommodate the new entry. The buffer associated with pPrev |
| 203046 | ** is extended by this function if required. |
| 203047 | ** |
| 203048 | ** If an error (i.e. OOM condition) occurs, an SQLite error code is |
| 203049 | ** returned. Otherwise, SQLITE_OK. |
| 203050 | */ |
| 203051 | static int fts3AppendToNode( |
| @@ -203946,11 +204704,11 @@ | |
| 204704 | #endif |
| 204705 | |
| 204706 | /* |
| 204707 | ** SQLite value pRowid contains the rowid of a row that may or may not be |
| 204708 | ** present in the FTS3 table. If it is, delete it and adjust the contents |
| 204709 | ** of subsidiary data structures accordingly. |
| 204710 | */ |
| 204711 | static int fts3DeleteByRowid( |
| 204712 | Fts3Table *p, |
| 204713 | sqlite3_value *pRowid, |
| 204714 | int *pnChng, /* IN/OUT: Decrement if row is deleted */ |
| @@ -204272,12 +205030,16 @@ | |
| 205030 | struct MatchinfoBuffer { |
| 205031 | u8 aRef[3]; |
| 205032 | int nElem; |
| 205033 | int bGlobal; /* Set if global data is loaded */ |
| 205034 | char *zMatchinfo; |
| 205035 | u32 aMI[FLEXARRAY]; |
| 205036 | }; |
| 205037 | |
| 205038 | /* Size (in bytes) of a MatchinfoBuffer sufficient for N elements */ |
| 205039 | #define SZ_MATCHINFOBUFFER(N) \ |
| 205040 | (offsetof(MatchinfoBuffer,aMI)+(((N)+1)/2)*sizeof(u64)) |
| 205041 | |
| 205042 | |
| 205043 | /* |
| 205044 | ** The snippet() and offsets() functions both return text values. An instance |
| 205045 | ** of the following structure is used to accumulate those values while the |
| @@ -204299,17 +205061,17 @@ | |
| 205061 | ** Allocate a two-slot MatchinfoBuffer object. |
| 205062 | */ |
| 205063 | static MatchinfoBuffer *fts3MIBufferNew(size_t nElem, const char *zMatchinfo){ |
| 205064 | MatchinfoBuffer *pRet; |
| 205065 | sqlite3_int64 nByte = sizeof(u32) * (2*(sqlite3_int64)nElem + 1) |
| 205066 | + SZ_MATCHINFOBUFFER(1); |
| 205067 | sqlite3_int64 nStr = strlen(zMatchinfo); |
| 205068 | |
| 205069 | pRet = sqlite3Fts3MallocZero(nByte + nStr+1); |
| 205070 | if( pRet ){ |
| 205071 | pRet->aMI[0] = (u8*)(&pRet->aMI[1]) - (u8*)pRet; |
| 205072 | pRet->aMI[1+nElem] = pRet->aMI[0] |
| 205073 | + sizeof(u32)*((int)nElem+1); |
| 205074 | pRet->nElem = (int)nElem; |
| 205075 | pRet->zMatchinfo = ((char*)pRet) + nByte; |
| 205076 | memcpy(pRet->zMatchinfo, zMatchinfo, nStr+1); |
| 205077 | pRet->aRef[0] = 1; |
| @@ -204319,14 +205081,14 @@ | |
| 205081 | } |
| 205082 | |
| 205083 | static void fts3MIBufferFree(void *p){ |
| 205084 | MatchinfoBuffer *pBuf = (MatchinfoBuffer*)((u8*)p - ((u32*)p)[-1]); |
| 205085 | |
| 205086 | assert( (u32*)p==&pBuf->aMI[1] |
| 205087 | || (u32*)p==&pBuf->aMI[pBuf->nElem+2] |
| 205088 | ); |
| 205089 | if( (u32*)p==&pBuf->aMI[1] ){ |
| 205090 | pBuf->aRef[1] = 0; |
| 205091 | }else{ |
| 205092 | pBuf->aRef[2] = 0; |
| 205093 | } |
| 205094 | |
| @@ -204339,32 +205101,32 @@ | |
| 205101 | void (*xRet)(void*) = 0; |
| 205102 | u32 *aOut = 0; |
| 205103 | |
| 205104 | if( p->aRef[1]==0 ){ |
| 205105 | p->aRef[1] = 1; |
| 205106 | aOut = &p->aMI[1]; |
| 205107 | xRet = fts3MIBufferFree; |
| 205108 | } |
| 205109 | else if( p->aRef[2]==0 ){ |
| 205110 | p->aRef[2] = 1; |
| 205111 | aOut = &p->aMI[p->nElem+2]; |
| 205112 | xRet = fts3MIBufferFree; |
| 205113 | }else{ |
| 205114 | aOut = (u32*)sqlite3_malloc64(p->nElem * sizeof(u32)); |
| 205115 | if( aOut ){ |
| 205116 | xRet = sqlite3_free; |
| 205117 | if( p->bGlobal ) memcpy(aOut, &p->aMI[1], p->nElem*sizeof(u32)); |
| 205118 | } |
| 205119 | } |
| 205120 | |
| 205121 | *paOut = aOut; |
| 205122 | return xRet; |
| 205123 | } |
| 205124 | |
| 205125 | static void fts3MIBufferSetGlobal(MatchinfoBuffer *p){ |
| 205126 | p->bGlobal = 1; |
| 205127 | memcpy(&p->aMI[2+p->nElem], &p->aMI[1], p->nElem*sizeof(u32)); |
| 205128 | } |
| 205129 | |
| 205130 | /* |
| 205131 | ** Free a MatchinfoBuffer object allocated using fts3MIBufferNew() |
| 205132 | */ |
| @@ -204775,11 +205537,11 @@ | |
| 205537 | if( nAppend<0 ){ |
| 205538 | nAppend = (int)strlen(zAppend); |
| 205539 | } |
| 205540 | |
| 205541 | /* If there is insufficient space allocated at StrBuffer.z, use realloc() |
| 205542 | ** to grow the buffer until so that it is big enough to accommodate the |
| 205543 | ** appended data. |
| 205544 | */ |
| 205545 | if( pStr->n+nAppend+1>=pStr->nAlloc ){ |
| 205546 | sqlite3_int64 nAlloc = pStr->nAlloc+(sqlite3_int64)nAppend+100; |
| 205547 | char *zNew = sqlite3_realloc64(pStr->z, nAlloc); |
| @@ -207150,11 +207912,11 @@ | |
| 207912 | ** |
| 207913 | ** When a match if found, the matching entry is moved to become the |
| 207914 | ** most-recently used entry if it isn't so already. |
| 207915 | ** |
| 207916 | ** The JsonParse object returned still belongs to the Cache and might |
| 207917 | ** be deleted at any moment. If the caller wants the JsonParse to |
| 207918 | ** linger, it needs to increment the nPJRef reference counter. |
| 207919 | */ |
| 207920 | static JsonParse *jsonCacheSearch( |
| 207921 | sqlite3_context *ctx, /* The SQL statement context holding the cache */ |
| 207922 | sqlite3_value *pArg /* Function argument containing SQL text */ |
| @@ -210195,11 +210957,11 @@ | |
| 210957 | ** |
| 210958 | ** This goes against all historical documentation about how the SQLite |
| 210959 | ** JSON functions were suppose to work. From the beginning, blob was |
| 210960 | ** reserved for expansion and a blob value should have raised an error. |
| 210961 | ** But it did not, due to a bug. And many applications came to depend |
| 210962 | ** upon this buggy behavior, especially when using the CLI and reading |
| 210963 | ** JSON text using readfile(), which returns a blob. For this reason |
| 210964 | ** we will continue to support the bug moving forward. |
| 210965 | ** See for example https://sqlite.org/forum/forumpost/012136abd5292b8d |
| 210966 | */ |
| 210967 | } |
| @@ -212295,10 +213057,18 @@ | |
| 213057 | # define NEVER(X) ((X)?(assert(0),1):0) |
| 213058 | #else |
| 213059 | # define ALWAYS(X) (X) |
| 213060 | # define NEVER(X) (X) |
| 213061 | #endif |
| 213062 | #ifndef offsetof |
| 213063 | #define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD)) |
| 213064 | #endif |
| 213065 | #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) |
| 213066 | # define FLEXARRAY |
| 213067 | #else |
| 213068 | # define FLEXARRAY 1 |
| 213069 | #endif |
| 213070 | #endif /* !defined(SQLITE_AMALGAMATION) */ |
| 213071 | |
| 213072 | /* Macro to check for 4-byte alignment. Only used inside of assert() */ |
| 213073 | #ifdef SQLITE_DEBUG |
| 213074 | # define FOUR_BYTE_ALIGNED(X) ((((char*)(X) - (char*)0) & 3)==0) |
| @@ -212615,13 +213385,17 @@ | |
| 213385 | struct RtreeMatchArg { |
| 213386 | u32 iSize; /* Size of this object */ |
| 213387 | RtreeGeomCallback cb; /* Info about the callback functions */ |
| 213388 | int nParam; /* Number of parameters to the SQL function */ |
| 213389 | sqlite3_value **apSqlParam; /* Original SQL parameter values */ |
| 213390 | RtreeDValue aParam[FLEXARRAY]; /* Values for parameters to the SQL function */ |
| 213391 | }; |
| 213392 | |
| 213393 | /* Size of an RtreeMatchArg object with N parameters */ |
| 213394 | #define SZ_RTREEMATCHARG(N) \ |
| 213395 | (offsetof(RtreeMatchArg,aParam)+(N)*sizeof(RtreeDValue)) |
| 213396 | |
| 213397 | #ifndef MAX |
| 213398 | # define MAX(x,y) ((x) < (y) ? (y) : (x)) |
| 213399 | #endif |
| 213400 | #ifndef MIN |
| 213401 | # define MIN(x,y) ((x) > (y) ? (y) : (x)) |
| @@ -214306,11 +215080,11 @@ | |
| 215080 | |
| 215081 | return rc; |
| 215082 | } |
| 215083 | |
| 215084 | /* |
| 215085 | ** Return the N-dimensional volume of the cell stored in *p. |
| 215086 | */ |
| 215087 | static RtreeDValue cellArea(Rtree *pRtree, RtreeCell *p){ |
| 215088 | RtreeDValue area = (RtreeDValue)1; |
| 215089 | assert( pRtree->nDim>=1 && pRtree->nDim<=5 ); |
| 215090 | #ifndef SQLITE_RTREE_INT_ONLY |
| @@ -216072,11 +216846,11 @@ | |
| 216846 | } |
| 216847 | |
| 216848 | /* |
| 216849 | ** The second and subsequent arguments to this function are a printf() |
| 216850 | ** style format string and arguments. This function formats the string and |
| 216851 | ** appends it to the report being accumulated in pCheck. |
| 216852 | */ |
| 216853 | static void rtreeCheckAppendMsg(RtreeCheck *pCheck, const char *zFmt, ...){ |
| 216854 | va_list ap; |
| 216855 | va_start(ap, zFmt); |
| 216856 | if( pCheck->rc==SQLITE_OK && pCheck->nErr<RTREE_CHECK_MAX_ERROR ){ |
| @@ -217260,11 +218034,11 @@ | |
| 218034 | |
| 218035 | /* |
| 218036 | ** Determine if point (x0,y0) is beneath line segment (x1,y1)->(x2,y2). |
| 218037 | ** Returns: |
| 218038 | ** |
| 218039 | ** +2 x0,y0 is on the line segment |
| 218040 | ** |
| 218041 | ** +1 x0,y0 is beneath line segment |
| 218042 | ** |
| 218043 | ** 0 x0,y0 is not on or beneath the line segment or the line segment |
| 218044 | ** is vertical and x0,y0 is not on the line segment |
| @@ -217366,11 +218140,11 @@ | |
| 218140 | } |
| 218141 | sqlite3_free(p1); |
| 218142 | sqlite3_free(p2); |
| 218143 | } |
| 218144 | |
| 218145 | /* Objects used by the overlap algorithm. */ |
| 218146 | typedef struct GeoEvent GeoEvent; |
| 218147 | typedef struct GeoSegment GeoSegment; |
| 218148 | typedef struct GeoOverlap GeoOverlap; |
| 218149 | struct GeoEvent { |
| 218150 | double x; /* X coordinate at which event occurs */ |
| @@ -218413,12 +219187,11 @@ | |
| 219187 | RtreeGeomCallback *pGeomCtx = (RtreeGeomCallback *)sqlite3_user_data(ctx); |
| 219188 | RtreeMatchArg *pBlob; |
| 219189 | sqlite3_int64 nBlob; |
| 219190 | int memErr = 0; |
| 219191 | |
| 219192 | nBlob = SZ_RTREEMATCHARG(nArg) + nArg*sizeof(sqlite3_value*); |
| 219193 | pBlob = (RtreeMatchArg *)sqlite3_malloc64(nBlob); |
| 219194 | if( !pBlob ){ |
| 219195 | sqlite3_result_error_nomem(ctx); |
| 219196 | }else{ |
| 219197 | int i; |
| @@ -219509,11 +220282,11 @@ | |
| 220282 | ** to read from the original database snapshot. In other words, partially |
| 220283 | ** applied transactions are not visible to other clients. |
| 220284 | ** |
| 220285 | ** "RBU" stands for "Resumable Bulk Update". As in a large database update |
| 220286 | ** transmitted via a wireless network to a mobile device. A transaction |
| 220287 | ** applied using this extension is hence referred to as an "RBU update". |
| 220288 | ** |
| 220289 | ** |
| 220290 | ** LIMITATIONS |
| 220291 | ** |
| 220292 | ** An "RBU update" transaction is subject to the following limitations: |
| @@ -219806,11 +220579,11 @@ | |
| 220579 | ** sqlite3rbu_close() returns any value other than SQLITE_OK, the contents |
| 220580 | ** of the state tables within the state database are zeroed. This way, |
| 220581 | ** the next call to sqlite3rbu_vacuum() opens a handle that starts a |
| 220582 | ** new RBU vacuum operation. |
| 220583 | ** |
| 220584 | ** As with sqlite3rbu_open(), Zipvfs users should refer to the comment |
| 220585 | ** describing the sqlite3rbu_create_vfs() API function below for |
| 220586 | ** a description of the complications associated with using RBU with |
| 220587 | ** zipvfs databases. |
| 220588 | */ |
| 220589 | SQLITE_API sqlite3rbu *sqlite3rbu_vacuum( |
| @@ -219902,11 +220675,11 @@ | |
| 220675 | /* |
| 220676 | ** Close an RBU handle. |
| 220677 | ** |
| 220678 | ** If the RBU update has been completely applied, mark the RBU database |
| 220679 | ** as fully applied. Otherwise, assuming no error has occurred, save the |
| 220680 | ** current state of the RBU update application to the RBU database. |
| 220681 | ** |
| 220682 | ** If an error has already occurred as part of an sqlite3rbu_step() |
| 220683 | ** or sqlite3rbu_open() call, or if one occurs within this function, an |
| 220684 | ** SQLite error code is returned. Additionally, if pzErrmsg is not NULL, |
| 220685 | ** *pzErrmsg may be set to point to a buffer containing a utf-8 formatted |
| @@ -224828,11 +225601,11 @@ | |
| 225601 | int rc; |
| 225602 | rc = p->pReal->pMethods->xFileSize(p->pReal, pSize); |
| 225603 | |
| 225604 | /* If this is an RBU vacuum operation and this is the target database, |
| 225605 | ** pretend that it has at least one page. Otherwise, SQLite will not |
| 225606 | ** check for the existence of a *-wal file. rbuVfsRead() contains |
| 225607 | ** similar logic. */ |
| 225608 | if( rc==SQLITE_OK && *pSize==0 |
| 225609 | && p->pRbu && rbuIsVacuum(p->pRbu) |
| 225610 | && (p->openFlags & SQLITE_OPEN_MAIN_DB) |
| 225611 | ){ |
| @@ -228058,11 +228831,11 @@ | |
| 228831 | } |
| 228832 | |
| 228833 | /* |
| 228834 | ** This function is called to initialize the SessionTable.nCol, azCol[] |
| 228835 | ** abPK[] and azDflt[] members of SessionTable object pTab. If these |
| 228836 | ** fields are already initialized, this function is a no-op. |
| 228837 | ** |
| 228838 | ** If an error occurs, an error code is stored in sqlite3_session.rc and |
| 228839 | ** non-zero returned. Or, if no error occurs but the table has no primary |
| 228840 | ** key, sqlite3_session.rc is left set to SQLITE_OK and non-zero returned to |
| 228841 | ** indicate that updates on this table should be ignored. SessionTable.abPK |
| @@ -229881,11 +230654,11 @@ | |
| 230654 | int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */ |
| 230655 | void **ppChangeset /* OUT: Buffer containing changeset */ |
| 230656 | ){ |
| 230657 | sqlite3 *db = pSession->db; /* Source database handle */ |
| 230658 | SessionTable *pTab; /* Used to iterate through attached tables */ |
| 230659 | SessionBuffer buf = {0,0,0}; /* Buffer in which to accumulate changeset */ |
| 230660 | int rc; /* Return code */ |
| 230661 | |
| 230662 | assert( xOutput==0 || (pnChangeset==0 && ppChangeset==0) ); |
| 230663 | assert( xOutput!=0 || (pnChangeset!=0 && ppChangeset!=0) ); |
| 230664 | |
| @@ -234315,10 +235088,22 @@ | |
| 235088 | # define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&3)==0) |
| 235089 | #else |
| 235090 | # define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&7)==0) |
| 235091 | #endif |
| 235092 | |
| 235093 | /* |
| 235094 | ** Macros needed to provide flexible arrays in a portable way |
| 235095 | */ |
| 235096 | #ifndef offsetof |
| 235097 | # define offsetof(STRUCTURE,FIELD) ((size_t)((char*)&((STRUCTURE*)0)->FIELD)) |
| 235098 | #endif |
| 235099 | #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) |
| 235100 | # define FLEXARRAY |
| 235101 | #else |
| 235102 | # define FLEXARRAY 1 |
| 235103 | #endif |
| 235104 | |
| 235105 | #endif |
| 235106 | |
| 235107 | /* Truncate very long tokens to this many bytes. Hard limit is |
| 235108 | ** (65536-1-1-4-9)==65521 bytes. The limiting factor is the 16-bit offset |
| 235109 | ** field that occurs at the start of each leaf page (see fts5_index.c). */ |
| @@ -234387,14 +235172,15 @@ | |
| 235172 | ** |
| 235173 | ** This object is used by fts5_expr.c and fts5_index.c. |
| 235174 | */ |
| 235175 | struct Fts5Colset { |
| 235176 | int nCol; |
| 235177 | int aiCol[FLEXARRAY]; |
| 235178 | }; |
| 235179 | |
| 235180 | /* Size (int bytes) of a complete Fts5Colset object with N columns. */ |
| 235181 | #define SZ_FTS5COLSET(N) (sizeof(i64)*((N+2)/2)) |
| 235182 | |
| 235183 | /************************************************************************** |
| 235184 | ** Interface to code in fts5_config.c. fts5_config.c contains contains code |
| 235185 | ** to parse the arguments passed to the CREATE VIRTUAL TABLE statement. |
| 235186 | */ |
| @@ -235219,11 +236005,11 @@ | |
| 236005 | ************************************************************************* |
| 236006 | ** Driver template for the LEMON parser generator. |
| 236007 | ** |
| 236008 | ** The "lemon" program processes an LALR(1) input grammar file, then uses |
| 236009 | ** this template to construct a parser. The "lemon" program inserts text |
| 236010 | ** at each "%%" line. Also, any "P-a-r-s-e" identifier prefix (without the |
| 236011 | ** interstitial "-" characters) contained in this template is changed into |
| 236012 | ** the value of the %name directive from the grammar. Otherwise, the content |
| 236013 | ** of this template is copied straight through into the generate parser |
| 236014 | ** source file. |
| 236015 | ** |
| @@ -237373,11 +238159,11 @@ | |
| 238159 | ** where "N" is the total number of documents in the set and nHit |
| 238160 | ** is the number that contain at least one instance of the phrase |
| 238161 | ** under consideration. |
| 238162 | ** |
| 238163 | ** The problem with this is that if (N < 2*nHit), the IDF is |
| 238164 | ** negative. Which is undesirable. So the minimum allowable IDF is |
| 238165 | ** (1e-6) - roughly the same as a term that appears in just over |
| 238166 | ** half of set of 5,000,000 documents. */ |
| 238167 | double idf = log( (nRow - nHit + 0.5) / (nHit + 0.5) ); |
| 238168 | if( idf<=0.0 ) idf = 1e-6; |
| 238169 | p->aIDF[i] = idf; |
| @@ -237836,11 +238622,11 @@ | |
| 238622 | ** |
| 238623 | ** * All non-ASCII characters, |
| 238624 | ** * The 52 upper and lower case ASCII characters, and |
| 238625 | ** * The 10 integer ASCII characters. |
| 238626 | ** * The underscore character "_" (0x5F). |
| 238627 | ** * The unicode "substitute" character (0x1A). |
| 238628 | */ |
| 238629 | static int sqlite3Fts5IsBareword(char t){ |
| 238630 | u8 aBareword[128] = { |
| 238631 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 .. 0x0F */ |
| 238632 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* 0x10 .. 0x1F */ |
| @@ -239154,12 +239940,16 @@ | |
| 239940 | Fts5ExprNearset *pNear; /* For FTS5_STRING - cluster of phrases */ |
| 239941 | |
| 239942 | /* Child nodes. For a NOT node, this array always contains 2 entries. For |
| 239943 | ** AND or OR nodes, it contains 2 or more entries. */ |
| 239944 | int nChild; /* Number of child nodes */ |
| 239945 | Fts5ExprNode *apChild[FLEXARRAY]; /* Array of child nodes */ |
| 239946 | }; |
| 239947 | |
| 239948 | /* Size (in bytes) of an Fts5ExprNode object that holds up to N children */ |
| 239949 | #define SZ_FTS5EXPRNODE(N) \ |
| 239950 | (offsetof(Fts5ExprNode,apChild) + (N)*sizeof(Fts5ExprNode*)) |
| 239951 | |
| 239952 | #define Fts5NodeIsString(p) ((p)->eType==FTS5_TERM || (p)->eType==FTS5_STRING) |
| 239953 | |
| 239954 | /* |
| 239955 | ** Invoke the xNext method of an Fts5ExprNode object. This macro should be |
| @@ -239187,24 +239977,31 @@ | |
| 239977 | */ |
| 239978 | struct Fts5ExprPhrase { |
| 239979 | Fts5ExprNode *pNode; /* FTS5_STRING node this phrase is part of */ |
| 239980 | Fts5Buffer poslist; /* Current position list */ |
| 239981 | int nTerm; /* Number of entries in aTerm[] */ |
| 239982 | Fts5ExprTerm aTerm[FLEXARRAY]; /* Terms that make up this phrase */ |
| 239983 | }; |
| 239984 | |
| 239985 | /* Size (in bytes) of an Fts5ExprPhrase object that holds up to N terms */ |
| 239986 | #define SZ_FTS5EXPRPHRASE(N) \ |
| 239987 | (offsetof(Fts5ExprPhrase,aTerm) + (N)*sizeof(Fts5ExprTerm)) |
| 239988 | |
| 239989 | /* |
| 239990 | ** One or more phrases that must appear within a certain token distance of |
| 239991 | ** each other within each matching document. |
| 239992 | */ |
| 239993 | struct Fts5ExprNearset { |
| 239994 | int nNear; /* NEAR parameter */ |
| 239995 | Fts5Colset *pColset; /* Columns to search (NULL -> all columns) */ |
| 239996 | int nPhrase; /* Number of entries in aPhrase[] array */ |
| 239997 | Fts5ExprPhrase *apPhrase[FLEXARRAY]; /* Array of phrase pointers */ |
| 239998 | }; |
| 239999 | |
| 240000 | /* Size (in bytes) of an Fts5ExprNearset object covering up to N phrases */ |
| 240001 | #define SZ_FTS5EXPRNEARSET(N) \ |
| 240002 | (offsetof(Fts5ExprNearset,apPhrase)+(N)*sizeof(Fts5ExprPhrase*)) |
| 240003 | |
| 240004 | /* |
| 240005 | ** Parse context. |
| 240006 | */ |
| 240007 | struct Fts5Parse { |
| @@ -239360,11 +240157,11 @@ | |
| 240157 | assert_expr_depth_ok(sParse.rc, sParse.pExpr); |
| 240158 | |
| 240159 | /* If the LHS of the MATCH expression was a user column, apply the |
| 240160 | ** implicit column-filter. */ |
| 240161 | if( sParse.rc==SQLITE_OK && iCol<pConfig->nCol ){ |
| 240162 | int n = SZ_FTS5COLSET(1); |
| 240163 | Fts5Colset *pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&sParse.rc, n); |
| 240164 | if( pColset ){ |
| 240165 | pColset->nCol = 1; |
| 240166 | pColset->aiCol[0] = iCol; |
| 240167 | sqlite3Fts5ParseSetColset(&sParse, sParse.pExpr, pColset); |
| @@ -240718,11 +241515,11 @@ | |
| 241515 | Fts5ExprNearset *pRet = 0; |
| 241516 | |
| 241517 | if( pParse->rc==SQLITE_OK ){ |
| 241518 | if( pNear==0 ){ |
| 241519 | sqlite3_int64 nByte; |
| 241520 | nByte = SZ_FTS5EXPRNEARSET(SZALLOC+1); |
| 241521 | pRet = sqlite3_malloc64(nByte); |
| 241522 | if( pRet==0 ){ |
| 241523 | pParse->rc = SQLITE_NOMEM; |
| 241524 | }else{ |
| 241525 | memset(pRet, 0, (size_t)nByte); |
| @@ -240729,11 +241526,11 @@ | |
| 241526 | } |
| 241527 | }else if( (pNear->nPhrase % SZALLOC)==0 ){ |
| 241528 | int nNew = pNear->nPhrase + SZALLOC; |
| 241529 | sqlite3_int64 nByte; |
| 241530 | |
| 241531 | nByte = SZ_FTS5EXPRNEARSET(nNew+1); |
| 241532 | pRet = (Fts5ExprNearset*)sqlite3_realloc64(pNear, nByte); |
| 241533 | if( pRet==0 ){ |
| 241534 | pParse->rc = SQLITE_NOMEM; |
| 241535 | } |
| 241536 | }else{ |
| @@ -240820,16 +241617,16 @@ | |
| 241617 | if( pPhrase==0 || (pPhrase->nTerm % SZALLOC)==0 ){ |
| 241618 | Fts5ExprPhrase *pNew; |
| 241619 | int nNew = SZALLOC + (pPhrase ? pPhrase->nTerm : 0); |
| 241620 | |
| 241621 | pNew = (Fts5ExprPhrase*)sqlite3_realloc64(pPhrase, |
| 241622 | SZ_FTS5EXPRPHRASE(nNew+1) |
| 241623 | ); |
| 241624 | if( pNew==0 ){ |
| 241625 | rc = SQLITE_NOMEM; |
| 241626 | }else{ |
| 241627 | if( pPhrase==0 ) memset(pNew, 0, SZ_FTS5EXPRPHRASE(1)); |
| 241628 | pCtx->pPhrase = pPhrase = pNew; |
| 241629 | pNew->nTerm = nNew - SZALLOC; |
| 241630 | } |
| 241631 | } |
| 241632 | |
| @@ -240933,11 +241730,11 @@ | |
| 241730 | } |
| 241731 | |
| 241732 | if( sCtx.pPhrase==0 ){ |
| 241733 | /* This happens when parsing a token or quoted phrase that contains |
| 241734 | ** no token characters at all. (e.g ... MATCH '""'). */ |
| 241735 | sCtx.pPhrase = sqlite3Fts5MallocZero(&pParse->rc, SZ_FTS5EXPRPHRASE(1)); |
| 241736 | }else if( sCtx.pPhrase->nTerm ){ |
| 241737 | sCtx.pPhrase->aTerm[sCtx.pPhrase->nTerm-1].bPrefix = (u8)bPrefix; |
| 241738 | } |
| 241739 | assert( pParse->apPhrase!=0 ); |
| 241740 | pParse->apPhrase[pParse->nPhrase-1] = sCtx.pPhrase; |
| @@ -240968,23 +241765,22 @@ | |
| 241765 | if( rc==SQLITE_OK ){ |
| 241766 | pNew->apExprPhrase = (Fts5ExprPhrase**)sqlite3Fts5MallocZero(&rc, |
| 241767 | sizeof(Fts5ExprPhrase*)); |
| 241768 | } |
| 241769 | if( rc==SQLITE_OK ){ |
| 241770 | pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&rc, SZ_FTS5EXPRNODE(1)); |
| 241771 | } |
| 241772 | if( rc==SQLITE_OK ){ |
| 241773 | pNew->pRoot->pNear = (Fts5ExprNearset*)sqlite3Fts5MallocZero(&rc, |
| 241774 | SZ_FTS5EXPRNEARSET(2)); |
| 241775 | } |
| 241776 | if( rc==SQLITE_OK && ALWAYS(pOrig!=0) ){ |
| 241777 | Fts5Colset *pColsetOrig = pOrig->pNode->pNear->pColset; |
| 241778 | if( pColsetOrig ){ |
| 241779 | sqlite3_int64 nByte; |
| 241780 | Fts5Colset *pColset; |
| 241781 | nByte = SZ_FTS5COLSET(pColsetOrig->nCol); |
| 241782 | pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&rc, nByte); |
| 241783 | if( pColset ){ |
| 241784 | memcpy(pColset, pColsetOrig, (size_t)nByte); |
| 241785 | } |
| 241786 | pNew->pRoot->pNear->pColset = pColset; |
| @@ -241008,11 +241804,11 @@ | |
| 241804 | } |
| 241805 | } |
| 241806 | }else{ |
| 241807 | /* This happens when parsing a token or quoted phrase that contains |
| 241808 | ** no token characters at all. (e.g ... MATCH '""'). */ |
| 241809 | sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, SZ_FTS5EXPRPHRASE(1)); |
| 241810 | } |
| 241811 | } |
| 241812 | |
| 241813 | if( rc==SQLITE_OK && ALWAYS(sCtx.pPhrase) ){ |
| 241814 | /* All the allocations succeeded. Put the expression object together. */ |
| @@ -241102,11 +241898,11 @@ | |
| 241898 | Fts5Colset *pNew; /* New colset object to return */ |
| 241899 | |
| 241900 | assert( pParse->rc==SQLITE_OK ); |
| 241901 | assert( iCol>=0 && iCol<pParse->pConfig->nCol ); |
| 241902 | |
| 241903 | pNew = sqlite3_realloc64(p, SZ_FTS5COLSET(nCol+1)); |
| 241904 | if( pNew==0 ){ |
| 241905 | pParse->rc = SQLITE_NOMEM; |
| 241906 | }else{ |
| 241907 | int *aiCol = pNew->aiCol; |
| 241908 | int i, j; |
| @@ -241137,11 +241933,11 @@ | |
| 241933 | static Fts5Colset *sqlite3Fts5ParseColsetInvert(Fts5Parse *pParse, Fts5Colset *p){ |
| 241934 | Fts5Colset *pRet; |
| 241935 | int nCol = pParse->pConfig->nCol; |
| 241936 | |
| 241937 | pRet = (Fts5Colset*)sqlite3Fts5MallocZero(&pParse->rc, |
| 241938 | SZ_FTS5COLSET(nCol+1) |
| 241939 | ); |
| 241940 | if( pRet ){ |
| 241941 | int i; |
| 241942 | int iOld = 0; |
| 241943 | for(i=0; i<nCol; i++){ |
| @@ -241198,11 +241994,11 @@ | |
| 241994 | ** fails, (*pRc) is set to SQLITE_NOMEM and NULL is returned. |
| 241995 | */ |
| 241996 | static Fts5Colset *fts5CloneColset(int *pRc, Fts5Colset *pOrig){ |
| 241997 | Fts5Colset *pRet; |
| 241998 | if( pOrig ){ |
| 241999 | sqlite3_int64 nByte = SZ_FTS5COLSET(pOrig->nCol); |
| 242000 | pRet = (Fts5Colset*)sqlite3Fts5MallocZero(pRc, nByte); |
| 242001 | if( pRet ){ |
| 242002 | memcpy(pRet, pOrig, (size_t)nByte); |
| 242003 | } |
| 242004 | }else{ |
| @@ -241366,21 +242162,21 @@ | |
| 242162 | Fts5ExprNode *pRet; |
| 242163 | |
| 242164 | assert( pNear->nPhrase==1 ); |
| 242165 | assert( pParse->bPhraseToAnd ); |
| 242166 | |
| 242167 | nByte = SZ_FTS5EXPRNODE(nTerm+1); |
| 242168 | pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte); |
| 242169 | if( pRet ){ |
| 242170 | pRet->eType = FTS5_AND; |
| 242171 | pRet->nChild = nTerm; |
| 242172 | pRet->iHeight = 1; |
| 242173 | fts5ExprAssignXNext(pRet); |
| 242174 | pParse->nPhrase--; |
| 242175 | for(ii=0; ii<nTerm; ii++){ |
| 242176 | Fts5ExprPhrase *pPhrase = (Fts5ExprPhrase*)sqlite3Fts5MallocZero( |
| 242177 | &pParse->rc, SZ_FTS5EXPRPHRASE(1) |
| 242178 | ); |
| 242179 | if( pPhrase ){ |
| 242180 | if( parseGrowPhraseArray(pParse) ){ |
| 242181 | fts5ExprPhraseFree(pPhrase); |
| 242182 | }else{ |
| @@ -241445,11 +242241,11 @@ | |
| 242241 | nChild = 2; |
| 242242 | if( pLeft->eType==eType ) nChild += pLeft->nChild-1; |
| 242243 | if( pRight->eType==eType ) nChild += pRight->nChild-1; |
| 242244 | } |
| 242245 | |
| 242246 | nByte = SZ_FTS5EXPRNODE(nChild); |
| 242247 | pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte); |
| 242248 | |
| 242249 | if( pRet ){ |
| 242250 | pRet->eType = eType; |
| 242251 | pRet->pNear = pNear; |
| @@ -242320,11 +243116,11 @@ | |
| 243116 | } |
| 243117 | return rc; |
| 243118 | } |
| 243119 | |
| 243120 | /* |
| 243121 | ** Clear the token mappings for all Fts5IndexIter objects managed by |
| 243122 | ** the expression passed as the only argument. |
| 243123 | */ |
| 243124 | static void sqlite3Fts5ExprClearTokens(Fts5Expr *pExpr){ |
| 243125 | int ii; |
| 243126 | for(ii=0; ii<pExpr->nPhrase; ii++){ |
| @@ -242355,11 +243151,11 @@ | |
| 243151 | |
| 243152 | typedef struct Fts5HashEntry Fts5HashEntry; |
| 243153 | |
| 243154 | /* |
| 243155 | ** This file contains the implementation of an in-memory hash table used |
| 243156 | ** to accumulate "term -> doclist" content before it is flushed to a level-0 |
| 243157 | ** segment. |
| 243158 | */ |
| 243159 | |
| 243160 | |
| 243161 | struct Fts5Hash { |
| @@ -242412,11 +243208,11 @@ | |
| 243208 | int iPos; /* Position of last value written */ |
| 243209 | i64 iRowid; /* Rowid of last value written */ |
| 243210 | }; |
| 243211 | |
| 243212 | /* |
| 243213 | ** Equivalent to: |
| 243214 | ** |
| 243215 | ** char *fts5EntryKey(Fts5HashEntry *pEntry){ return zKey; } |
| 243216 | */ |
| 243217 | #define fts5EntryKey(p) ( ((char *)(&(p)[1])) ) |
| 243218 | |
| @@ -243348,13 +244144,17 @@ | |
| 244144 | int nRef; /* Object reference count */ |
| 244145 | u64 nWriteCounter; /* Total leaves written to level 0 */ |
| 244146 | u64 nOriginCntr; /* Origin value for next top-level segment */ |
| 244147 | int nSegment; /* Total segments in this structure */ |
| 244148 | int nLevel; /* Number of levels in this index */ |
| 244149 | Fts5StructureLevel aLevel[FLEXARRAY]; /* Array of nLevel level objects */ |
| 244150 | }; |
| 244151 | |
| 244152 | /* Size (in bytes) of an Fts5Structure object holding up to N levels */ |
| 244153 | #define SZ_FTS5STRUCTURE(N) \ |
| 244154 | (offsetof(Fts5Structure,aLevel) + (N)*sizeof(Fts5StructureLevel)) |
| 244155 | |
| 244156 | /* |
| 244157 | ** An object of type Fts5SegWriter is used to write to segments. |
| 244158 | */ |
| 244159 | struct Fts5PageWriter { |
| 244160 | int pgno; /* Page number for this page */ |
| @@ -243480,14 +244280,18 @@ | |
| 244280 | |
| 244281 | /* |
| 244282 | ** Array of tombstone pages. Reference counted. |
| 244283 | */ |
| 244284 | struct Fts5TombstoneArray { |
| 244285 | int nRef; /* Number of pointers to this object */ |
| 244286 | int nTombstone; |
| 244287 | Fts5Data *apTombstone[FLEXARRAY]; /* Array of tombstone pages */ |
| 244288 | }; |
| 244289 | |
| 244290 | /* Size (in bytes) of an Fts5TombstoneArray holding up to N tombstones */ |
| 244291 | #define SZ_FTS5TOMBSTONEARRAY(N) \ |
| 244292 | (offsetof(Fts5TombstoneArray,apTombstone)+(N)*sizeof(Fts5Data*)) |
| 244293 | |
| 244294 | /* |
| 244295 | ** Argument is a pointer to an Fts5Data structure that contains a |
| 244296 | ** leaf page. |
| 244297 | */ |
| @@ -243553,12 +244357,15 @@ | |
| 244357 | int bRev; /* True to iterate in reverse order */ |
| 244358 | u8 bSkipEmpty; /* True to skip deleted entries */ |
| 244359 | |
| 244360 | i64 iSwitchRowid; /* Firstest rowid of other than aFirst[1] */ |
| 244361 | Fts5CResult *aFirst; /* Current merge state (see above) */ |
| 244362 | Fts5SegIter aSeg[FLEXARRAY]; /* Array of segment iterators */ |
| 244363 | }; |
| 244364 | |
| 244365 | /* Size (in bytes) of an Fts5Iter object holding up to N segment iterators */ |
| 244366 | #define SZ_FTS5ITER(N) (offsetof(Fts5Iter,aSeg)+(N)*sizeof(Fts5SegIter)) |
| 244367 | |
| 244368 | /* |
| 244369 | ** An instance of the following type is used to iterate through the contents |
| 244370 | ** of a doclist-index record. |
| 244371 | ** |
| @@ -243582,12 +244389,16 @@ | |
| 244389 | i64 iRowid; /* First rowid on leaf iLeafPgno */ |
| 244390 | }; |
| 244391 | struct Fts5DlidxIter { |
| 244392 | int nLvl; |
| 244393 | int iSegid; |
| 244394 | Fts5DlidxLvl aLvl[FLEXARRAY]; |
| 244395 | }; |
| 244396 | |
| 244397 | /* Size (in bytes) of an Fts5DlidxIter object with up to N levels */ |
| 244398 | #define SZ_FTS5DLIDXITER(N) \ |
| 244399 | (offsetof(Fts5DlidxIter,aLvl)+(N)*sizeof(Fts5DlidxLvl)) |
| 244400 | |
| 244401 | static void fts5PutU16(u8 *aOut, u16 iVal){ |
| 244402 | aOut[0] = (iVal>>8); |
| 244403 | aOut[1] = (iVal&0xFF); |
| 244404 | } |
| @@ -243952,11 +244763,11 @@ | |
| 244763 | ** an error occurs, (*pRc) is set to an SQLite error code before returning. |
| 244764 | */ |
| 244765 | static void fts5StructureMakeWritable(int *pRc, Fts5Structure **pp){ |
| 244766 | Fts5Structure *p = *pp; |
| 244767 | if( *pRc==SQLITE_OK && p->nRef>1 ){ |
| 244768 | i64 nByte = SZ_FTS5STRUCTURE(p->nLevel); |
| 244769 | Fts5Structure *pNew; |
| 244770 | pNew = (Fts5Structure*)sqlite3Fts5MallocZero(pRc, nByte); |
| 244771 | if( pNew ){ |
| 244772 | int i; |
| 244773 | memcpy(pNew, p, nByte); |
| @@ -244026,14 +244837,11 @@ | |
| 244837 | if( nLevel>FTS5_MAX_SEGMENT || nLevel<0 |
| 244838 | || nSegment>FTS5_MAX_SEGMENT || nSegment<0 |
| 244839 | ){ |
| 244840 | return FTS5_CORRUPT; |
| 244841 | } |
| 244842 | nByte = SZ_FTS5STRUCTURE(nLevel); |
| 244843 | pRet = (Fts5Structure*)sqlite3Fts5MallocZero(&rc, nByte); |
| 244844 | |
| 244845 | if( pRet ){ |
| 244846 | pRet->nRef = 1; |
| 244847 | pRet->nLevel = nLevel; |
| @@ -244109,14 +244917,11 @@ | |
| 244917 | fts5StructureMakeWritable(pRc, ppStruct); |
| 244918 | assert( (ppStruct!=0 && (*ppStruct)!=0) || (*pRc)!=SQLITE_OK ); |
| 244919 | if( *pRc==SQLITE_OK ){ |
| 244920 | Fts5Structure *pStruct = *ppStruct; |
| 244921 | int nLevel = pStruct->nLevel; |
| 244922 | sqlite3_int64 nByte = SZ_FTS5STRUCTURE(nLevel+2); |
| 244923 | |
| 244924 | pStruct = sqlite3_realloc64(pStruct, nByte); |
| 244925 | if( pStruct ){ |
| 244926 | memset(&pStruct->aLevel[nLevel], 0, sizeof(Fts5StructureLevel)); |
| 244927 | pStruct->nLevel++; |
| @@ -244651,11 +245456,11 @@ | |
| 245456 | Fts5DlidxIter *pIter = 0; |
| 245457 | int i; |
| 245458 | int bDone = 0; |
| 245459 | |
| 245460 | for(i=0; p->rc==SQLITE_OK && bDone==0; i++){ |
| 245461 | sqlite3_int64 nByte = SZ_FTS5DLIDXITER(i+1); |
| 245462 | Fts5DlidxIter *pNew; |
| 245463 | |
| 245464 | pNew = (Fts5DlidxIter*)sqlite3_realloc64(pIter, nByte); |
| 245465 | if( pNew==0 ){ |
| 245466 | p->rc = SQLITE_NOMEM; |
| @@ -244869,11 +245674,11 @@ | |
| 245674 | ** leave an error in the Fts5Index object. |
| 245675 | */ |
| 245676 | static void fts5SegIterAllocTombstone(Fts5Index *p, Fts5SegIter *pIter){ |
| 245677 | const int nTomb = pIter->pSeg->nPgTombstone; |
| 245678 | if( nTomb>0 ){ |
| 245679 | int nByte = SZ_FTS5TOMBSTONEARRAY(nTomb+1); |
| 245680 | Fts5TombstoneArray *pNew; |
| 245681 | pNew = (Fts5TombstoneArray*)sqlite3Fts5MallocZero(&p->rc, nByte); |
| 245682 | if( pNew ){ |
| 245683 | pNew->nTombstone = nTomb; |
| 245684 | pNew->nRef = 1; |
| @@ -246330,12 +247135,11 @@ | |
| 247135 | Fts5Iter *pNew; |
| 247136 | i64 nSlot; /* Power of two >= nSeg */ |
| 247137 | |
| 247138 | for(nSlot=2; nSlot<nSeg; nSlot=nSlot*2); |
| 247139 | pNew = fts5IdxMalloc(p, |
| 247140 | SZ_FTS5ITER(nSlot) + /* pNew + pNew->aSeg[] */ |
| 247141 | sizeof(Fts5CResult) * nSlot /* pNew->aFirst[] */ |
| 247142 | ); |
| 247143 | if( pNew ){ |
| 247144 | pNew->nSeg = nSlot; |
| 247145 | pNew->aFirst = (Fts5CResult*)&pNew->aSeg[nSlot]; |
| @@ -248697,11 +249501,11 @@ | |
| 249501 | static Fts5Structure *fts5IndexOptimizeStruct( |
| 249502 | Fts5Index *p, |
| 249503 | Fts5Structure *pStruct |
| 249504 | ){ |
| 249505 | Fts5Structure *pNew = 0; |
| 249506 | sqlite3_int64 nByte = SZ_FTS5STRUCTURE(1); |
| 249507 | int nSeg = pStruct->nSegment; |
| 249508 | int i; |
| 249509 | |
| 249510 | /* Figure out if this structure requires optimization. A structure does |
| 249511 | ** not require optimization if either: |
| @@ -248727,10 +249531,11 @@ | |
| 249531 | } |
| 249532 | assert( pStruct->aLevel[i].nMerge<=nThis ); |
| 249533 | } |
| 249534 | |
| 249535 | nByte += (((i64)pStruct->nLevel)+1) * sizeof(Fts5StructureLevel); |
| 249536 | assert( nByte==SZ_FTS5STRUCTURE(pStruct->nLevel+2) ); |
| 249537 | pNew = (Fts5Structure*)sqlite3Fts5MallocZero(&p->rc, nByte); |
| 249538 | |
| 249539 | if( pNew ){ |
| 249540 | Fts5StructureLevel *pLvl; |
| 249541 | nByte = nSeg * sizeof(Fts5StructureSegment); |
| @@ -249303,12 +250108,16 @@ | |
| 250108 | /* The following are used for other full-token tokendata queries only. */ |
| 250109 | int nIter; |
| 250110 | int nIterAlloc; |
| 250111 | Fts5PoslistReader *aPoslistReader; |
| 250112 | int *aPoslistToIter; |
| 250113 | Fts5Iter *apIter[FLEXARRAY]; |
| 250114 | }; |
| 250115 | |
| 250116 | /* Size in bytes of an Fts5TokenDataIter object holding up to N iterators */ |
| 250117 | #define SZ_FTS5TOKENDATAITER(N) \ |
| 250118 | (offsetof(Fts5TokenDataIter,apIter) + (N)*sizeof(Fts5Iter)) |
| 250119 | |
| 250120 | /* |
| 250121 | ** The two input arrays - a1[] and a2[] - are in sorted order. This function |
| 250122 | ** merges the two arrays together and writes the result to output array |
| 250123 | ** aOut[]. aOut[] is guaranteed to be large enough to hold the result. |
| @@ -249377,11 +250186,11 @@ | |
| 250186 | } |
| 250187 | |
| 250188 | /* |
| 250189 | ** Sort the contents of the pT->aMap[] array. |
| 250190 | ** |
| 250191 | ** The sorting algorithm requires a malloc(). If this fails, an error code |
| 250192 | ** is left in Fts5Index.rc before returning. |
| 250193 | */ |
| 250194 | static void fts5TokendataIterSortMap(Fts5Index *p, Fts5TokenDataIter *pT){ |
| 250195 | Fts5TokenDataMap *aTmp = 0; |
| 250196 | int nByte = pT->nMap * sizeof(Fts5TokenDataMap); |
| @@ -249568,11 +250377,11 @@ | |
| 250377 | if( iIdx==0 |
| 250378 | && p->pConfig->eDetail==FTS5_DETAIL_FULL |
| 250379 | && p->pConfig->bPrefixInsttoken |
| 250380 | ){ |
| 250381 | s.pTokendata = &s2; |
| 250382 | s2.pT = (Fts5TokenDataIter*)fts5IdxMalloc(p, SZ_FTS5TOKENDATAITER(1)); |
| 250383 | } |
| 250384 | |
| 250385 | if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){ |
| 250386 | s.xMerge = fts5MergeRowidLists; |
| 250387 | s.xAppend = fts5AppendRowid; |
| @@ -249696,19 +250505,21 @@ | |
| 250505 | ** The %_data table is completely empty when this function is called. This |
| 250506 | ** function populates it with the initial structure objects for each index, |
| 250507 | ** and the initial version of the "averages" record (a zero-byte blob). |
| 250508 | */ |
| 250509 | static int sqlite3Fts5IndexReinit(Fts5Index *p){ |
| 250510 | Fts5Structure *pTmp; |
| 250511 | u8 tmpSpace[SZ_FTS5STRUCTURE(1)]; |
| 250512 | fts5StructureInvalidate(p); |
| 250513 | fts5IndexDiscardData(p); |
| 250514 | pTmp = (Fts5Structure*)tmpSpace; |
| 250515 | memset(pTmp, 0, SZ_FTS5STRUCTURE(1)); |
| 250516 | if( p->pConfig->bContentlessDelete ){ |
| 250517 | pTmp->nOriginCntr = 1; |
| 250518 | } |
| 250519 | fts5DataWrite(p, FTS5_AVERAGES_ROWID, (const u8*)"", 0); |
| 250520 | fts5StructureWrite(p, pTmp); |
| 250521 | return fts5IndexReturn(p); |
| 250522 | } |
| 250523 | |
| 250524 | /* |
| 250525 | ** Open a new Fts5Index handle. If the bCreate argument is true, create |
| @@ -249912,11 +250723,11 @@ | |
| 250723 | Fts5TokenDataIter *pRet = pIn; |
| 250724 | |
| 250725 | if( p->rc==SQLITE_OK ){ |
| 250726 | if( pIn==0 || pIn->nIter==pIn->nIterAlloc ){ |
| 250727 | int nAlloc = pIn ? pIn->nIterAlloc*2 : 16; |
| 250728 | int nByte = SZ_FTS5TOKENDATAITER(nAlloc+1); |
| 250729 | Fts5TokenDataIter *pNew = (Fts5TokenDataIter*)sqlite3_realloc(pIn, nByte); |
| 250730 | |
| 250731 | if( pNew==0 ){ |
| 250732 | p->rc = SQLITE_NOMEM; |
| 250733 | }else{ |
| @@ -250428,11 +251239,12 @@ | |
| 251239 | |
| 251240 | memset(&ctx, 0, sizeof(ctx)); |
| 251241 | |
| 251242 | fts5BufferGrow(&p->rc, &token, nToken+1); |
| 251243 | assert( token.p!=0 || p->rc!=SQLITE_OK ); |
| 251244 | ctx.pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, |
| 251245 | SZ_FTS5TOKENDATAITER(1)); |
| 251246 | |
| 251247 | if( p->rc==SQLITE_OK ){ |
| 251248 | |
| 251249 | /* Fill in the token prefix to search for */ |
| 251250 | token.p[0] = FTS5_MAIN_PREFIX; |
| @@ -250559,11 +251371,12 @@ | |
| 251371 | assert( p->pConfig->eDetail!=FTS5_DETAIL_FULL ); |
| 251372 | assert( pIter->pTokenDataIter || pIter->nSeg>0 ); |
| 251373 | if( pIter->nSeg>0 ){ |
| 251374 | /* This is a prefix term iterator. */ |
| 251375 | if( pT==0 ){ |
| 251376 | pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, |
| 251377 | SZ_FTS5TOKENDATAITER(1)); |
| 251378 | pIter->pTokenDataIter = pT; |
| 251379 | } |
| 251380 | if( pT ){ |
| 251381 | fts5TokendataIterAppendMap(p, pT, pT->terms.n, nToken, iRowid, iPos); |
| 251382 | fts5BufferAppendBlob(&p->rc, &pT->terms, nToken, (const u8*)pToken); |
| @@ -251593,11 +252406,11 @@ | |
| 252406 | } |
| 252407 | #endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */ |
| 252408 | |
| 252409 | #if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG) |
| 252410 | static void fts5DebugRowid(int *pRc, Fts5Buffer *pBuf, i64 iKey){ |
| 252411 | int iSegid, iHeight, iPgno, bDlidx, bTomb; /* Rowid components */ |
| 252412 | fts5DecodeRowid(iKey, &bTomb, &iSegid, &bDlidx, &iHeight, &iPgno); |
| 252413 | |
| 252414 | if( iSegid==0 ){ |
| 252415 | if( iKey==FTS5_AVERAGES_ROWID ){ |
| 252416 | sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "{averages} "); |
| @@ -252554,13 +253367,15 @@ | |
| 253367 | struct Fts5Sorter { |
| 253368 | sqlite3_stmt *pStmt; |
| 253369 | i64 iRowid; /* Current rowid */ |
| 253370 | const u8 *aPoslist; /* Position lists for current row */ |
| 253371 | int nIdx; /* Number of entries in aIdx[] */ |
| 253372 | int aIdx[FLEXARRAY]; /* Offsets into aPoslist for current row */ |
| 253373 | }; |
| 253374 | |
| 253375 | /* Size (int bytes) of an Fts5Sorter object with N indexes */ |
| 253376 | #define SZ_FTS5SORTER(N) (offsetof(Fts5Sorter,nIdx)+((N+2)/2)*sizeof(i64)) |
| 253377 | |
| 253378 | /* |
| 253379 | ** Virtual-table cursor object. |
| 253380 | ** |
| 253381 | ** iSpecial: |
| @@ -253434,11 +254249,11 @@ | |
| 254249 | int rc; |
| 254250 | const char *zRank = pCsr->zRank; |
| 254251 | const char *zRankArgs = pCsr->zRankArgs; |
| 254252 | |
| 254253 | nPhrase = sqlite3Fts5ExprPhraseCount(pCsr->pExpr); |
| 254254 | nByte = SZ_FTS5SORTER(nPhrase); |
| 254255 | pSorter = (Fts5Sorter*)sqlite3_malloc64(nByte); |
| 254256 | if( pSorter==0 ) return SQLITE_NOMEM; |
| 254257 | memset(pSorter, 0, (size_t)nByte); |
| 254258 | pSorter->nIdx = nPhrase; |
| 254259 | |
| @@ -255960,11 +256775,11 @@ | |
| 256775 | int nArg, /* Number of args */ |
| 256776 | sqlite3_value **apUnused /* Function arguments */ |
| 256777 | ){ |
| 256778 | assert( nArg==0 ); |
| 256779 | UNUSED_PARAM2(nArg, apUnused); |
| 256780 | sqlite3_result_text(pCtx, "fts5: 2025-03-16 00:13:29 18bda13e197e4b4ec7464b3e70012f71edc05f73d8b14bb48bad452f81c7e185", -1, SQLITE_TRANSIENT); |
| 256781 | } |
| 256782 | |
| 256783 | /* |
| 256784 | ** Implementation of fts5_locale(LOCALE, TEXT) function. |
| 256785 | ** |
| @@ -256185,12 +257000,12 @@ | |
| 257000 | /* If SQLITE_FTS5_ENABLE_TEST_MI is defined, assume that the file |
| 257001 | ** fts5_test_mi.c is compiled and linked into the executable. And call |
| 257002 | ** its entry point to enable the matchinfo() demo. */ |
| 257003 | #ifdef SQLITE_FTS5_ENABLE_TEST_MI |
| 257004 | if( rc==SQLITE_OK ){ |
| 257005 | extern int sqlite3Fts5TestRegisterMatchinfoAPI(fts5_api*); |
| 257006 | rc = sqlite3Fts5TestRegisterMatchinfoAPI(&pGlobal->api); |
| 257007 | } |
| 257008 | #endif |
| 257009 | |
| 257010 | return rc; |
| 257011 | } |
| 257012 |
+49
-2
| --- extsrc/sqlite3.h | ||
| +++ extsrc/sqlite3.h | ||
| @@ -146,11 +146,11 @@ | ||
| 146 | 146 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 147 | 147 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 148 | 148 | */ |
| 149 | 149 | #define SQLITE_VERSION "3.50.0" |
| 150 | 150 | #define SQLITE_VERSION_NUMBER 3050000 |
| 151 | -#define SQLITE_SOURCE_ID "2025-02-18 01:16:26 57caa3136d1bfca06e4f2285734a4977b8d3fa1f75bf87453b975867e9de38fc" | |
| 151 | +#define SQLITE_SOURCE_ID "2025-03-16 00:13:29 18bda13e197e4b4ec7464b3e70012f71edc05f73d8b14bb48bad452f81c7e185" | |
| 152 | 152 | |
| 153 | 153 | /* |
| 154 | 154 | ** CAPI3REF: Run-Time Library Version Numbers |
| 155 | 155 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 156 | 156 | ** |
| @@ -1160,10 +1160,16 @@ | ||
| 1160 | 1160 | ** to block for up to M milliseconds before failing when attempting to |
| 1161 | 1161 | ** obtain a file lock using the xLock or xShmLock methods of the VFS. |
| 1162 | 1162 | ** The parameter is a pointer to a 32-bit signed integer that contains |
| 1163 | 1163 | ** the value that M is to be set to. Before returning, the 32-bit signed |
| 1164 | 1164 | ** integer is overwritten with the previous value of M. |
| 1165 | +** | |
| 1166 | +** <li>[[SQLITE_FCNTL_BLOCK_ON_CONNECT]] | |
| 1167 | +** The [SQLITE_FCNTL_BLOCK_ON_CONNECT] opcode is used to configure the | |
| 1168 | +** VFS to block when taking a SHARED lock to connect to a wal mode database. | |
| 1169 | +** This is used to implement the functionality associated with | |
| 1170 | +** SQLITE_SETLK_BLOCK_ON_CONNECT. | |
| 1165 | 1171 | ** |
| 1166 | 1172 | ** <li>[[SQLITE_FCNTL_DATA_VERSION]] |
| 1167 | 1173 | ** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to |
| 1168 | 1174 | ** a database file. The argument is a pointer to a 32-bit unsigned integer. |
| 1169 | 1175 | ** The "data version" for the pager is written into the pointer. The |
| @@ -1257,10 +1263,11 @@ | ||
| 1257 | 1263 | #define SQLITE_FCNTL_CKPT_START 39 |
| 1258 | 1264 | #define SQLITE_FCNTL_EXTERNAL_READER 40 |
| 1259 | 1265 | #define SQLITE_FCNTL_CKSM_FILE 41 |
| 1260 | 1266 | #define SQLITE_FCNTL_RESET_CACHE 42 |
| 1261 | 1267 | #define SQLITE_FCNTL_NULL_IO 43 |
| 1268 | +#define SQLITE_FCNTL_BLOCK_ON_CONNECT 44 | |
| 1262 | 1269 | |
| 1263 | 1270 | /* deprecated names */ |
| 1264 | 1271 | #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE |
| 1265 | 1272 | #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE |
| 1266 | 1273 | #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO |
| @@ -3013,10 +3020,48 @@ | ||
| 3013 | 3020 | ** |
| 3014 | 3021 | ** See also: [PRAGMA busy_timeout] |
| 3015 | 3022 | */ |
| 3016 | 3023 | SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); |
| 3017 | 3024 | |
| 3025 | +/* | |
| 3026 | +** CAPI3REF: Set the Setlk Timeout | |
| 3027 | +** METHOD: sqlite3 | |
| 3028 | +** | |
| 3029 | +** This routine is only useful in SQLITE_ENABLE_SETLK_TIMEOUT builds. If | |
| 3030 | +** the VFS supports blocking locks, it sets the timeout in ms used by | |
| 3031 | +** eligible locks taken on wal mode databases by the specified database | |
| 3032 | +** handle. In non-SQLITE_ENABLE_SETLK_TIMEOUT builds, or if the VFS does | |
| 3033 | +** not support blocking locks, this function is a no-op. | |
| 3034 | +** | |
| 3035 | +** Passing 0 to this function disables blocking locks altogether. Passing | |
| 3036 | +** -1 to this function requests that the VFS blocks for a long time - | |
| 3037 | +** indefinitely if possible. The results of passing any other negative value | |
| 3038 | +** are undefined. | |
| 3039 | +** | |
| 3040 | +** Internally, each SQLite database handle store two timeout values - the | |
| 3041 | +** busy-timeout (used for rollback mode databases, or if the VFS does not | |
| 3042 | +** support blocking locks) and the setlk-timeout (used for blocking locks | |
| 3043 | +** on wal-mode databases). The sqlite3_busy_timeout() method sets both | |
| 3044 | +** values, this function sets only the setlk-timeout value. Therefore, | |
| 3045 | +** to configure separate busy-timeout and setlk-timeout values for a single | |
| 3046 | +** database handle, call sqlite3_busy_timeout() followed by this function. | |
| 3047 | +** | |
| 3048 | +** Whenever the number of connections to a wal mode database falls from | |
| 3049 | +** 1 to 0, the last connection takes an exclusive lock on the database, | |
| 3050 | +** then checkpoints and deletes the wal file. While it is doing this, any | |
| 3051 | +** new connection that tries to read from the database fails with an | |
| 3052 | +** SQLITE_BUSY error. Or, if the SQLITE_SETLK_BLOCK_ON_CONNECT flag is | |
| 3053 | +** passed to this API, the new connection blocks until the exclusive lock | |
| 3054 | +** has been released. | |
| 3055 | +*/ | |
| 3056 | +SQLITE_API int sqlite3_setlk_timeout(sqlite3*, int ms, int flags); | |
| 3057 | + | |
| 3058 | +/* | |
| 3059 | +** CAPI3REF: Flags for sqlite3_setlk_timeout() | |
| 3060 | +*/ | |
| 3061 | +#define SQLITE_SETLK_BLOCK_ON_CONNECT 0x01 | |
| 3062 | + | |
| 3018 | 3063 | /* |
| 3019 | 3064 | ** CAPI3REF: Convenience Routines For Running Queries |
| 3020 | 3065 | ** METHOD: sqlite3 |
| 3021 | 3066 | ** |
| 3022 | 3067 | ** This is a legacy interface that is preserved for backwards compatibility. |
| @@ -5128,11 +5173,11 @@ | ||
| 5128 | 5173 | ** For all versions of SQLite up to and including 3.6.23.1, a call to |
| 5129 | 5174 | ** [sqlite3_reset()] was required after sqlite3_step() returned anything |
| 5130 | 5175 | ** other than [SQLITE_ROW] before any subsequent invocation of |
| 5131 | 5176 | ** sqlite3_step(). Failure to reset the prepared statement using |
| 5132 | 5177 | ** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from |
| 5133 | -** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1], | |
| 5178 | +** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1]), | |
| 5134 | 5179 | ** sqlite3_step() began |
| 5135 | 5180 | ** calling [sqlite3_reset()] automatically in this circumstance rather |
| 5136 | 5181 | ** than returning [SQLITE_MISUSE]. This is not considered a compatibility |
| 5137 | 5182 | ** break because any application that ever receives an SQLITE_MISUSE error |
| 5138 | 5183 | ** is broken by definition. The [SQLITE_OMIT_AUTORESET] compile-time option |
| @@ -7024,10 +7069,12 @@ | ||
| 7024 | 7069 | ** ^Any callback set by a previous call to this function |
| 7025 | 7070 | ** for the same database connection is overridden. |
| 7026 | 7071 | ** |
| 7027 | 7072 | ** ^The second argument is a pointer to the function to invoke when a |
| 7028 | 7073 | ** row is updated, inserted or deleted in a rowid table. |
| 7074 | +** ^The update hook is disabled by invoking sqlite3_update_hook() | |
| 7075 | +** with a NULL pointer as the second parameter. | |
| 7029 | 7076 | ** ^The first argument to the callback is a copy of the third argument |
| 7030 | 7077 | ** to sqlite3_update_hook(). |
| 7031 | 7078 | ** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE], |
| 7032 | 7079 | ** or [SQLITE_UPDATE], depending on the operation that caused the callback |
| 7033 | 7080 | ** to be invoked. |
| 7034 | 7081 |
| --- extsrc/sqlite3.h | |
| +++ extsrc/sqlite3.h | |
| @@ -146,11 +146,11 @@ | |
| 146 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 147 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 148 | */ |
| 149 | #define SQLITE_VERSION "3.50.0" |
| 150 | #define SQLITE_VERSION_NUMBER 3050000 |
| 151 | #define SQLITE_SOURCE_ID "2025-02-18 01:16:26 57caa3136d1bfca06e4f2285734a4977b8d3fa1f75bf87453b975867e9de38fc" |
| 152 | |
| 153 | /* |
| 154 | ** CAPI3REF: Run-Time Library Version Numbers |
| 155 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 156 | ** |
| @@ -1160,10 +1160,16 @@ | |
| 1160 | ** to block for up to M milliseconds before failing when attempting to |
| 1161 | ** obtain a file lock using the xLock or xShmLock methods of the VFS. |
| 1162 | ** The parameter is a pointer to a 32-bit signed integer that contains |
| 1163 | ** the value that M is to be set to. Before returning, the 32-bit signed |
| 1164 | ** integer is overwritten with the previous value of M. |
| 1165 | ** |
| 1166 | ** <li>[[SQLITE_FCNTL_DATA_VERSION]] |
| 1167 | ** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to |
| 1168 | ** a database file. The argument is a pointer to a 32-bit unsigned integer. |
| 1169 | ** The "data version" for the pager is written into the pointer. The |
| @@ -1257,10 +1263,11 @@ | |
| 1257 | #define SQLITE_FCNTL_CKPT_START 39 |
| 1258 | #define SQLITE_FCNTL_EXTERNAL_READER 40 |
| 1259 | #define SQLITE_FCNTL_CKSM_FILE 41 |
| 1260 | #define SQLITE_FCNTL_RESET_CACHE 42 |
| 1261 | #define SQLITE_FCNTL_NULL_IO 43 |
| 1262 | |
| 1263 | /* deprecated names */ |
| 1264 | #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE |
| 1265 | #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE |
| 1266 | #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO |
| @@ -3013,10 +3020,48 @@ | |
| 3013 | ** |
| 3014 | ** See also: [PRAGMA busy_timeout] |
| 3015 | */ |
| 3016 | SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); |
| 3017 | |
| 3018 | /* |
| 3019 | ** CAPI3REF: Convenience Routines For Running Queries |
| 3020 | ** METHOD: sqlite3 |
| 3021 | ** |
| 3022 | ** This is a legacy interface that is preserved for backwards compatibility. |
| @@ -5128,11 +5173,11 @@ | |
| 5128 | ** For all versions of SQLite up to and including 3.6.23.1, a call to |
| 5129 | ** [sqlite3_reset()] was required after sqlite3_step() returned anything |
| 5130 | ** other than [SQLITE_ROW] before any subsequent invocation of |
| 5131 | ** sqlite3_step(). Failure to reset the prepared statement using |
| 5132 | ** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from |
| 5133 | ** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1], |
| 5134 | ** sqlite3_step() began |
| 5135 | ** calling [sqlite3_reset()] automatically in this circumstance rather |
| 5136 | ** than returning [SQLITE_MISUSE]. This is not considered a compatibility |
| 5137 | ** break because any application that ever receives an SQLITE_MISUSE error |
| 5138 | ** is broken by definition. The [SQLITE_OMIT_AUTORESET] compile-time option |
| @@ -7024,10 +7069,12 @@ | |
| 7024 | ** ^Any callback set by a previous call to this function |
| 7025 | ** for the same database connection is overridden. |
| 7026 | ** |
| 7027 | ** ^The second argument is a pointer to the function to invoke when a |
| 7028 | ** row is updated, inserted or deleted in a rowid table. |
| 7029 | ** ^The first argument to the callback is a copy of the third argument |
| 7030 | ** to sqlite3_update_hook(). |
| 7031 | ** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE], |
| 7032 | ** or [SQLITE_UPDATE], depending on the operation that caused the callback |
| 7033 | ** to be invoked. |
| 7034 |
| --- extsrc/sqlite3.h | |
| +++ extsrc/sqlite3.h | |
| @@ -146,11 +146,11 @@ | |
| 146 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 147 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 148 | */ |
| 149 | #define SQLITE_VERSION "3.50.0" |
| 150 | #define SQLITE_VERSION_NUMBER 3050000 |
| 151 | #define SQLITE_SOURCE_ID "2025-03-16 00:13:29 18bda13e197e4b4ec7464b3e70012f71edc05f73d8b14bb48bad452f81c7e185" |
| 152 | |
| 153 | /* |
| 154 | ** CAPI3REF: Run-Time Library Version Numbers |
| 155 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 156 | ** |
| @@ -1160,10 +1160,16 @@ | |
| 1160 | ** to block for up to M milliseconds before failing when attempting to |
| 1161 | ** obtain a file lock using the xLock or xShmLock methods of the VFS. |
| 1162 | ** The parameter is a pointer to a 32-bit signed integer that contains |
| 1163 | ** the value that M is to be set to. Before returning, the 32-bit signed |
| 1164 | ** integer is overwritten with the previous value of M. |
| 1165 | ** |
| 1166 | ** <li>[[SQLITE_FCNTL_BLOCK_ON_CONNECT]] |
| 1167 | ** The [SQLITE_FCNTL_BLOCK_ON_CONNECT] opcode is used to configure the |
| 1168 | ** VFS to block when taking a SHARED lock to connect to a wal mode database. |
| 1169 | ** This is used to implement the functionality associated with |
| 1170 | ** SQLITE_SETLK_BLOCK_ON_CONNECT. |
| 1171 | ** |
| 1172 | ** <li>[[SQLITE_FCNTL_DATA_VERSION]] |
| 1173 | ** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to |
| 1174 | ** a database file. The argument is a pointer to a 32-bit unsigned integer. |
| 1175 | ** The "data version" for the pager is written into the pointer. The |
| @@ -1257,10 +1263,11 @@ | |
| 1263 | #define SQLITE_FCNTL_CKPT_START 39 |
| 1264 | #define SQLITE_FCNTL_EXTERNAL_READER 40 |
| 1265 | #define SQLITE_FCNTL_CKSM_FILE 41 |
| 1266 | #define SQLITE_FCNTL_RESET_CACHE 42 |
| 1267 | #define SQLITE_FCNTL_NULL_IO 43 |
| 1268 | #define SQLITE_FCNTL_BLOCK_ON_CONNECT 44 |
| 1269 | |
| 1270 | /* deprecated names */ |
| 1271 | #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE |
| 1272 | #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE |
| 1273 | #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO |
| @@ -3013,10 +3020,48 @@ | |
| 3020 | ** |
| 3021 | ** See also: [PRAGMA busy_timeout] |
| 3022 | */ |
| 3023 | SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); |
| 3024 | |
| 3025 | /* |
| 3026 | ** CAPI3REF: Set the Setlk Timeout |
| 3027 | ** METHOD: sqlite3 |
| 3028 | ** |
| 3029 | ** This routine is only useful in SQLITE_ENABLE_SETLK_TIMEOUT builds. If |
| 3030 | ** the VFS supports blocking locks, it sets the timeout in ms used by |
| 3031 | ** eligible locks taken on wal mode databases by the specified database |
| 3032 | ** handle. In non-SQLITE_ENABLE_SETLK_TIMEOUT builds, or if the VFS does |
| 3033 | ** not support blocking locks, this function is a no-op. |
| 3034 | ** |
| 3035 | ** Passing 0 to this function disables blocking locks altogether. Passing |
| 3036 | ** -1 to this function requests that the VFS blocks for a long time - |
| 3037 | ** indefinitely if possible. The results of passing any other negative value |
| 3038 | ** are undefined. |
| 3039 | ** |
| 3040 | ** Internally, each SQLite database handle store two timeout values - the |
| 3041 | ** busy-timeout (used for rollback mode databases, or if the VFS does not |
| 3042 | ** support blocking locks) and the setlk-timeout (used for blocking locks |
| 3043 | ** on wal-mode databases). The sqlite3_busy_timeout() method sets both |
| 3044 | ** values, this function sets only the setlk-timeout value. Therefore, |
| 3045 | ** to configure separate busy-timeout and setlk-timeout values for a single |
| 3046 | ** database handle, call sqlite3_busy_timeout() followed by this function. |
| 3047 | ** |
| 3048 | ** Whenever the number of connections to a wal mode database falls from |
| 3049 | ** 1 to 0, the last connection takes an exclusive lock on the database, |
| 3050 | ** then checkpoints and deletes the wal file. While it is doing this, any |
| 3051 | ** new connection that tries to read from the database fails with an |
| 3052 | ** SQLITE_BUSY error. Or, if the SQLITE_SETLK_BLOCK_ON_CONNECT flag is |
| 3053 | ** passed to this API, the new connection blocks until the exclusive lock |
| 3054 | ** has been released. |
| 3055 | */ |
| 3056 | SQLITE_API int sqlite3_setlk_timeout(sqlite3*, int ms, int flags); |
| 3057 | |
| 3058 | /* |
| 3059 | ** CAPI3REF: Flags for sqlite3_setlk_timeout() |
| 3060 | */ |
| 3061 | #define SQLITE_SETLK_BLOCK_ON_CONNECT 0x01 |
| 3062 | |
| 3063 | /* |
| 3064 | ** CAPI3REF: Convenience Routines For Running Queries |
| 3065 | ** METHOD: sqlite3 |
| 3066 | ** |
| 3067 | ** This is a legacy interface that is preserved for backwards compatibility. |
| @@ -5128,11 +5173,11 @@ | |
| 5173 | ** For all versions of SQLite up to and including 3.6.23.1, a call to |
| 5174 | ** [sqlite3_reset()] was required after sqlite3_step() returned anything |
| 5175 | ** other than [SQLITE_ROW] before any subsequent invocation of |
| 5176 | ** sqlite3_step(). Failure to reset the prepared statement using |
| 5177 | ** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from |
| 5178 | ** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1]), |
| 5179 | ** sqlite3_step() began |
| 5180 | ** calling [sqlite3_reset()] automatically in this circumstance rather |
| 5181 | ** than returning [SQLITE_MISUSE]. This is not considered a compatibility |
| 5182 | ** break because any application that ever receives an SQLITE_MISUSE error |
| 5183 | ** is broken by definition. The [SQLITE_OMIT_AUTORESET] compile-time option |
| @@ -7024,10 +7069,12 @@ | |
| 7069 | ** ^Any callback set by a previous call to this function |
| 7070 | ** for the same database connection is overridden. |
| 7071 | ** |
| 7072 | ** ^The second argument is a pointer to the function to invoke when a |
| 7073 | ** row is updated, inserted or deleted in a rowid table. |
| 7074 | ** ^The update hook is disabled by invoking sqlite3_update_hook() |
| 7075 | ** with a NULL pointer as the second parameter. |
| 7076 | ** ^The first argument to the callback is a copy of the third argument |
| 7077 | ** to sqlite3_update_hook(). |
| 7078 | ** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE], |
| 7079 | ** or [SQLITE_UPDATE], depending on the operation that caused the callback |
| 7080 | ** to be invoked. |
| 7081 |
+2
-1
| --- skins/default/css.txt | ||
| +++ skins/default/css.txt | ||
| @@ -356,13 +356,14 @@ | ||
| 356 | 356 | .content pre, table.numbered-lines > tbody > tr { |
| 357 | 357 | hyphens: none; |
| 358 | 358 | line-height: 1.25; |
| 359 | 359 | } |
| 360 | 360 | |
| 361 | -.content ul:not(.browser) li { | |
| 361 | +.content ul:not(.browser) > li { | |
| 362 | 362 | list-style-type: disc; |
| 363 | 363 | } |
| 364 | + | |
| 364 | 365 | div.filetree ul li.dir, |
| 365 | 366 | div.filetree ul li.subdir, |
| 366 | 367 | div.filetree ul li.file{ |
| 367 | 368 | list-style-type: none; |
| 368 | 369 | } |
| 369 | 370 |
| --- skins/default/css.txt | |
| +++ skins/default/css.txt | |
| @@ -356,13 +356,14 @@ | |
| 356 | .content pre, table.numbered-lines > tbody > tr { |
| 357 | hyphens: none; |
| 358 | line-height: 1.25; |
| 359 | } |
| 360 | |
| 361 | .content ul:not(.browser) li { |
| 362 | list-style-type: disc; |
| 363 | } |
| 364 | div.filetree ul li.dir, |
| 365 | div.filetree ul li.subdir, |
| 366 | div.filetree ul li.file{ |
| 367 | list-style-type: none; |
| 368 | } |
| 369 |
| --- skins/default/css.txt | |
| +++ skins/default/css.txt | |
| @@ -356,13 +356,14 @@ | |
| 356 | .content pre, table.numbered-lines > tbody > tr { |
| 357 | hyphens: none; |
| 358 | line-height: 1.25; |
| 359 | } |
| 360 | |
| 361 | .content ul:not(.browser) > li { |
| 362 | list-style-type: disc; |
| 363 | } |
| 364 | |
| 365 | div.filetree ul li.dir, |
| 366 | div.filetree ul li.subdir, |
| 367 | div.filetree ul li.file{ |
| 368 | list-style-type: none; |
| 369 | } |
| 370 |
+1
-1
| --- src/add.c | ||
| +++ src/add.c | ||
| @@ -78,11 +78,11 @@ | ||
| 78 | 78 | static int numManifests; |
| 79 | 79 | |
| 80 | 80 | if( cachedManifest == -1 ){ |
| 81 | 81 | int i; |
| 82 | 82 | Blob repo; |
| 83 | - cachedManifest = db_get_manifest_setting(); | |
| 83 | + cachedManifest = db_get_manifest_setting(0); | |
| 84 | 84 | numManifests = 0; |
| 85 | 85 | for(i=0; i<count(aManifestflags); i++){ |
| 86 | 86 | if( cachedManifest&aManifestflags[i].flg ) { |
| 87 | 87 | azManifests[numManifests++] = aManifestflags[i].fname; |
| 88 | 88 | } |
| 89 | 89 |
| --- src/add.c | |
| +++ src/add.c | |
| @@ -78,11 +78,11 @@ | |
| 78 | static int numManifests; |
| 79 | |
| 80 | if( cachedManifest == -1 ){ |
| 81 | int i; |
| 82 | Blob repo; |
| 83 | cachedManifest = db_get_manifest_setting(); |
| 84 | numManifests = 0; |
| 85 | for(i=0; i<count(aManifestflags); i++){ |
| 86 | if( cachedManifest&aManifestflags[i].flg ) { |
| 87 | azManifests[numManifests++] = aManifestflags[i].fname; |
| 88 | } |
| 89 |
| --- src/add.c | |
| +++ src/add.c | |
| @@ -78,11 +78,11 @@ | |
| 78 | static int numManifests; |
| 79 | |
| 80 | if( cachedManifest == -1 ){ |
| 81 | int i; |
| 82 | Blob repo; |
| 83 | cachedManifest = db_get_manifest_setting(0); |
| 84 | numManifests = 0; |
| 85 | for(i=0; i<count(aManifestflags); i++){ |
| 86 | if( cachedManifest&aManifestflags[i].flg ) { |
| 87 | azManifests[numManifests++] = aManifestflags[i].fname; |
| 88 | } |
| 89 |
+27
-6
| --- src/alerts.c | ||
| +++ src/alerts.c | ||
| @@ -51,10 +51,11 @@ | ||
| 51 | 51 | @ -- f - Forum posts |
| 52 | 52 | @ -- k - ** Special: Unsubscribed using /oneclickunsub |
| 53 | 53 | @ -- n - New forum threads |
| 54 | 54 | @ -- r - Replies to my own forum posts |
| 55 | 55 | @ -- t - Ticket changes |
| 56 | +@ -- u - Elevation of users' permissions (admins only) | |
| 56 | 57 | @ -- w - Wiki changes |
| 57 | 58 | @ -- x - Edits to forum posts |
| 58 | 59 | @ -- Probably different codes will be added in the future. In the future |
| 59 | 60 | @ -- we might also add a separate table that allows subscribing to email |
| 60 | 61 | @ -- notifications for specific branches or tags or tickets. |
| @@ -1133,11 +1134,11 @@ | ||
| 1133 | 1134 | ** designated host and port and all times. |
| 1134 | 1135 | */ |
| 1135 | 1136 | |
| 1136 | 1137 | |
| 1137 | 1138 | /* |
| 1138 | -** COMMAND: alerts* | |
| 1139 | +** COMMAND: alerts* abbrv-subcom | |
| 1139 | 1140 | ** |
| 1140 | 1141 | ** Usage: %fossil alerts SUBCOMMAND ARGS... |
| 1141 | 1142 | ** |
| 1142 | 1143 | ** Subcommands: |
| 1143 | 1144 | ** |
| @@ -1258,11 +1259,11 @@ | ||
| 1258 | 1259 | g.argc = 3; |
| 1259 | 1260 | } |
| 1260 | 1261 | pSetting = setting_info(&nSetting); |
| 1261 | 1262 | for(; nSetting>0; nSetting--, pSetting++ ){ |
| 1262 | 1263 | if( strncmp(pSetting->name,"email-",6)!=0 ) continue; |
| 1263 | - print_setting(pSetting, 0); | |
| 1264 | + print_setting(pSetting, 0, 0); | |
| 1264 | 1265 | } |
| 1265 | 1266 | }else |
| 1266 | 1267 | if( strncmp(zCmd, "status", nCmd)==0 ){ |
| 1267 | 1268 | Stmt q; |
| 1268 | 1269 | int iCutoff; |
| @@ -1273,11 +1274,11 @@ | ||
| 1273 | 1274 | verify_all_options(); |
| 1274 | 1275 | if( g.argc!=3 ) usage("status"); |
| 1275 | 1276 | pSetting = setting_info(&nSetting); |
| 1276 | 1277 | for(; nSetting>0; nSetting--, pSetting++ ){ |
| 1277 | 1278 | if( strncmp(pSetting->name,"email-",6)!=0 ) continue; |
| 1278 | - print_setting(pSetting, 0); | |
| 1279 | + print_setting(pSetting, 0, 0); | |
| 1279 | 1280 | } |
| 1280 | 1281 | n = db_int(0,"SELECT count(*) FROM pending_alert WHERE NOT sentSep"); |
| 1281 | 1282 | fossil_print(zFmt/*works-like:"%s%d"*/, "pending-alerts", n); |
| 1282 | 1283 | n = db_int(0,"SELECT count(*) FROM pending_alert WHERE NOT sentDigest"); |
| 1283 | 1284 | fossil_print(zFmt/*works-like:"%s%d"*/, "pending-digest-alerts", n); |
| @@ -1563,10 +1564,11 @@ | ||
| 1563 | 1564 | if( g.perm.Read && PB("sc") ) ssub[nsub++] = 'c'; |
| 1564 | 1565 | if( g.perm.RdForum && PB("sf") ) ssub[nsub++] = 'f'; |
| 1565 | 1566 | if( g.perm.RdForum && PB("sn") ) ssub[nsub++] = 'n'; |
| 1566 | 1567 | if( g.perm.RdForum && PB("sr") ) ssub[nsub++] = 'r'; |
| 1567 | 1568 | if( g.perm.RdTkt && PB("st") ) ssub[nsub++] = 't'; |
| 1569 | + if( g.perm.Admin && PB("su") ) ssub[nsub++] = 'u'; | |
| 1568 | 1570 | if( g.perm.RdWiki && PB("sw") ) ssub[nsub++] = 'w'; |
| 1569 | 1571 | if( g.perm.RdForum && PB("sx") ) ssub[nsub++] = 'x'; |
| 1570 | 1572 | ssub[nsub] = 0; |
| 1571 | 1573 | zCode = db_text(0, |
| 1572 | 1574 | "INSERT INTO subscriber(semail,suname," |
| @@ -1627,10 +1629,11 @@ | ||
| 1627 | 1629 | if( g.perm.Read ) cgi_set_parameter_nocopy("sc","1",1); |
| 1628 | 1630 | if( g.perm.RdForum ) cgi_set_parameter_nocopy("sf","1",1); |
| 1629 | 1631 | if( g.perm.RdForum ) cgi_set_parameter_nocopy("sn","1",1); |
| 1630 | 1632 | if( g.perm.RdForum ) cgi_set_parameter_nocopy("sr","1",1); |
| 1631 | 1633 | if( g.perm.RdTkt ) cgi_set_parameter_nocopy("st","1",1); |
| 1634 | + if( g.perm.Admin ) cgi_set_parameter_nocopy("su","1",1); | |
| 1632 | 1635 | if( g.perm.RdWiki ) cgi_set_parameter_nocopy("sw","1",1); |
| 1633 | 1636 | } |
| 1634 | 1637 | @ <p>To receive email notifications for changes to this |
| 1635 | 1638 | @ repository, fill out the form below and press the "Submit" button.</p> |
| 1636 | 1639 | form_begin(0, "%R/subscribe"); |
| @@ -1699,10 +1702,14 @@ | ||
| 1699 | 1702 | } |
| 1700 | 1703 | if( g.perm.RdWiki ){ |
| 1701 | 1704 | @ <label><input type="checkbox" name="sw" %s(PCK("sw"))> \ |
| 1702 | 1705 | @ Wiki</label><br> |
| 1703 | 1706 | } |
| 1707 | + if( g.perm.Admin ){ | |
| 1708 | + @ <label><input type="checkbox" name="su" %s(PCK("su"))> \ | |
| 1709 | + @ User permission elevation</label> | |
| 1710 | + } | |
| 1704 | 1711 | di = PB("di"); |
| 1705 | 1712 | @ </td></tr> |
| 1706 | 1713 | @ <tr> |
| 1707 | 1714 | @ <td class="form_label">Delivery:</td> |
| 1708 | 1715 | @ <td><select size="1" name="di"> |
| @@ -1820,11 +1827,11 @@ | ||
| 1820 | 1827 | ** verifying the email address. |
| 1821 | 1828 | */ |
| 1822 | 1829 | void alert_page(void){ |
| 1823 | 1830 | const char *zName = 0; /* Value of the name= query parameter */ |
| 1824 | 1831 | Stmt q; /* For querying the database */ |
| 1825 | - int sa, sc, sf, st, sw, sx; /* Types of notifications requested */ | |
| 1832 | + int sa, sc, sf, st, su, sw, sx; /* Types of notifications requested */ | |
| 1826 | 1833 | int sn, sr; |
| 1827 | 1834 | int sdigest = 0, sdonotcall = 0, sverified = 0; /* Other fields */ |
| 1828 | 1835 | int isLogin; /* True if logged in as an individual */ |
| 1829 | 1836 | const char *ssub = 0; /* Subscription flags */ |
| 1830 | 1837 | const char *semail = 0; /* Email address */ |
| @@ -1881,10 +1888,11 @@ | ||
| 1881 | 1888 | if( g.perm.Read && PB("sc") ) newSsub[nsub++] = 'c'; |
| 1882 | 1889 | if( g.perm.RdForum && PB("sf") ) newSsub[nsub++] = 'f'; |
| 1883 | 1890 | if( g.perm.RdForum && PB("sn") ) newSsub[nsub++] = 'n'; |
| 1884 | 1891 | if( g.perm.RdForum && PB("sr") ) newSsub[nsub++] = 'r'; |
| 1885 | 1892 | if( g.perm.RdTkt && PB("st") ) newSsub[nsub++] = 't'; |
| 1893 | + if( g.perm.Admin && PB("su") ) newSsub[nsub++] = 'u'; | |
| 1886 | 1894 | if( g.perm.RdWiki && PB("sw") ) newSsub[nsub++] = 'w'; |
| 1887 | 1895 | if( g.perm.RdForum && PB("sx") ) newSsub[nsub++] = 'x'; |
| 1888 | 1896 | newSsub[nsub] = 0; |
| 1889 | 1897 | ssub = newSsub; |
| 1890 | 1898 | blob_init(&update, "UPDATE subscriber SET", -1); |
| @@ -1979,10 +1987,11 @@ | ||
| 1979 | 1987 | sc = strchr(ssub,'c')!=0; |
| 1980 | 1988 | sf = strchr(ssub,'f')!=0; |
| 1981 | 1989 | sn = strchr(ssub,'n')!=0; |
| 1982 | 1990 | sr = strchr(ssub,'r')!=0; |
| 1983 | 1991 | st = strchr(ssub,'t')!=0; |
| 1992 | + su = strchr(ssub,'u')!=0; | |
| 1984 | 1993 | sw = strchr(ssub,'w')!=0; |
| 1985 | 1994 | sx = strchr(ssub,'x')!=0; |
| 1986 | 1995 | smip = db_column_text(&q, 5); |
| 1987 | 1996 | mtime = db_column_text(&q, 7); |
| 1988 | 1997 | sctime = db_column_text(&q, 8); |
| @@ -2097,11 +2106,19 @@ | ||
| 2097 | 2106 | @ <label><input type="checkbox" name="st" %s(st?"checked":"")>\ |
| 2098 | 2107 | @ Ticket changes</label><br> |
| 2099 | 2108 | } |
| 2100 | 2109 | if( g.perm.RdWiki ){ |
| 2101 | 2110 | @ <label><input type="checkbox" name="sw" %s(sw?"checked":"")>\ |
| 2102 | - @ Wiki</label> | |
| 2111 | + @ Wiki</label><br> | |
| 2112 | + } | |
| 2113 | + if( g.perm.Admin ){ | |
| 2114 | + /* Corner-case bug: if an admin assigns 'u' to a non-admin, that | |
| 2115 | + ** subscription will get removed if the user later edits their | |
| 2116 | + ** subscriptions, as non-admins are not permitted to add that | |
| 2117 | + ** subscription. */ | |
| 2118 | + @ <label><input type="checkbox" name="su" %s(su?"checked":"")>\ | |
| 2119 | + @ User permission elevation</label> | |
| 2103 | 2120 | } |
| 2104 | 2121 | @ </td></tr> |
| 2105 | 2122 | if( strchr(ssub,'k')!=0 ){ |
| 2106 | 2123 | @ <tr><td></td><td> ↑ |
| 2107 | 2124 | @ Note: User did a one-click unsubscribe</td></tr> |
| @@ -2529,15 +2546,16 @@ | ||
| 2529 | 2546 | ** f An original forum post |
| 2530 | 2547 | ** n New forum threads |
| 2531 | 2548 | ** r Replies to my forum posts |
| 2532 | 2549 | ** x An edit to a prior forum post |
| 2533 | 2550 | ** t A new ticket or a change to an existing ticket |
| 2551 | +** u A user was added or received new permissions | |
| 2534 | 2552 | ** w A change to a wiki page |
| 2535 | 2553 | ** x Edits to forum posts |
| 2536 | 2554 | */ |
| 2537 | 2555 | struct EmailEvent { |
| 2538 | - int type; /* 'c', 'f', 'n', 'r', 't', 'w', 'x' */ | |
| 2556 | + int type; /* 'c', 'f', 'n', 'r', 't', 'u', 'w', 'x' */ | |
| 2539 | 2557 | int needMod; /* Pending moderator approval */ |
| 2540 | 2558 | Blob hdr; /* Header content, for forum entries */ |
| 2541 | 2559 | Blob txt; /* Text description to appear in an alert */ |
| 2542 | 2560 | char *zFromName; /* Human name of the sender */ |
| 2543 | 2561 | char *zPriors; /* Upthread sender IDs for forum posts */ |
| @@ -2932,10 +2950,11 @@ | ||
| 2932 | 2950 | ); |
| 2933 | 2951 | if( strchr(zSub, 'a') ) blob_appendf(pBody, " * Announcements\n"); |
| 2934 | 2952 | if( strchr(zSub, 'c') ) blob_appendf(pBody, " * Check-ins\n"); |
| 2935 | 2953 | if( strchr(zSub, 'f') ) blob_appendf(pBody, " * Forum posts\n"); |
| 2936 | 2954 | if( strchr(zSub, 't') ) blob_appendf(pBody, " * Ticket changes\n"); |
| 2955 | + if( strchr(zSub, 'u') ) blob_appendf(pBody, " * User permission elevation\n"); | |
| 2937 | 2956 | if( strchr(zSub, 'w') ) blob_appendf(pBody, " * Wiki changes\n"); |
| 2938 | 2957 | blob_appendf(pBody, "\n" |
| 2939 | 2958 | "If you take no action, your subscription will expire and you will be\n" |
| 2940 | 2959 | "unsubscribed in about %d days. To make other changes or to unsubscribe\n" |
| 2941 | 2960 | "immediately, visit the following webpage:\n\n" |
| @@ -3144,10 +3163,11 @@ | ||
| 3144 | 3163 | switch( p->type ){ |
| 3145 | 3164 | case 'x': case 'f': |
| 3146 | 3165 | case 'n': case 'r': xType = '5'; break; |
| 3147 | 3166 | case 't': xType = 'q'; break; |
| 3148 | 3167 | case 'w': xType = 'l'; break; |
| 3168 | + /* Note: case 'u' is not handled here */ | |
| 3149 | 3169 | } |
| 3150 | 3170 | if( strchr(zCap,xType)==0 ) continue; |
| 3151 | 3171 | } |
| 3152 | 3172 | }else if( strchr(zCap,'s')!=0 || strchr(zCap,'a')!=0 ){ |
| 3153 | 3173 | /* Setup and admin users can get any notification that does not |
| @@ -3160,10 +3180,11 @@ | ||
| 3160 | 3180 | case 'c': xType = 'o'; break; |
| 3161 | 3181 | case 'x': case 'f': |
| 3162 | 3182 | case 'n': case 'r': xType = '2'; break; |
| 3163 | 3183 | case 't': xType = 'r'; break; |
| 3164 | 3184 | case 'w': xType = 'j'; break; |
| 3185 | + /* Note: case 'u' is not handled here */ | |
| 3165 | 3186 | } |
| 3166 | 3187 | if( strchr(zCap,xType)==0 ) continue; |
| 3167 | 3188 | } |
| 3168 | 3189 | if( blob_size(&p->hdr)>0 ){ |
| 3169 | 3190 | /* This alert should be sent as a separate email */ |
| 3170 | 3191 |
| --- src/alerts.c | |
| +++ src/alerts.c | |
| @@ -51,10 +51,11 @@ | |
| 51 | @ -- f - Forum posts |
| 52 | @ -- k - ** Special: Unsubscribed using /oneclickunsub |
| 53 | @ -- n - New forum threads |
| 54 | @ -- r - Replies to my own forum posts |
| 55 | @ -- t - Ticket changes |
| 56 | @ -- w - Wiki changes |
| 57 | @ -- x - Edits to forum posts |
| 58 | @ -- Probably different codes will be added in the future. In the future |
| 59 | @ -- we might also add a separate table that allows subscribing to email |
| 60 | @ -- notifications for specific branches or tags or tickets. |
| @@ -1133,11 +1134,11 @@ | |
| 1133 | ** designated host and port and all times. |
| 1134 | */ |
| 1135 | |
| 1136 | |
| 1137 | /* |
| 1138 | ** COMMAND: alerts* |
| 1139 | ** |
| 1140 | ** Usage: %fossil alerts SUBCOMMAND ARGS... |
| 1141 | ** |
| 1142 | ** Subcommands: |
| 1143 | ** |
| @@ -1258,11 +1259,11 @@ | |
| 1258 | g.argc = 3; |
| 1259 | } |
| 1260 | pSetting = setting_info(&nSetting); |
| 1261 | for(; nSetting>0; nSetting--, pSetting++ ){ |
| 1262 | if( strncmp(pSetting->name,"email-",6)!=0 ) continue; |
| 1263 | print_setting(pSetting, 0); |
| 1264 | } |
| 1265 | }else |
| 1266 | if( strncmp(zCmd, "status", nCmd)==0 ){ |
| 1267 | Stmt q; |
| 1268 | int iCutoff; |
| @@ -1273,11 +1274,11 @@ | |
| 1273 | verify_all_options(); |
| 1274 | if( g.argc!=3 ) usage("status"); |
| 1275 | pSetting = setting_info(&nSetting); |
| 1276 | for(; nSetting>0; nSetting--, pSetting++ ){ |
| 1277 | if( strncmp(pSetting->name,"email-",6)!=0 ) continue; |
| 1278 | print_setting(pSetting, 0); |
| 1279 | } |
| 1280 | n = db_int(0,"SELECT count(*) FROM pending_alert WHERE NOT sentSep"); |
| 1281 | fossil_print(zFmt/*works-like:"%s%d"*/, "pending-alerts", n); |
| 1282 | n = db_int(0,"SELECT count(*) FROM pending_alert WHERE NOT sentDigest"); |
| 1283 | fossil_print(zFmt/*works-like:"%s%d"*/, "pending-digest-alerts", n); |
| @@ -1563,10 +1564,11 @@ | |
| 1563 | if( g.perm.Read && PB("sc") ) ssub[nsub++] = 'c'; |
| 1564 | if( g.perm.RdForum && PB("sf") ) ssub[nsub++] = 'f'; |
| 1565 | if( g.perm.RdForum && PB("sn") ) ssub[nsub++] = 'n'; |
| 1566 | if( g.perm.RdForum && PB("sr") ) ssub[nsub++] = 'r'; |
| 1567 | if( g.perm.RdTkt && PB("st") ) ssub[nsub++] = 't'; |
| 1568 | if( g.perm.RdWiki && PB("sw") ) ssub[nsub++] = 'w'; |
| 1569 | if( g.perm.RdForum && PB("sx") ) ssub[nsub++] = 'x'; |
| 1570 | ssub[nsub] = 0; |
| 1571 | zCode = db_text(0, |
| 1572 | "INSERT INTO subscriber(semail,suname," |
| @@ -1627,10 +1629,11 @@ | |
| 1627 | if( g.perm.Read ) cgi_set_parameter_nocopy("sc","1",1); |
| 1628 | if( g.perm.RdForum ) cgi_set_parameter_nocopy("sf","1",1); |
| 1629 | if( g.perm.RdForum ) cgi_set_parameter_nocopy("sn","1",1); |
| 1630 | if( g.perm.RdForum ) cgi_set_parameter_nocopy("sr","1",1); |
| 1631 | if( g.perm.RdTkt ) cgi_set_parameter_nocopy("st","1",1); |
| 1632 | if( g.perm.RdWiki ) cgi_set_parameter_nocopy("sw","1",1); |
| 1633 | } |
| 1634 | @ <p>To receive email notifications for changes to this |
| 1635 | @ repository, fill out the form below and press the "Submit" button.</p> |
| 1636 | form_begin(0, "%R/subscribe"); |
| @@ -1699,10 +1702,14 @@ | |
| 1699 | } |
| 1700 | if( g.perm.RdWiki ){ |
| 1701 | @ <label><input type="checkbox" name="sw" %s(PCK("sw"))> \ |
| 1702 | @ Wiki</label><br> |
| 1703 | } |
| 1704 | di = PB("di"); |
| 1705 | @ </td></tr> |
| 1706 | @ <tr> |
| 1707 | @ <td class="form_label">Delivery:</td> |
| 1708 | @ <td><select size="1" name="di"> |
| @@ -1820,11 +1827,11 @@ | |
| 1820 | ** verifying the email address. |
| 1821 | */ |
| 1822 | void alert_page(void){ |
| 1823 | const char *zName = 0; /* Value of the name= query parameter */ |
| 1824 | Stmt q; /* For querying the database */ |
| 1825 | int sa, sc, sf, st, sw, sx; /* Types of notifications requested */ |
| 1826 | int sn, sr; |
| 1827 | int sdigest = 0, sdonotcall = 0, sverified = 0; /* Other fields */ |
| 1828 | int isLogin; /* True if logged in as an individual */ |
| 1829 | const char *ssub = 0; /* Subscription flags */ |
| 1830 | const char *semail = 0; /* Email address */ |
| @@ -1881,10 +1888,11 @@ | |
| 1881 | if( g.perm.Read && PB("sc") ) newSsub[nsub++] = 'c'; |
| 1882 | if( g.perm.RdForum && PB("sf") ) newSsub[nsub++] = 'f'; |
| 1883 | if( g.perm.RdForum && PB("sn") ) newSsub[nsub++] = 'n'; |
| 1884 | if( g.perm.RdForum && PB("sr") ) newSsub[nsub++] = 'r'; |
| 1885 | if( g.perm.RdTkt && PB("st") ) newSsub[nsub++] = 't'; |
| 1886 | if( g.perm.RdWiki && PB("sw") ) newSsub[nsub++] = 'w'; |
| 1887 | if( g.perm.RdForum && PB("sx") ) newSsub[nsub++] = 'x'; |
| 1888 | newSsub[nsub] = 0; |
| 1889 | ssub = newSsub; |
| 1890 | blob_init(&update, "UPDATE subscriber SET", -1); |
| @@ -1979,10 +1987,11 @@ | |
| 1979 | sc = strchr(ssub,'c')!=0; |
| 1980 | sf = strchr(ssub,'f')!=0; |
| 1981 | sn = strchr(ssub,'n')!=0; |
| 1982 | sr = strchr(ssub,'r')!=0; |
| 1983 | st = strchr(ssub,'t')!=0; |
| 1984 | sw = strchr(ssub,'w')!=0; |
| 1985 | sx = strchr(ssub,'x')!=0; |
| 1986 | smip = db_column_text(&q, 5); |
| 1987 | mtime = db_column_text(&q, 7); |
| 1988 | sctime = db_column_text(&q, 8); |
| @@ -2097,11 +2106,19 @@ | |
| 2097 | @ <label><input type="checkbox" name="st" %s(st?"checked":"")>\ |
| 2098 | @ Ticket changes</label><br> |
| 2099 | } |
| 2100 | if( g.perm.RdWiki ){ |
| 2101 | @ <label><input type="checkbox" name="sw" %s(sw?"checked":"")>\ |
| 2102 | @ Wiki</label> |
| 2103 | } |
| 2104 | @ </td></tr> |
| 2105 | if( strchr(ssub,'k')!=0 ){ |
| 2106 | @ <tr><td></td><td> ↑ |
| 2107 | @ Note: User did a one-click unsubscribe</td></tr> |
| @@ -2529,15 +2546,16 @@ | |
| 2529 | ** f An original forum post |
| 2530 | ** n New forum threads |
| 2531 | ** r Replies to my forum posts |
| 2532 | ** x An edit to a prior forum post |
| 2533 | ** t A new ticket or a change to an existing ticket |
| 2534 | ** w A change to a wiki page |
| 2535 | ** x Edits to forum posts |
| 2536 | */ |
| 2537 | struct EmailEvent { |
| 2538 | int type; /* 'c', 'f', 'n', 'r', 't', 'w', 'x' */ |
| 2539 | int needMod; /* Pending moderator approval */ |
| 2540 | Blob hdr; /* Header content, for forum entries */ |
| 2541 | Blob txt; /* Text description to appear in an alert */ |
| 2542 | char *zFromName; /* Human name of the sender */ |
| 2543 | char *zPriors; /* Upthread sender IDs for forum posts */ |
| @@ -2932,10 +2950,11 @@ | |
| 2932 | ); |
| 2933 | if( strchr(zSub, 'a') ) blob_appendf(pBody, " * Announcements\n"); |
| 2934 | if( strchr(zSub, 'c') ) blob_appendf(pBody, " * Check-ins\n"); |
| 2935 | if( strchr(zSub, 'f') ) blob_appendf(pBody, " * Forum posts\n"); |
| 2936 | if( strchr(zSub, 't') ) blob_appendf(pBody, " * Ticket changes\n"); |
| 2937 | if( strchr(zSub, 'w') ) blob_appendf(pBody, " * Wiki changes\n"); |
| 2938 | blob_appendf(pBody, "\n" |
| 2939 | "If you take no action, your subscription will expire and you will be\n" |
| 2940 | "unsubscribed in about %d days. To make other changes or to unsubscribe\n" |
| 2941 | "immediately, visit the following webpage:\n\n" |
| @@ -3144,10 +3163,11 @@ | |
| 3144 | switch( p->type ){ |
| 3145 | case 'x': case 'f': |
| 3146 | case 'n': case 'r': xType = '5'; break; |
| 3147 | case 't': xType = 'q'; break; |
| 3148 | case 'w': xType = 'l'; break; |
| 3149 | } |
| 3150 | if( strchr(zCap,xType)==0 ) continue; |
| 3151 | } |
| 3152 | }else if( strchr(zCap,'s')!=0 || strchr(zCap,'a')!=0 ){ |
| 3153 | /* Setup and admin users can get any notification that does not |
| @@ -3160,10 +3180,11 @@ | |
| 3160 | case 'c': xType = 'o'; break; |
| 3161 | case 'x': case 'f': |
| 3162 | case 'n': case 'r': xType = '2'; break; |
| 3163 | case 't': xType = 'r'; break; |
| 3164 | case 'w': xType = 'j'; break; |
| 3165 | } |
| 3166 | if( strchr(zCap,xType)==0 ) continue; |
| 3167 | } |
| 3168 | if( blob_size(&p->hdr)>0 ){ |
| 3169 | /* This alert should be sent as a separate email */ |
| 3170 |
| --- src/alerts.c | |
| +++ src/alerts.c | |
| @@ -51,10 +51,11 @@ | |
| 51 | @ -- f - Forum posts |
| 52 | @ -- k - ** Special: Unsubscribed using /oneclickunsub |
| 53 | @ -- n - New forum threads |
| 54 | @ -- r - Replies to my own forum posts |
| 55 | @ -- t - Ticket changes |
| 56 | @ -- u - Elevation of users' permissions (admins only) |
| 57 | @ -- w - Wiki changes |
| 58 | @ -- x - Edits to forum posts |
| 59 | @ -- Probably different codes will be added in the future. In the future |
| 60 | @ -- we might also add a separate table that allows subscribing to email |
| 61 | @ -- notifications for specific branches or tags or tickets. |
| @@ -1133,11 +1134,11 @@ | |
| 1134 | ** designated host and port and all times. |
| 1135 | */ |
| 1136 | |
| 1137 | |
| 1138 | /* |
| 1139 | ** COMMAND: alerts* abbrv-subcom |
| 1140 | ** |
| 1141 | ** Usage: %fossil alerts SUBCOMMAND ARGS... |
| 1142 | ** |
| 1143 | ** Subcommands: |
| 1144 | ** |
| @@ -1258,11 +1259,11 @@ | |
| 1259 | g.argc = 3; |
| 1260 | } |
| 1261 | pSetting = setting_info(&nSetting); |
| 1262 | for(; nSetting>0; nSetting--, pSetting++ ){ |
| 1263 | if( strncmp(pSetting->name,"email-",6)!=0 ) continue; |
| 1264 | print_setting(pSetting, 0, 0); |
| 1265 | } |
| 1266 | }else |
| 1267 | if( strncmp(zCmd, "status", nCmd)==0 ){ |
| 1268 | Stmt q; |
| 1269 | int iCutoff; |
| @@ -1273,11 +1274,11 @@ | |
| 1274 | verify_all_options(); |
| 1275 | if( g.argc!=3 ) usage("status"); |
| 1276 | pSetting = setting_info(&nSetting); |
| 1277 | for(; nSetting>0; nSetting--, pSetting++ ){ |
| 1278 | if( strncmp(pSetting->name,"email-",6)!=0 ) continue; |
| 1279 | print_setting(pSetting, 0, 0); |
| 1280 | } |
| 1281 | n = db_int(0,"SELECT count(*) FROM pending_alert WHERE NOT sentSep"); |
| 1282 | fossil_print(zFmt/*works-like:"%s%d"*/, "pending-alerts", n); |
| 1283 | n = db_int(0,"SELECT count(*) FROM pending_alert WHERE NOT sentDigest"); |
| 1284 | fossil_print(zFmt/*works-like:"%s%d"*/, "pending-digest-alerts", n); |
| @@ -1563,10 +1564,11 @@ | |
| 1564 | if( g.perm.Read && PB("sc") ) ssub[nsub++] = 'c'; |
| 1565 | if( g.perm.RdForum && PB("sf") ) ssub[nsub++] = 'f'; |
| 1566 | if( g.perm.RdForum && PB("sn") ) ssub[nsub++] = 'n'; |
| 1567 | if( g.perm.RdForum && PB("sr") ) ssub[nsub++] = 'r'; |
| 1568 | if( g.perm.RdTkt && PB("st") ) ssub[nsub++] = 't'; |
| 1569 | if( g.perm.Admin && PB("su") ) ssub[nsub++] = 'u'; |
| 1570 | if( g.perm.RdWiki && PB("sw") ) ssub[nsub++] = 'w'; |
| 1571 | if( g.perm.RdForum && PB("sx") ) ssub[nsub++] = 'x'; |
| 1572 | ssub[nsub] = 0; |
| 1573 | zCode = db_text(0, |
| 1574 | "INSERT INTO subscriber(semail,suname," |
| @@ -1627,10 +1629,11 @@ | |
| 1629 | if( g.perm.Read ) cgi_set_parameter_nocopy("sc","1",1); |
| 1630 | if( g.perm.RdForum ) cgi_set_parameter_nocopy("sf","1",1); |
| 1631 | if( g.perm.RdForum ) cgi_set_parameter_nocopy("sn","1",1); |
| 1632 | if( g.perm.RdForum ) cgi_set_parameter_nocopy("sr","1",1); |
| 1633 | if( g.perm.RdTkt ) cgi_set_parameter_nocopy("st","1",1); |
| 1634 | if( g.perm.Admin ) cgi_set_parameter_nocopy("su","1",1); |
| 1635 | if( g.perm.RdWiki ) cgi_set_parameter_nocopy("sw","1",1); |
| 1636 | } |
| 1637 | @ <p>To receive email notifications for changes to this |
| 1638 | @ repository, fill out the form below and press the "Submit" button.</p> |
| 1639 | form_begin(0, "%R/subscribe"); |
| @@ -1699,10 +1702,14 @@ | |
| 1702 | } |
| 1703 | if( g.perm.RdWiki ){ |
| 1704 | @ <label><input type="checkbox" name="sw" %s(PCK("sw"))> \ |
| 1705 | @ Wiki</label><br> |
| 1706 | } |
| 1707 | if( g.perm.Admin ){ |
| 1708 | @ <label><input type="checkbox" name="su" %s(PCK("su"))> \ |
| 1709 | @ User permission elevation</label> |
| 1710 | } |
| 1711 | di = PB("di"); |
| 1712 | @ </td></tr> |
| 1713 | @ <tr> |
| 1714 | @ <td class="form_label">Delivery:</td> |
| 1715 | @ <td><select size="1" name="di"> |
| @@ -1820,11 +1827,11 @@ | |
| 1827 | ** verifying the email address. |
| 1828 | */ |
| 1829 | void alert_page(void){ |
| 1830 | const char *zName = 0; /* Value of the name= query parameter */ |
| 1831 | Stmt q; /* For querying the database */ |
| 1832 | int sa, sc, sf, st, su, sw, sx; /* Types of notifications requested */ |
| 1833 | int sn, sr; |
| 1834 | int sdigest = 0, sdonotcall = 0, sverified = 0; /* Other fields */ |
| 1835 | int isLogin; /* True if logged in as an individual */ |
| 1836 | const char *ssub = 0; /* Subscription flags */ |
| 1837 | const char *semail = 0; /* Email address */ |
| @@ -1881,10 +1888,11 @@ | |
| 1888 | if( g.perm.Read && PB("sc") ) newSsub[nsub++] = 'c'; |
| 1889 | if( g.perm.RdForum && PB("sf") ) newSsub[nsub++] = 'f'; |
| 1890 | if( g.perm.RdForum && PB("sn") ) newSsub[nsub++] = 'n'; |
| 1891 | if( g.perm.RdForum && PB("sr") ) newSsub[nsub++] = 'r'; |
| 1892 | if( g.perm.RdTkt && PB("st") ) newSsub[nsub++] = 't'; |
| 1893 | if( g.perm.Admin && PB("su") ) newSsub[nsub++] = 'u'; |
| 1894 | if( g.perm.RdWiki && PB("sw") ) newSsub[nsub++] = 'w'; |
| 1895 | if( g.perm.RdForum && PB("sx") ) newSsub[nsub++] = 'x'; |
| 1896 | newSsub[nsub] = 0; |
| 1897 | ssub = newSsub; |
| 1898 | blob_init(&update, "UPDATE subscriber SET", -1); |
| @@ -1979,10 +1987,11 @@ | |
| 1987 | sc = strchr(ssub,'c')!=0; |
| 1988 | sf = strchr(ssub,'f')!=0; |
| 1989 | sn = strchr(ssub,'n')!=0; |
| 1990 | sr = strchr(ssub,'r')!=0; |
| 1991 | st = strchr(ssub,'t')!=0; |
| 1992 | su = strchr(ssub,'u')!=0; |
| 1993 | sw = strchr(ssub,'w')!=0; |
| 1994 | sx = strchr(ssub,'x')!=0; |
| 1995 | smip = db_column_text(&q, 5); |
| 1996 | mtime = db_column_text(&q, 7); |
| 1997 | sctime = db_column_text(&q, 8); |
| @@ -2097,11 +2106,19 @@ | |
| 2106 | @ <label><input type="checkbox" name="st" %s(st?"checked":"")>\ |
| 2107 | @ Ticket changes</label><br> |
| 2108 | } |
| 2109 | if( g.perm.RdWiki ){ |
| 2110 | @ <label><input type="checkbox" name="sw" %s(sw?"checked":"")>\ |
| 2111 | @ Wiki</label><br> |
| 2112 | } |
| 2113 | if( g.perm.Admin ){ |
| 2114 | /* Corner-case bug: if an admin assigns 'u' to a non-admin, that |
| 2115 | ** subscription will get removed if the user later edits their |
| 2116 | ** subscriptions, as non-admins are not permitted to add that |
| 2117 | ** subscription. */ |
| 2118 | @ <label><input type="checkbox" name="su" %s(su?"checked":"")>\ |
| 2119 | @ User permission elevation</label> |
| 2120 | } |
| 2121 | @ </td></tr> |
| 2122 | if( strchr(ssub,'k')!=0 ){ |
| 2123 | @ <tr><td></td><td> ↑ |
| 2124 | @ Note: User did a one-click unsubscribe</td></tr> |
| @@ -2529,15 +2546,16 @@ | |
| 2546 | ** f An original forum post |
| 2547 | ** n New forum threads |
| 2548 | ** r Replies to my forum posts |
| 2549 | ** x An edit to a prior forum post |
| 2550 | ** t A new ticket or a change to an existing ticket |
| 2551 | ** u A user was added or received new permissions |
| 2552 | ** w A change to a wiki page |
| 2553 | ** x Edits to forum posts |
| 2554 | */ |
| 2555 | struct EmailEvent { |
| 2556 | int type; /* 'c', 'f', 'n', 'r', 't', 'u', 'w', 'x' */ |
| 2557 | int needMod; /* Pending moderator approval */ |
| 2558 | Blob hdr; /* Header content, for forum entries */ |
| 2559 | Blob txt; /* Text description to appear in an alert */ |
| 2560 | char *zFromName; /* Human name of the sender */ |
| 2561 | char *zPriors; /* Upthread sender IDs for forum posts */ |
| @@ -2932,10 +2950,11 @@ | |
| 2950 | ); |
| 2951 | if( strchr(zSub, 'a') ) blob_appendf(pBody, " * Announcements\n"); |
| 2952 | if( strchr(zSub, 'c') ) blob_appendf(pBody, " * Check-ins\n"); |
| 2953 | if( strchr(zSub, 'f') ) blob_appendf(pBody, " * Forum posts\n"); |
| 2954 | if( strchr(zSub, 't') ) blob_appendf(pBody, " * Ticket changes\n"); |
| 2955 | if( strchr(zSub, 'u') ) blob_appendf(pBody, " * User permission elevation\n"); |
| 2956 | if( strchr(zSub, 'w') ) blob_appendf(pBody, " * Wiki changes\n"); |
| 2957 | blob_appendf(pBody, "\n" |
| 2958 | "If you take no action, your subscription will expire and you will be\n" |
| 2959 | "unsubscribed in about %d days. To make other changes or to unsubscribe\n" |
| 2960 | "immediately, visit the following webpage:\n\n" |
| @@ -3144,10 +3163,11 @@ | |
| 3163 | switch( p->type ){ |
| 3164 | case 'x': case 'f': |
| 3165 | case 'n': case 'r': xType = '5'; break; |
| 3166 | case 't': xType = 'q'; break; |
| 3167 | case 'w': xType = 'l'; break; |
| 3168 | /* Note: case 'u' is not handled here */ |
| 3169 | } |
| 3170 | if( strchr(zCap,xType)==0 ) continue; |
| 3171 | } |
| 3172 | }else if( strchr(zCap,'s')!=0 || strchr(zCap,'a')!=0 ){ |
| 3173 | /* Setup and admin users can get any notification that does not |
| @@ -3160,10 +3180,11 @@ | |
| 3180 | case 'c': xType = 'o'; break; |
| 3181 | case 'x': case 'f': |
| 3182 | case 'n': case 'r': xType = '2'; break; |
| 3183 | case 't': xType = 'r'; break; |
| 3184 | case 'w': xType = 'j'; break; |
| 3185 | /* Note: case 'u' is not handled here */ |
| 3186 | } |
| 3187 | if( strchr(zCap,xType)==0 ) continue; |
| 3188 | } |
| 3189 | if( blob_size(&p->hdr)>0 ){ |
| 3190 | /* This alert should be sent as a separate email */ |
| 3191 |
+2
-1
| --- src/allrepo.c | ||
| +++ src/allrepo.c | ||
| @@ -51,11 +51,11 @@ | ||
| 51 | 51 | blob_appendf(pExtra, " %s", g.argv[i]); |
| 52 | 52 | } |
| 53 | 53 | } |
| 54 | 54 | |
| 55 | 55 | /* |
| 56 | -** COMMAND: all | |
| 56 | +** COMMAND: all abbrv-subcom | |
| 57 | 57 | ** |
| 58 | 58 | ** Usage: %fossil all SUBCOMMAND ... |
| 59 | 59 | ** |
| 60 | 60 | ** The ~/.fossil file records the location of all repositories for a |
| 61 | 61 | ** user. This command performs certain operations on all repositories |
| @@ -316,10 +316,11 @@ | ||
| 316 | 316 | zCmd = "repack"; |
| 317 | 317 | }else if( fossil_strcmp(zCmd, "set")==0 |
| 318 | 318 | || fossil_strcmp(zCmd, "setting")==0 |
| 319 | 319 | || fossil_strcmp(zCmd, "settings")==0 ){ |
| 320 | 320 | zCmd = "settings -R"; |
| 321 | + collect_argument(&extra, "changed", 0); | |
| 321 | 322 | collect_argv(&extra, 3); |
| 322 | 323 | }else if( fossil_strcmp(zCmd, "unset")==0 ){ |
| 323 | 324 | zCmd = "unset -R"; |
| 324 | 325 | collect_argv(&extra, 3); |
| 325 | 326 | }else if( fossil_strcmp(zCmd, "fts-config")==0 ){ |
| 326 | 327 |
| --- src/allrepo.c | |
| +++ src/allrepo.c | |
| @@ -51,11 +51,11 @@ | |
| 51 | blob_appendf(pExtra, " %s", g.argv[i]); |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | /* |
| 56 | ** COMMAND: all |
| 57 | ** |
| 58 | ** Usage: %fossil all SUBCOMMAND ... |
| 59 | ** |
| 60 | ** The ~/.fossil file records the location of all repositories for a |
| 61 | ** user. This command performs certain operations on all repositories |
| @@ -316,10 +316,11 @@ | |
| 316 | zCmd = "repack"; |
| 317 | }else if( fossil_strcmp(zCmd, "set")==0 |
| 318 | || fossil_strcmp(zCmd, "setting")==0 |
| 319 | || fossil_strcmp(zCmd, "settings")==0 ){ |
| 320 | zCmd = "settings -R"; |
| 321 | collect_argv(&extra, 3); |
| 322 | }else if( fossil_strcmp(zCmd, "unset")==0 ){ |
| 323 | zCmd = "unset -R"; |
| 324 | collect_argv(&extra, 3); |
| 325 | }else if( fossil_strcmp(zCmd, "fts-config")==0 ){ |
| 326 |
| --- src/allrepo.c | |
| +++ src/allrepo.c | |
| @@ -51,11 +51,11 @@ | |
| 51 | blob_appendf(pExtra, " %s", g.argv[i]); |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | /* |
| 56 | ** COMMAND: all abbrv-subcom |
| 57 | ** |
| 58 | ** Usage: %fossil all SUBCOMMAND ... |
| 59 | ** |
| 60 | ** The ~/.fossil file records the location of all repositories for a |
| 61 | ** user. This command performs certain operations on all repositories |
| @@ -316,10 +316,11 @@ | |
| 316 | zCmd = "repack"; |
| 317 | }else if( fossil_strcmp(zCmd, "set")==0 |
| 318 | || fossil_strcmp(zCmd, "setting")==0 |
| 319 | || fossil_strcmp(zCmd, "settings")==0 ){ |
| 320 | zCmd = "settings -R"; |
| 321 | collect_argument(&extra, "changed", 0); |
| 322 | collect_argv(&extra, 3); |
| 323 | }else if( fossil_strcmp(zCmd, "unset")==0 ){ |
| 324 | zCmd = "unset -R"; |
| 325 | collect_argv(&extra, 3); |
| 326 | }else if( fossil_strcmp(zCmd, "fts-config")==0 ){ |
| 327 |
+2
-2
| --- src/backlink.c | ||
| +++ src/backlink.c | ||
| @@ -431,13 +431,13 @@ | ||
| 431 | 431 | blob_reset(&in); |
| 432 | 432 | } |
| 433 | 433 | |
| 434 | 434 | |
| 435 | 435 | /* |
| 436 | -** COMMAND: test-wiki-relink | |
| 436 | +** COMMAND: test-relink-wiki | |
| 437 | 437 | ** |
| 438 | -** Usage: %fossil test-wiki-relink WIKI-PAGE-NAME | |
| 438 | +** Usage: %fossil test-relink-wiki WIKI-PAGE-NAME | |
| 439 | 439 | ** |
| 440 | 440 | ** Run the backlink_wiki_refresh() procedure on the wiki page |
| 441 | 441 | ** named. WIKI-PAGE-NAME can be a glob pattern or a prefix |
| 442 | 442 | ** of the wiki page. |
| 443 | 443 | */ |
| 444 | 444 |
| --- src/backlink.c | |
| +++ src/backlink.c | |
| @@ -431,13 +431,13 @@ | |
| 431 | blob_reset(&in); |
| 432 | } |
| 433 | |
| 434 | |
| 435 | /* |
| 436 | ** COMMAND: test-wiki-relink |
| 437 | ** |
| 438 | ** Usage: %fossil test-wiki-relink WIKI-PAGE-NAME |
| 439 | ** |
| 440 | ** Run the backlink_wiki_refresh() procedure on the wiki page |
| 441 | ** named. WIKI-PAGE-NAME can be a glob pattern or a prefix |
| 442 | ** of the wiki page. |
| 443 | */ |
| 444 |
| --- src/backlink.c | |
| +++ src/backlink.c | |
| @@ -431,13 +431,13 @@ | |
| 431 | blob_reset(&in); |
| 432 | } |
| 433 | |
| 434 | |
| 435 | /* |
| 436 | ** COMMAND: test-relink-wiki |
| 437 | ** |
| 438 | ** Usage: %fossil test-relink-wiki WIKI-PAGE-NAME |
| 439 | ** |
| 440 | ** Run the backlink_wiki_refresh() procedure on the wiki page |
| 441 | ** named. WIKI-PAGE-NAME can be a glob pattern or a prefix |
| 442 | ** of the wiki page. |
| 443 | */ |
| 444 |
+4
-4
| --- src/bisect.c | ||
| +++ src/bisect.c | ||
| @@ -37,13 +37,13 @@ | ||
| 37 | 37 | void bisect_path(void){ |
| 38 | 38 | PathNode *p; |
| 39 | 39 | bisect.bad = db_lget_int("bisect-bad", 0); |
| 40 | 40 | bisect.good = db_lget_int("bisect-good", 0); |
| 41 | 41 | if( bisect.good>0 && bisect.bad==0 ){ |
| 42 | - path_shortest(bisect.good, bisect.good, 0, 0, 0); | |
| 42 | + path_shortest(bisect.good, bisect.good, 0, 0, 0, 0); | |
| 43 | 43 | }else if( bisect.bad>0 && bisect.good==0 ){ |
| 44 | - path_shortest(bisect.bad, bisect.bad, 0, 0, 0); | |
| 44 | + path_shortest(bisect.bad, bisect.bad, 0, 0, 0, 0); | |
| 45 | 45 | }else if( bisect.bad==0 && bisect.good==0 ){ |
| 46 | 46 | fossil_fatal("neither \"good\" nor \"bad\" versions have been identified"); |
| 47 | 47 | }else{ |
| 48 | 48 | Bag skip; |
| 49 | 49 | int bDirect = bisect_option("direct-only"); |
| @@ -55,11 +55,11 @@ | ||
| 55 | 55 | if( blob_str(&id)[0]=='s' ){ |
| 56 | 56 | bag_insert(&skip, atoi(blob_str(&id)+1)); |
| 57 | 57 | } |
| 58 | 58 | } |
| 59 | 59 | blob_reset(&log); |
| 60 | - p = path_shortest(bisect.good, bisect.bad, bDirect, 0, &skip); | |
| 60 | + p = path_shortest(bisect.good, bisect.bad, bDirect, 0, &skip, 0); | |
| 61 | 61 | bag_clear(&skip); |
| 62 | 62 | if( p==0 ){ |
| 63 | 63 | char *zBad = db_text(0,"SELECT uuid FROM blob WHERE rid=%d",bisect.bad); |
| 64 | 64 | char *zGood = db_text(0,"SELECT uuid FROM blob WHERE rid=%d",bisect.good); |
| 65 | 65 | fossil_fatal("no path from good ([%S]) to bad ([%S]) or back", |
| @@ -292,11 +292,11 @@ | ||
| 292 | 292 | if( iCurrent>0 ){ |
| 293 | 293 | bisect_log_append(&ins, ++cnt, "CURRENT", iCurrent); |
| 294 | 294 | } |
| 295 | 295 | if( bDetail && lastGood>0 && lastBad>0 ){ |
| 296 | 296 | PathNode *p; |
| 297 | - p = path_shortest(lastGood, lastBad, bisect_option("direct-only"),0, 0); | |
| 297 | + p = path_shortest(lastGood, lastBad, bisect_option("direct-only"),0, 0, 0); | |
| 298 | 298 | while( p ){ |
| 299 | 299 | bisect_log_append(&ins, ++cnt, 0, p->rid); |
| 300 | 300 | p = p->u.pTo; |
| 301 | 301 | } |
| 302 | 302 | path_reset(); |
| 303 | 303 |
| --- src/bisect.c | |
| +++ src/bisect.c | |
| @@ -37,13 +37,13 @@ | |
| 37 | void bisect_path(void){ |
| 38 | PathNode *p; |
| 39 | bisect.bad = db_lget_int("bisect-bad", 0); |
| 40 | bisect.good = db_lget_int("bisect-good", 0); |
| 41 | if( bisect.good>0 && bisect.bad==0 ){ |
| 42 | path_shortest(bisect.good, bisect.good, 0, 0, 0); |
| 43 | }else if( bisect.bad>0 && bisect.good==0 ){ |
| 44 | path_shortest(bisect.bad, bisect.bad, 0, 0, 0); |
| 45 | }else if( bisect.bad==0 && bisect.good==0 ){ |
| 46 | fossil_fatal("neither \"good\" nor \"bad\" versions have been identified"); |
| 47 | }else{ |
| 48 | Bag skip; |
| 49 | int bDirect = bisect_option("direct-only"); |
| @@ -55,11 +55,11 @@ | |
| 55 | if( blob_str(&id)[0]=='s' ){ |
| 56 | bag_insert(&skip, atoi(blob_str(&id)+1)); |
| 57 | } |
| 58 | } |
| 59 | blob_reset(&log); |
| 60 | p = path_shortest(bisect.good, bisect.bad, bDirect, 0, &skip); |
| 61 | bag_clear(&skip); |
| 62 | if( p==0 ){ |
| 63 | char *zBad = db_text(0,"SELECT uuid FROM blob WHERE rid=%d",bisect.bad); |
| 64 | char *zGood = db_text(0,"SELECT uuid FROM blob WHERE rid=%d",bisect.good); |
| 65 | fossil_fatal("no path from good ([%S]) to bad ([%S]) or back", |
| @@ -292,11 +292,11 @@ | |
| 292 | if( iCurrent>0 ){ |
| 293 | bisect_log_append(&ins, ++cnt, "CURRENT", iCurrent); |
| 294 | } |
| 295 | if( bDetail && lastGood>0 && lastBad>0 ){ |
| 296 | PathNode *p; |
| 297 | p = path_shortest(lastGood, lastBad, bisect_option("direct-only"),0, 0); |
| 298 | while( p ){ |
| 299 | bisect_log_append(&ins, ++cnt, 0, p->rid); |
| 300 | p = p->u.pTo; |
| 301 | } |
| 302 | path_reset(); |
| 303 |
| --- src/bisect.c | |
| +++ src/bisect.c | |
| @@ -37,13 +37,13 @@ | |
| 37 | void bisect_path(void){ |
| 38 | PathNode *p; |
| 39 | bisect.bad = db_lget_int("bisect-bad", 0); |
| 40 | bisect.good = db_lget_int("bisect-good", 0); |
| 41 | if( bisect.good>0 && bisect.bad==0 ){ |
| 42 | path_shortest(bisect.good, bisect.good, 0, 0, 0, 0); |
| 43 | }else if( bisect.bad>0 && bisect.good==0 ){ |
| 44 | path_shortest(bisect.bad, bisect.bad, 0, 0, 0, 0); |
| 45 | }else if( bisect.bad==0 && bisect.good==0 ){ |
| 46 | fossil_fatal("neither \"good\" nor \"bad\" versions have been identified"); |
| 47 | }else{ |
| 48 | Bag skip; |
| 49 | int bDirect = bisect_option("direct-only"); |
| @@ -55,11 +55,11 @@ | |
| 55 | if( blob_str(&id)[0]=='s' ){ |
| 56 | bag_insert(&skip, atoi(blob_str(&id)+1)); |
| 57 | } |
| 58 | } |
| 59 | blob_reset(&log); |
| 60 | p = path_shortest(bisect.good, bisect.bad, bDirect, 0, &skip, 0); |
| 61 | bag_clear(&skip); |
| 62 | if( p==0 ){ |
| 63 | char *zBad = db_text(0,"SELECT uuid FROM blob WHERE rid=%d",bisect.bad); |
| 64 | char *zGood = db_text(0,"SELECT uuid FROM blob WHERE rid=%d",bisect.good); |
| 65 | fossil_fatal("no path from good ([%S]) to bad ([%S]) or back", |
| @@ -292,11 +292,11 @@ | |
| 292 | if( iCurrent>0 ){ |
| 293 | bisect_log_append(&ins, ++cnt, "CURRENT", iCurrent); |
| 294 | } |
| 295 | if( bDetail && lastGood>0 && lastBad>0 ){ |
| 296 | PathNode *p; |
| 297 | p = path_shortest(lastGood, lastBad, bisect_option("direct-only"),0, 0, 0); |
| 298 | while( p ){ |
| 299 | bisect_log_append(&ins, ++cnt, 0, p->rid); |
| 300 | p = p->u.pTo; |
| 301 | } |
| 302 | path_reset(); |
| 303 |
+1
-1
| --- src/blob.c | ||
| +++ src/blob.c | ||
| @@ -1994,11 +1994,11 @@ | ||
| 1994 | 1994 | zUtf8 = blob_str(pBlob) + bomSize; |
| 1995 | 1995 | zUtf8 = fossil_unicode_to_utf8(zUtf8); |
| 1996 | 1996 | blob_reset(pBlob); |
| 1997 | 1997 | blob_set_dynamic(pBlob, zUtf8); |
| 1998 | 1998 | }else if( useMbcs && invalid_utf8(pBlob) ){ |
| 1999 | -#if defined(_WIN32) || defined(__CYGWIN__) | |
| 1999 | +#if defined(_WIN32) | |
| 2000 | 2000 | zUtf8 = fossil_mbcs_to_utf8(blob_str(pBlob)); |
| 2001 | 2001 | blob_reset(pBlob); |
| 2002 | 2002 | blob_append(pBlob, zUtf8, -1); |
| 2003 | 2003 | fossil_mbcs_free(zUtf8); |
| 2004 | 2004 | #else |
| 2005 | 2005 |
| --- src/blob.c | |
| +++ src/blob.c | |
| @@ -1994,11 +1994,11 @@ | |
| 1994 | zUtf8 = blob_str(pBlob) + bomSize; |
| 1995 | zUtf8 = fossil_unicode_to_utf8(zUtf8); |
| 1996 | blob_reset(pBlob); |
| 1997 | blob_set_dynamic(pBlob, zUtf8); |
| 1998 | }else if( useMbcs && invalid_utf8(pBlob) ){ |
| 1999 | #if defined(_WIN32) || defined(__CYGWIN__) |
| 2000 | zUtf8 = fossil_mbcs_to_utf8(blob_str(pBlob)); |
| 2001 | blob_reset(pBlob); |
| 2002 | blob_append(pBlob, zUtf8, -1); |
| 2003 | fossil_mbcs_free(zUtf8); |
| 2004 | #else |
| 2005 |
| --- src/blob.c | |
| +++ src/blob.c | |
| @@ -1994,11 +1994,11 @@ | |
| 1994 | zUtf8 = blob_str(pBlob) + bomSize; |
| 1995 | zUtf8 = fossil_unicode_to_utf8(zUtf8); |
| 1996 | blob_reset(pBlob); |
| 1997 | blob_set_dynamic(pBlob, zUtf8); |
| 1998 | }else if( useMbcs && invalid_utf8(pBlob) ){ |
| 1999 | #if defined(_WIN32) |
| 2000 | zUtf8 = fossil_mbcs_to_utf8(blob_str(pBlob)); |
| 2001 | blob_reset(pBlob); |
| 2002 | blob_append(pBlob, zUtf8, -1); |
| 2003 | fossil_mbcs_free(zUtf8); |
| 2004 | #else |
| 2005 |
+9
-8
| --- src/branch.c | ||
| +++ src/branch.c | ||
| @@ -598,11 +598,11 @@ | ||
| 598 | 598 | ** Usage: %fossil branch SUBCOMMAND ... ?OPTIONS? |
| 599 | 599 | ** |
| 600 | 600 | ** Run various subcommands to manage branches of the open repository or |
| 601 | 601 | ** of the repository identified by the -R or --repository option. |
| 602 | 602 | ** |
| 603 | -** > fossil branch close|reopen ?OPTIONS? BRANCH-NAME ?...BRANCH-NAMES? | |
| 603 | +** > fossil branch close|reopen ?OPTIONS? BRANCH-NAME ?...BRANCH-NAMES? | |
| 604 | 604 | ** |
| 605 | 605 | ** Adds or cancels the "closed" tag to one or more branches. |
| 606 | 606 | ** It accepts arbitrary unambiguous symbolic names but |
| 607 | 607 | ** will only resolve check-in names and skips any which resolve |
| 608 | 608 | ** to non-leaf check-ins. |
| @@ -612,26 +612,26 @@ | ||
| 612 | 612 | ** to stdout |
| 613 | 613 | ** -v|--verbose Output more information |
| 614 | 614 | ** --date-override DATE DATE to use instead of 'now' |
| 615 | 615 | ** --user-override USER USER to use instead of the current default |
| 616 | 616 | ** |
| 617 | -** > fossil branch current | |
| 617 | +** > fossil branch current | |
| 618 | 618 | ** |
| 619 | 619 | ** Print the name of the branch for the current check-out |
| 620 | 620 | ** |
| 621 | -** > fossil branch hide|unhide ?OPTIONS? BRANCH-NAME ?...BRANCH-NAMES? | |
| 621 | +** > fossil branch hide|unhide ?OPTIONS? BRANCH-NAME ?...BRANCH-NAMES? | |
| 622 | 622 | ** |
| 623 | 623 | ** Adds or cancels the "hidden" tag for the specified branches or |
| 624 | 624 | ** or check-in IDs. Accepts the same options as the close |
| 625 | 625 | ** subcommand. |
| 626 | 626 | ** |
| 627 | -** > fossil branch info BRANCH-NAME | |
| 627 | +** > fossil branch info BRANCH-NAME | |
| 628 | 628 | ** |
| 629 | 629 | ** Print information about a branch |
| 630 | 630 | ** |
| 631 | -** > fossil branch list|ls ?OPTIONS? ?GLOB? | |
| 632 | -** > fossil branch lsh ?OPTIONS? ?LIMIT? | |
| 631 | +** > fossil branch list|ls ?OPTIONS? ?GLOB? | |
| 632 | +** > fossil branch lsh ?OPTIONS? ?LIMIT? | |
| 633 | 633 | ** |
| 634 | 634 | ** List all branches. |
| 635 | 635 | ** |
| 636 | 636 | ** Options: |
| 637 | 637 | ** -a|--all List all branches. Default show only open branches |
| @@ -653,11 +653,11 @@ | ||
| 653 | 653 | ** The "lsh" variant of this subcommand shows recently changed branches, |
| 654 | 654 | ** and accepts an optional LIMIT argument (defaults to 5) to cap output, |
| 655 | 655 | ** but no GLOB argument. All other options are supported, with -t being |
| 656 | 656 | ** an implied no-op. |
| 657 | 657 | ** |
| 658 | -** > fossil branch new BRANCH-NAME BASIS ?OPTIONS? | |
| 658 | +** > fossil branch new BRANCH-NAME BASIS ?OPTIONS? | |
| 659 | 659 | ** |
| 660 | 660 | ** Create a new branch BRANCH-NAME off of check-in BASIS. |
| 661 | 661 | ** |
| 662 | 662 | ** Options: |
| 663 | 663 | ** --private Branch is private (i.e., remains local) |
| @@ -870,11 +870,12 @@ | ||
| 870 | 870 | const char *zLastCkin = db_column_text(&q, 5); |
| 871 | 871 | const char *zBgClr = db_column_text(&q, 6); |
| 872 | 872 | char *zAge = human_readable_age(rNow - rMtime); |
| 873 | 873 | sqlite3_int64 iMtime = (sqlite3_int64)(rMtime*86400.0); |
| 874 | 874 | if( zMergeTo && zMergeTo[0]==0 ) zMergeTo = 0; |
| 875 | - if( zBgClr == 0 ){ | |
| 875 | + if( zBgClr ) zBgClr = reasonable_bg_color(zBgClr, 0); | |
| 876 | + if( zBgClr==0 ){ | |
| 876 | 877 | if( zBranch==0 || strcmp(zBranch,"trunk")==0 ){ |
| 877 | 878 | zBgClr = 0; |
| 878 | 879 | }else{ |
| 879 | 880 | zBgClr = hash_color(zBranch); |
| 880 | 881 | } |
| 881 | 882 |
| --- src/branch.c | |
| +++ src/branch.c | |
| @@ -598,11 +598,11 @@ | |
| 598 | ** Usage: %fossil branch SUBCOMMAND ... ?OPTIONS? |
| 599 | ** |
| 600 | ** Run various subcommands to manage branches of the open repository or |
| 601 | ** of the repository identified by the -R or --repository option. |
| 602 | ** |
| 603 | ** > fossil branch close|reopen ?OPTIONS? BRANCH-NAME ?...BRANCH-NAMES? |
| 604 | ** |
| 605 | ** Adds or cancels the "closed" tag to one or more branches. |
| 606 | ** It accepts arbitrary unambiguous symbolic names but |
| 607 | ** will only resolve check-in names and skips any which resolve |
| 608 | ** to non-leaf check-ins. |
| @@ -612,26 +612,26 @@ | |
| 612 | ** to stdout |
| 613 | ** -v|--verbose Output more information |
| 614 | ** --date-override DATE DATE to use instead of 'now' |
| 615 | ** --user-override USER USER to use instead of the current default |
| 616 | ** |
| 617 | ** > fossil branch current |
| 618 | ** |
| 619 | ** Print the name of the branch for the current check-out |
| 620 | ** |
| 621 | ** > fossil branch hide|unhide ?OPTIONS? BRANCH-NAME ?...BRANCH-NAMES? |
| 622 | ** |
| 623 | ** Adds or cancels the "hidden" tag for the specified branches or |
| 624 | ** or check-in IDs. Accepts the same options as the close |
| 625 | ** subcommand. |
| 626 | ** |
| 627 | ** > fossil branch info BRANCH-NAME |
| 628 | ** |
| 629 | ** Print information about a branch |
| 630 | ** |
| 631 | ** > fossil branch list|ls ?OPTIONS? ?GLOB? |
| 632 | ** > fossil branch lsh ?OPTIONS? ?LIMIT? |
| 633 | ** |
| 634 | ** List all branches. |
| 635 | ** |
| 636 | ** Options: |
| 637 | ** -a|--all List all branches. Default show only open branches |
| @@ -653,11 +653,11 @@ | |
| 653 | ** The "lsh" variant of this subcommand shows recently changed branches, |
| 654 | ** and accepts an optional LIMIT argument (defaults to 5) to cap output, |
| 655 | ** but no GLOB argument. All other options are supported, with -t being |
| 656 | ** an implied no-op. |
| 657 | ** |
| 658 | ** > fossil branch new BRANCH-NAME BASIS ?OPTIONS? |
| 659 | ** |
| 660 | ** Create a new branch BRANCH-NAME off of check-in BASIS. |
| 661 | ** |
| 662 | ** Options: |
| 663 | ** --private Branch is private (i.e., remains local) |
| @@ -870,11 +870,12 @@ | |
| 870 | const char *zLastCkin = db_column_text(&q, 5); |
| 871 | const char *zBgClr = db_column_text(&q, 6); |
| 872 | char *zAge = human_readable_age(rNow - rMtime); |
| 873 | sqlite3_int64 iMtime = (sqlite3_int64)(rMtime*86400.0); |
| 874 | if( zMergeTo && zMergeTo[0]==0 ) zMergeTo = 0; |
| 875 | if( zBgClr == 0 ){ |
| 876 | if( zBranch==0 || strcmp(zBranch,"trunk")==0 ){ |
| 877 | zBgClr = 0; |
| 878 | }else{ |
| 879 | zBgClr = hash_color(zBranch); |
| 880 | } |
| 881 |
| --- src/branch.c | |
| +++ src/branch.c | |
| @@ -598,11 +598,11 @@ | |
| 598 | ** Usage: %fossil branch SUBCOMMAND ... ?OPTIONS? |
| 599 | ** |
| 600 | ** Run various subcommands to manage branches of the open repository or |
| 601 | ** of the repository identified by the -R or --repository option. |
| 602 | ** |
| 603 | ** > fossil branch close|reopen ?OPTIONS? BRANCH-NAME ?...BRANCH-NAMES? |
| 604 | ** |
| 605 | ** Adds or cancels the "closed" tag to one or more branches. |
| 606 | ** It accepts arbitrary unambiguous symbolic names but |
| 607 | ** will only resolve check-in names and skips any which resolve |
| 608 | ** to non-leaf check-ins. |
| @@ -612,26 +612,26 @@ | |
| 612 | ** to stdout |
| 613 | ** -v|--verbose Output more information |
| 614 | ** --date-override DATE DATE to use instead of 'now' |
| 615 | ** --user-override USER USER to use instead of the current default |
| 616 | ** |
| 617 | ** > fossil branch current |
| 618 | ** |
| 619 | ** Print the name of the branch for the current check-out |
| 620 | ** |
| 621 | ** > fossil branch hide|unhide ?OPTIONS? BRANCH-NAME ?...BRANCH-NAMES? |
| 622 | ** |
| 623 | ** Adds or cancels the "hidden" tag for the specified branches or |
| 624 | ** or check-in IDs. Accepts the same options as the close |
| 625 | ** subcommand. |
| 626 | ** |
| 627 | ** > fossil branch info BRANCH-NAME |
| 628 | ** |
| 629 | ** Print information about a branch |
| 630 | ** |
| 631 | ** > fossil branch list|ls ?OPTIONS? ?GLOB? |
| 632 | ** > fossil branch lsh ?OPTIONS? ?LIMIT? |
| 633 | ** |
| 634 | ** List all branches. |
| 635 | ** |
| 636 | ** Options: |
| 637 | ** -a|--all List all branches. Default show only open branches |
| @@ -653,11 +653,11 @@ | |
| 653 | ** The "lsh" variant of this subcommand shows recently changed branches, |
| 654 | ** and accepts an optional LIMIT argument (defaults to 5) to cap output, |
| 655 | ** but no GLOB argument. All other options are supported, with -t being |
| 656 | ** an implied no-op. |
| 657 | ** |
| 658 | ** > fossil branch new BRANCH-NAME BASIS ?OPTIONS? |
| 659 | ** |
| 660 | ** Create a new branch BRANCH-NAME off of check-in BASIS. |
| 661 | ** |
| 662 | ** Options: |
| 663 | ** --private Branch is private (i.e., remains local) |
| @@ -870,11 +870,12 @@ | |
| 870 | const char *zLastCkin = db_column_text(&q, 5); |
| 871 | const char *zBgClr = db_column_text(&q, 6); |
| 872 | char *zAge = human_readable_age(rNow - rMtime); |
| 873 | sqlite3_int64 iMtime = (sqlite3_int64)(rMtime*86400.0); |
| 874 | if( zMergeTo && zMergeTo[0]==0 ) zMergeTo = 0; |
| 875 | if( zBgClr ) zBgClr = reasonable_bg_color(zBgClr, 0); |
| 876 | if( zBgClr==0 ){ |
| 877 | if( zBranch==0 || strcmp(zBranch,"trunk")==0 ){ |
| 878 | zBgClr = 0; |
| 879 | }else{ |
| 880 | zBgClr = hash_color(zBranch); |
| 881 | } |
| 882 |
+16
| --- src/builtin.c | ||
| +++ src/builtin.c | ||
| @@ -52,17 +52,33 @@ | ||
| 52 | 52 | return -1; |
| 53 | 53 | } |
| 54 | 54 | |
| 55 | 55 | /* |
| 56 | 56 | ** Return a pointer to built-in content |
| 57 | +** | |
| 58 | +** If the filename contains "-vNNNNNNNN" just before the final file | |
| 59 | +** suffix, where each N is a random digit, then omit that part of the | |
| 60 | +** filename before doing the lookup. The extra -vNNNNNNNN was added | |
| 61 | +** to defeat overly aggressive caching by web browsers. There must be | |
| 62 | +** at least 8 digits in NNNNNNNN but more than 8 are allowed. | |
| 57 | 63 | */ |
| 58 | 64 | const unsigned char *builtin_file(const char *zFilename, int *piSize){ |
| 59 | 65 | int i = builtin_file_index(zFilename); |
| 60 | 66 | if( i>=0 ){ |
| 61 | 67 | if( piSize ) *piSize = aBuiltinFiles[i].nByte; |
| 62 | 68 | return aBuiltinFiles[i].pData; |
| 63 | 69 | }else{ |
| 70 | + char *zV = strstr(zFilename, "-v"); | |
| 71 | + if( zV!=0 ){ | |
| 72 | + for(i=0; fossil_isdigit(zV[i+2]); i++){} | |
| 73 | + if( i>=8 && zV[i+2]=='.' ){ | |
| 74 | + char *zNew = mprintf("%.*s%s", (int)(zV-zFilename), zFilename, zV+i+2); | |
| 75 | + const unsigned char *pRes = builtin_file(zNew, piSize); | |
| 76 | + fossil_free(zNew); | |
| 77 | + return pRes; | |
| 78 | + } | |
| 79 | + } | |
| 64 | 80 | if( piSize ) *piSize = 0; |
| 65 | 81 | return 0; |
| 66 | 82 | } |
| 67 | 83 | } |
| 68 | 84 | const char *builtin_text(const char *zFilename){ |
| 69 | 85 |
| --- src/builtin.c | |
| +++ src/builtin.c | |
| @@ -52,17 +52,33 @@ | |
| 52 | return -1; |
| 53 | } |
| 54 | |
| 55 | /* |
| 56 | ** Return a pointer to built-in content |
| 57 | */ |
| 58 | const unsigned char *builtin_file(const char *zFilename, int *piSize){ |
| 59 | int i = builtin_file_index(zFilename); |
| 60 | if( i>=0 ){ |
| 61 | if( piSize ) *piSize = aBuiltinFiles[i].nByte; |
| 62 | return aBuiltinFiles[i].pData; |
| 63 | }else{ |
| 64 | if( piSize ) *piSize = 0; |
| 65 | return 0; |
| 66 | } |
| 67 | } |
| 68 | const char *builtin_text(const char *zFilename){ |
| 69 |
| --- src/builtin.c | |
| +++ src/builtin.c | |
| @@ -52,17 +52,33 @@ | |
| 52 | return -1; |
| 53 | } |
| 54 | |
| 55 | /* |
| 56 | ** Return a pointer to built-in content |
| 57 | ** |
| 58 | ** If the filename contains "-vNNNNNNNN" just before the final file |
| 59 | ** suffix, where each N is a random digit, then omit that part of the |
| 60 | ** filename before doing the lookup. The extra -vNNNNNNNN was added |
| 61 | ** to defeat overly aggressive caching by web browsers. There must be |
| 62 | ** at least 8 digits in NNNNNNNN but more than 8 are allowed. |
| 63 | */ |
| 64 | const unsigned char *builtin_file(const char *zFilename, int *piSize){ |
| 65 | int i = builtin_file_index(zFilename); |
| 66 | if( i>=0 ){ |
| 67 | if( piSize ) *piSize = aBuiltinFiles[i].nByte; |
| 68 | return aBuiltinFiles[i].pData; |
| 69 | }else{ |
| 70 | char *zV = strstr(zFilename, "-v"); |
| 71 | if( zV!=0 ){ |
| 72 | for(i=0; fossil_isdigit(zV[i+2]); i++){} |
| 73 | if( i>=8 && zV[i+2]=='.' ){ |
| 74 | char *zNew = mprintf("%.*s%s", (int)(zV-zFilename), zFilename, zV+i+2); |
| 75 | const unsigned char *pRes = builtin_file(zNew, piSize); |
| 76 | fossil_free(zNew); |
| 77 | return pRes; |
| 78 | } |
| 79 | } |
| 80 | if( piSize ) *piSize = 0; |
| 81 | return 0; |
| 82 | } |
| 83 | } |
| 84 | const char *builtin_text(const char *zFilename){ |
| 85 |
+1
-1
| --- src/cache.c | ||
| +++ src/cache.c | ||
| @@ -255,11 +255,11 @@ | ||
| 255 | 255 | void cache_initialize(void){ |
| 256 | 256 | sqlite3_close(cacheOpen(1)); |
| 257 | 257 | } |
| 258 | 258 | |
| 259 | 259 | /* |
| 260 | -** COMMAND: cache* | |
| 260 | +** COMMAND: cache* abbrv-subcom | |
| 261 | 261 | ** |
| 262 | 262 | ** Usage: %fossil cache SUBCOMMAND |
| 263 | 263 | ** |
| 264 | 264 | ** Manage the cache used for potentially expensive web pages such as |
| 265 | 265 | ** /zip and /tarball. SUBCOMMAND can be: |
| 266 | 266 |
| --- src/cache.c | |
| +++ src/cache.c | |
| @@ -255,11 +255,11 @@ | |
| 255 | void cache_initialize(void){ |
| 256 | sqlite3_close(cacheOpen(1)); |
| 257 | } |
| 258 | |
| 259 | /* |
| 260 | ** COMMAND: cache* |
| 261 | ** |
| 262 | ** Usage: %fossil cache SUBCOMMAND |
| 263 | ** |
| 264 | ** Manage the cache used for potentially expensive web pages such as |
| 265 | ** /zip and /tarball. SUBCOMMAND can be: |
| 266 |
| --- src/cache.c | |
| +++ src/cache.c | |
| @@ -255,11 +255,11 @@ | |
| 255 | void cache_initialize(void){ |
| 256 | sqlite3_close(cacheOpen(1)); |
| 257 | } |
| 258 | |
| 259 | /* |
| 260 | ** COMMAND: cache* abbrv-subcom |
| 261 | ** |
| 262 | ** Usage: %fossil cache SUBCOMMAND |
| 263 | ** |
| 264 | ** Manage the cache used for potentially expensive web pages such as |
| 265 | ** /zip and /tarball. SUBCOMMAND can be: |
| 266 |
+25
-5
| --- src/cgi.c | ||
| +++ src/cgi.c | ||
| @@ -688,21 +688,41 @@ | ||
| 688 | 688 | } |
| 689 | 689 | |
| 690 | 690 | |
| 691 | 691 | /* |
| 692 | 692 | ** Return true if the current request is coming from the same origin. |
| 693 | +** | |
| 694 | +** If the request comes from a different origin and bErrorLog is true, then | |
| 695 | +** put a warning message on the error log as this was a possible hack | |
| 696 | +** attempt. | |
| 693 | 697 | */ |
| 694 | -int cgi_same_origin(void){ | |
| 698 | +int cgi_same_origin(int bErrorLog){ | |
| 695 | 699 | const char *zRef; |
| 700 | + char *zToFree = 0; | |
| 696 | 701 | int nBase; |
| 702 | + int rc; | |
| 697 | 703 | if( g.zBaseURL==0 ) return 0; |
| 698 | 704 | zRef = P("HTTP_REFERER"); |
| 699 | 705 | if( zRef==0 ) return 0; |
| 706 | + if( strchr(zRef,'%')!=0 ){ | |
| 707 | + zToFree = strdup(zRef); | |
| 708 | + dehttpize(zToFree); | |
| 709 | + zRef = zToFree; | |
| 710 | + } | |
| 700 | 711 | nBase = (int)strlen(g.zBaseURL); |
| 701 | - if( fossil_strncmp(g.zBaseURL,zRef,nBase)!=0 ) return 0; | |
| 702 | - if( zRef[nBase]!=0 && zRef[nBase]!='/' ) return 0; | |
| 703 | - return 1; | |
| 712 | + if( fossil_strncmp(g.zBaseURL,zRef,nBase)!=0 ){ | |
| 713 | + rc = 0; | |
| 714 | + }else if( zRef[nBase]!=0 && zRef[nBase]!='/' ){ | |
| 715 | + rc = 0; | |
| 716 | + }else{ | |
| 717 | + rc = 1; | |
| 718 | + } | |
| 719 | + if( rc==0 && bErrorLog && fossil_strcmp(P("REQUST_METHOD"),"POST")==0 ){ | |
| 720 | + fossil_errorlog("warning: POST from different origin"); | |
| 721 | + } | |
| 722 | + fossil_free(zToFree); | |
| 723 | + return rc; | |
| 704 | 724 | } |
| 705 | 725 | |
| 706 | 726 | /* |
| 707 | 727 | ** Return true if the current CGI request is a POST request |
| 708 | 728 | */ |
| @@ -733,11 +753,11 @@ | ||
| 733 | 753 | ** 3: (2) plus there is a valid "csrf" token in the request |
| 734 | 754 | */ |
| 735 | 755 | int cgi_csrf_safe(int securityLevel){ |
| 736 | 756 | if( g.okCsrf<0 ) return 0; |
| 737 | 757 | if( g.okCsrf==0 ){ |
| 738 | - if( !cgi_same_origin() ){ | |
| 758 | + if( !cgi_same_origin(1) ){ | |
| 739 | 759 | g.okCsrf = -1; |
| 740 | 760 | }else{ |
| 741 | 761 | g.okCsrf = 1; |
| 742 | 762 | if( cgi_is_post_request() ){ |
| 743 | 763 | g.okCsrf = 2; |
| 744 | 764 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -688,21 +688,41 @@ | |
| 688 | } |
| 689 | |
| 690 | |
| 691 | /* |
| 692 | ** Return true if the current request is coming from the same origin. |
| 693 | */ |
| 694 | int cgi_same_origin(void){ |
| 695 | const char *zRef; |
| 696 | int nBase; |
| 697 | if( g.zBaseURL==0 ) return 0; |
| 698 | zRef = P("HTTP_REFERER"); |
| 699 | if( zRef==0 ) return 0; |
| 700 | nBase = (int)strlen(g.zBaseURL); |
| 701 | if( fossil_strncmp(g.zBaseURL,zRef,nBase)!=0 ) return 0; |
| 702 | if( zRef[nBase]!=0 && zRef[nBase]!='/' ) return 0; |
| 703 | return 1; |
| 704 | } |
| 705 | |
| 706 | /* |
| 707 | ** Return true if the current CGI request is a POST request |
| 708 | */ |
| @@ -733,11 +753,11 @@ | |
| 733 | ** 3: (2) plus there is a valid "csrf" token in the request |
| 734 | */ |
| 735 | int cgi_csrf_safe(int securityLevel){ |
| 736 | if( g.okCsrf<0 ) return 0; |
| 737 | if( g.okCsrf==0 ){ |
| 738 | if( !cgi_same_origin() ){ |
| 739 | g.okCsrf = -1; |
| 740 | }else{ |
| 741 | g.okCsrf = 1; |
| 742 | if( cgi_is_post_request() ){ |
| 743 | g.okCsrf = 2; |
| 744 |
| --- src/cgi.c | |
| +++ src/cgi.c | |
| @@ -688,21 +688,41 @@ | |
| 688 | } |
| 689 | |
| 690 | |
| 691 | /* |
| 692 | ** Return true if the current request is coming from the same origin. |
| 693 | ** |
| 694 | ** If the request comes from a different origin and bErrorLog is true, then |
| 695 | ** put a warning message on the error log as this was a possible hack |
| 696 | ** attempt. |
| 697 | */ |
| 698 | int cgi_same_origin(int bErrorLog){ |
| 699 | const char *zRef; |
| 700 | char *zToFree = 0; |
| 701 | int nBase; |
| 702 | int rc; |
| 703 | if( g.zBaseURL==0 ) return 0; |
| 704 | zRef = P("HTTP_REFERER"); |
| 705 | if( zRef==0 ) return 0; |
| 706 | if( strchr(zRef,'%')!=0 ){ |
| 707 | zToFree = strdup(zRef); |
| 708 | dehttpize(zToFree); |
| 709 | zRef = zToFree; |
| 710 | } |
| 711 | nBase = (int)strlen(g.zBaseURL); |
| 712 | if( fossil_strncmp(g.zBaseURL,zRef,nBase)!=0 ){ |
| 713 | rc = 0; |
| 714 | }else if( zRef[nBase]!=0 && zRef[nBase]!='/' ){ |
| 715 | rc = 0; |
| 716 | }else{ |
| 717 | rc = 1; |
| 718 | } |
| 719 | if( rc==0 && bErrorLog && fossil_strcmp(P("REQUST_METHOD"),"POST")==0 ){ |
| 720 | fossil_errorlog("warning: POST from different origin"); |
| 721 | } |
| 722 | fossil_free(zToFree); |
| 723 | return rc; |
| 724 | } |
| 725 | |
| 726 | /* |
| 727 | ** Return true if the current CGI request is a POST request |
| 728 | */ |
| @@ -733,11 +753,11 @@ | |
| 753 | ** 3: (2) plus there is a valid "csrf" token in the request |
| 754 | */ |
| 755 | int cgi_csrf_safe(int securityLevel){ |
| 756 | if( g.okCsrf<0 ) return 0; |
| 757 | if( g.okCsrf==0 ){ |
| 758 | if( !cgi_same_origin(1) ){ |
| 759 | g.okCsrf = -1; |
| 760 | }else{ |
| 761 | g.okCsrf = 1; |
| 762 | if( cgi_is_post_request() ){ |
| 763 | g.okCsrf = 2; |
| 764 |
+224
-71
| --- src/checkin.c | ||
| +++ src/checkin.c | ||
| @@ -1467,10 +1467,11 @@ | ||
| 1467 | 1467 | CheckinInfo *p, |
| 1468 | 1468 | int parent_rid, |
| 1469 | 1469 | int dryRunFlag |
| 1470 | 1470 | ){ |
| 1471 | 1471 | Blob prompt; |
| 1472 | + int wikiFlags; | |
| 1472 | 1473 | #if defined(_WIN32) || defined(__CYGWIN__) |
| 1473 | 1474 | int bomSize; |
| 1474 | 1475 | const unsigned char *bom = get_utf8_bom(&bomSize); |
| 1475 | 1476 | blob_init(&prompt, (const char *) bom, bomSize); |
| 1476 | 1477 | if( zInit && zInit[0]){ |
| @@ -1479,14 +1480,37 @@ | ||
| 1479 | 1480 | #else |
| 1480 | 1481 | blob_init(&prompt, zInit, -1); |
| 1481 | 1482 | #endif |
| 1482 | 1483 | blob_append(&prompt, |
| 1483 | 1484 | "\n" |
| 1484 | - "# Enter a commit message for this check-in." | |
| 1485 | - " Lines beginning with # are ignored.\n" | |
| 1486 | - "#\n", -1 | |
| 1485 | + "# Enter the commit message. Formatting rules:\n" | |
| 1486 | + "# * Lines beginning with # are ignored.\n", | |
| 1487 | + -1 | |
| 1487 | 1488 | ); |
| 1489 | + wikiFlags = wiki_convert_flags(1); | |
| 1490 | + if( wikiFlags & WIKI_LINKSONLY ){ | |
| 1491 | + blob_append(&prompt,"# * Hyperlinks inside of [...]\n", -1); | |
| 1492 | + if( wikiFlags & WIKI_NEWLINE ){ | |
| 1493 | + blob_append(&prompt, | |
| 1494 | + "# * Newlines are significant and are displayed as written\n", -1); | |
| 1495 | + }else{ | |
| 1496 | + blob_append(&prompt, | |
| 1497 | + "# * Newlines are interpreted as ordinary spaces\n", | |
| 1498 | + -1 | |
| 1499 | + ); | |
| 1500 | + } | |
| 1501 | + blob_append(&prompt, | |
| 1502 | + "# * All other text will be displayed as written\n", -1); | |
| 1503 | + }else{ | |
| 1504 | + blob_append(&prompt, | |
| 1505 | + "# * Hyperlinks: [target] or [target|display-text]\n" | |
| 1506 | + "# * Blank lines cause a paragraph break\n" | |
| 1507 | + "# * Other text rendered as if it where HTML\n", -1 | |
| 1508 | + ); | |
| 1509 | + } | |
| 1510 | + blob_append(&prompt, "#\n", 2); | |
| 1511 | + | |
| 1488 | 1512 | if( dryRunFlag ){ |
| 1489 | 1513 | blob_appendf(&prompt, "# DRY-RUN: This is a test commit. No changes " |
| 1490 | 1514 | "will be made to the repository\n#\n"); |
| 1491 | 1515 | } |
| 1492 | 1516 | blob_appendf(&prompt, "# user: %s\n", |
| @@ -2282,41 +2306,112 @@ | ||
| 2282 | 2306 | char **pA = (char**)a; |
| 2283 | 2307 | char **pB = (char**)b; |
| 2284 | 2308 | return fossil_strcmp(pA[0], pB[0]); |
| 2285 | 2309 | } |
| 2286 | 2310 | |
| 2311 | +/* | |
| 2312 | +** SETTING: verify-comments width=8 default=on | |
| 2313 | +** | |
| 2314 | +** This setting determines how much sanity checking, if any, the | |
| 2315 | +** "fossil commit" and "fossil amend" commands do against check-in | |
| 2316 | +** comments. Recognized values: | |
| 2317 | +** | |
| 2318 | +** on (Default) Check for bad syntax and/or broken hyperlinks | |
| 2319 | +** in check-in comments and offer the user a chance to | |
| 2320 | +** continue editing for interactive sessions, or simply | |
| 2321 | +** abort the commit if the comment was entered using -m or -M | |
| 2322 | +** | |
| 2323 | +** off Do not do syntax checking of any kind | |
| 2324 | +** | |
| 2325 | +** preview Do all the same checks as "on" but also always preview the | |
| 2326 | +** check-in comment to the user during interactive sessions | |
| 2327 | +** even if no obvious errors are found, and provide an | |
| 2328 | +** opportunity to accept or re-edit | |
| 2329 | +*/ | |
| 2330 | + | |
| 2331 | +#if INTERFACE | |
| 2332 | +#define COMCK_MARKUP 0x01 /* Check for mistakes */ | |
| 2333 | +#define COMCK_PREVIEW 0x02 /* Always preview, even if no issues found */ | |
| 2334 | +#endif /* INTERFACE */ | |
| 2335 | + | |
| 2336 | +/* | |
| 2337 | +** Check for possible formatting errors in the comment string pComment. | |
| 2338 | +** | |
| 2339 | +** If issues are found, write an appropriate error notice, probably also | |
| 2340 | +** including the complete text of the comment formatted to highlight the | |
| 2341 | +** problem, to stdout and return non-zero. The return value is some | |
| 2342 | +** combination of the COMCK_* flags, depending on what went wrong. | |
| 2343 | +** | |
| 2344 | +** If no issues are seen, do not output anything and return zero. | |
| 2345 | +*/ | |
| 2346 | +int verify_comment(Blob *pComment, int mFlags){ | |
| 2347 | + Blob in, html; | |
| 2348 | + int mResult; | |
| 2349 | + int rc = mFlags & COMCK_PREVIEW; | |
| 2350 | + int wFlags; | |
| 2351 | + | |
| 2352 | + if( mFlags==0 ) return 0; | |
| 2353 | + blob_init(&in, blob_str(pComment), -1); | |
| 2354 | + blob_init(&html, 0, 0); | |
| 2355 | + wFlags = wiki_convert_flags(0); | |
| 2356 | + wFlags &= ~WIKI_NOBADLINKS; | |
| 2357 | + wFlags |= WIKI_MARK; | |
| 2358 | + mResult = wiki_convert(&in, &html, wFlags); | |
| 2359 | + if( mResult & RENDER_ANYERROR ) rc |= COMCK_MARKUP; | |
| 2360 | + if( rc ){ | |
| 2361 | + int htot = ((wFlags & WIKI_NEWLINE)!=0 ? 0 : HTOT_FLOW)|HTOT_TRIM; | |
| 2362 | + Blob txt; | |
| 2363 | + if( terminal_is_vt100() ) htot |= HTOT_VT100; | |
| 2364 | + blob_init(&txt, 0, 0); | |
| 2365 | + html_to_plaintext(blob_str(&html), &txt, htot); | |
| 2366 | + if( rc & COMCK_MARKUP ){ | |
| 2367 | + fossil_print("Possible format errors in the check-in comment:\n\n "); | |
| 2368 | + }else{ | |
| 2369 | + fossil_print("Preview of the check-in comment:\n\n "); | |
| 2370 | + } | |
| 2371 | + if( wFlags & WIKI_NEWLINE ){ | |
| 2372 | + Blob line; | |
| 2373 | + char *zIndent = ""; | |
| 2374 | + while( blob_line(&txt, &line) ){ | |
| 2375 | + fossil_print("%s%b", zIndent, &line); | |
| 2376 | + zIndent = " "; | |
| 2377 | + } | |
| 2378 | + fossil_print("\n"); | |
| 2379 | + }else{ | |
| 2380 | + comment_print(blob_str(&txt), 0, 3, -1, get_comment_format()); | |
| 2381 | + } | |
| 2382 | + fossil_print("\n"); | |
| 2383 | + fflush(stdout); | |
| 2384 | + blob_reset(&txt); | |
| 2385 | + } | |
| 2386 | + blob_reset(&html); | |
| 2387 | + blob_reset(&in); | |
| 2388 | + return rc; | |
| 2389 | +} | |
| 2390 | + | |
| 2287 | 2391 | /* |
| 2288 | 2392 | ** COMMAND: ci# |
| 2289 | 2393 | ** COMMAND: commit |
| 2290 | 2394 | ** |
| 2291 | 2395 | ** Usage: %fossil commit ?OPTIONS? ?FILE...? |
| 2292 | 2396 | ** or: %fossil ci ?OPTIONS? ?FILE...? |
| 2293 | 2397 | ** |
| 2294 | -** Create a new version containing all of the changes in the current | |
| 2295 | -** check-out. You will be prompted to enter a check-in comment unless | |
| 2296 | -** the comment has been specified on the command-line using "-m" or a | |
| 2297 | -** file containing the comment using -M. The editor defined in the | |
| 2298 | -** "editor" fossil option (see %fossil help set) will be used, or from | |
| 2299 | -** the "VISUAL" or "EDITOR" environment variables (in that order) if | |
| 2300 | -** no editor is set. | |
| 2398 | +** Create a new check-in containing all of the changes in the current | |
| 2399 | +** check-out. All changes are committed unless some subset of files | |
| 2400 | +** is specified on the command line, in which case only the named files | |
| 2401 | +** become part of the new check-in. | |
| 2301 | 2402 | ** |
| 2302 | -** All files that have changed will be committed unless some subset of | |
| 2303 | -** files is specified on the command line. | |
| 2403 | +** You will be prompted to enter a check-in comment unless the comment | |
| 2404 | +** has been specified on the command-line using "-m" or "-M". The | |
| 2405 | +** text editor used is determined by the "editor" setting, or by the | |
| 2406 | +** "VISUAL" or "EDITOR" environment variables. Commit message text is | |
| 2407 | +** interpreted as fossil-wiki format. Potentially misformatted check-in | |
| 2408 | +** comment text is detected and reported unless the --no-verify-comment | |
| 2409 | +** option is used. | |
| 2304 | 2410 | ** |
| 2305 | 2411 | ** The --branch option followed by a branch name causes the new |
| 2306 | -** check-in to be placed in a newly-created branch with the name | |
| 2307 | -** passed to the --branch option. | |
| 2308 | -** | |
| 2309 | -** Use the --branchcolor option followed by a color name (ex: | |
| 2310 | -** '#ffc0c0') to specify the background color of entries in the new | |
| 2311 | -** branch when shown in the web timeline interface. The use of | |
| 2312 | -** the --branchcolor option is not recommended. Instead, let Fossil | |
| 2313 | -** choose the branch color automatically. | |
| 2314 | -** | |
| 2315 | -** The --bgcolor option works like --branchcolor but only sets the | |
| 2316 | -** background color for a single check-in. Subsequent check-ins revert | |
| 2317 | -** to the default color. | |
| 2412 | +** check-in to be placed in a newly-created branch with name specified. | |
| 2318 | 2413 | ** |
| 2319 | 2414 | ** A check-in is not permitted to fork unless the --allow-fork option |
| 2320 | 2415 | ** appears. An empty check-in (i.e. with nothing changed) is not |
| 2321 | 2416 | ** allowed unless the --allow-empty option appears. A check-in may not |
| 2322 | 2417 | ** be older than its ancestor unless the --allow-older option appears. |
| @@ -2328,21 +2423,16 @@ | ||
| 2328 | 2423 | ** unless the interactive user chooses to proceed. If there is no |
| 2329 | 2424 | ** interactive user or these warnings should be skipped for some other |
| 2330 | 2425 | ** reason, the --no-warnings option may be used. A check-in is not |
| 2331 | 2426 | ** allowed against a closed leaf. |
| 2332 | 2427 | ** |
| 2333 | -** If a commit message is blank, you will be prompted: | |
| 2334 | -** ("continue (y/N)?") to confirm you really want to commit with a | |
| 2335 | -** blank commit message. The default value is "N", do not commit. | |
| 2336 | -** | |
| 2337 | 2428 | ** The --private option creates a private check-in that is never synced. |
| 2338 | 2429 | ** Children of private check-ins are automatically private. |
| 2339 | 2430 | ** |
| 2340 | 2431 | ** The --tag option applies the symbolic tag name to the check-in. |
| 2341 | -** | |
| 2342 | -** The --hash option detects edited files by computing each file's | |
| 2343 | -** artifact hash rather than just checking for changes to its size or mtime. | |
| 2432 | +** The --tag option can be repeated to assign multiple tags to a check-in. | |
| 2433 | +** For example: "... --tag release --tag version-1.2.3 ..." | |
| 2344 | 2434 | ** |
| 2345 | 2435 | ** Options: |
| 2346 | 2436 | ** --allow-conflict Allow unresolved merge conflicts |
| 2347 | 2437 | ** --allow-empty Allow a commit with no changes |
| 2348 | 2438 | ** --allow-fork Allow the commit to fork |
| @@ -2350,45 +2440,45 @@ | ||
| 2350 | 2440 | ** --baseline Use a baseline manifest in the commit process |
| 2351 | 2441 | ** --bgcolor COLOR Apply COLOR to this one check-in only |
| 2352 | 2442 | ** --branch NEW-BRANCH-NAME Check in to this new branch |
| 2353 | 2443 | ** --branchcolor COLOR Apply given COLOR to the branch |
| 2354 | 2444 | ** --close Close the branch being committed |
| 2355 | -** --date-override DATETIME DATE to use instead of 'now' | |
| 2445 | +** --date-override DATETIME Make DATETIME the time of the check-in. | |
| 2446 | +** Useful when importing historical check-ins | |
| 2447 | +** from another version control system. | |
| 2356 | 2448 | ** --delta Use a delta manifest in the commit process |
| 2357 | 2449 | ** --hash Verify file status using hashing rather |
| 2358 | -** than relying on file mtimes | |
| 2450 | +** than relying on filesystem mtimes | |
| 2359 | 2451 | ** --if-changes Make this command a silent no-op if there |
| 2360 | 2452 | ** are no changes |
| 2361 | 2453 | ** --ignore-clock-skew If a clock skew is detected, ignore it and |
| 2362 | 2454 | ** behave as if the user had entered 'yes' to |
| 2363 | 2455 | ** the question of whether to proceed despite |
| 2364 | 2456 | ** the skew. |
| 2365 | 2457 | ** --ignore-oversize Do not warn the user about oversized files |
| 2366 | 2458 | ** --integrate Close all merged-in branches |
| 2367 | -** -m|--comment COMMENT-TEXT Use COMMENT-TEXT as commit comment | |
| 2368 | -** -M|--message-file FILE Read the commit comment from given file | |
| 2369 | -** --mimetype MIMETYPE Mimetype of check-in comment | |
| 2370 | -** -n|--dry-run If given, display instead of run actions | |
| 2459 | +** -m|--comment COMMENT-TEXT Use COMMENT-TEXT as the check-in comment | |
| 2460 | +** -M|--message-file FILE Read the check-in comment from FILE | |
| 2461 | +** -n|--dry-run Do not actually create a new check-in. Just | |
| 2462 | +** show what would have happened. For debugging. | |
| 2371 | 2463 | ** -v|--verbose Show a diff in the commit message prompt |
| 2372 | 2464 | ** --no-prompt This option disables prompting the user for |
| 2373 | 2465 | ** input and assumes an answer of 'No' for every |
| 2374 | 2466 | ** question. |
| 2375 | 2467 | ** --no-warnings Omit all warnings about file contents |
| 2376 | 2468 | ** --no-verify Do not run before-commit hooks |
| 2469 | +** --no-verify-comment Do not validate the check-in comment | |
| 2377 | 2470 | ** --nosign Do not attempt to sign this commit with gpg |
| 2378 | 2471 | ** --nosync Do not auto-sync prior to committing |
| 2379 | 2472 | ** --override-lock Allow a check-in even though parent is locked |
| 2380 | -** --private Do not sync changes and their descendants | |
| 2381 | -** --tag TAG-NAME Assign given tag TAG-NAME to the check-in | |
| 2473 | +** --private Never sync the resulting check-in and make | |
| 2474 | +** all descendants private too. | |
| 2475 | +** --proxy PROXY Use PROXY as http proxy during sync operation | |
| 2476 | +** --tag TAG-NAME Add TAG-NAME to the check-in. May be repeated. | |
| 2382 | 2477 | ** --trace Debug tracing |
| 2383 | -** --user-override USER USER to use instead of the current default | |
| 2384 | -** | |
| 2385 | -** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in | |
| 2386 | -** year-month-day form, it may be truncated, the "T" may be replaced by | |
| 2387 | -** a space, and it may also name a timezone offset from UTC as "-HH:MM" | |
| 2388 | -** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z" | |
| 2389 | -** means UTC. | |
| 2478 | +** --user-override USER Record USER as the login that created the | |
| 2479 | +** new check-in, rather that the current user. | |
| 2390 | 2480 | ** |
| 2391 | 2481 | ** See also: [[branch]], [[changes]], [[update]], [[extras]], [[sync]] |
| 2392 | 2482 | */ |
| 2393 | 2483 | void commit_cmd(void){ |
| 2394 | 2484 | int hasChanges; /* True if unsaved changes exist */ |
| @@ -2414,10 +2504,11 @@ | ||
| 2414 | 2504 | int allowConflict = 0; /* Allow unresolve merge conflicts */ |
| 2415 | 2505 | int allowEmpty = 0; /* Allow a commit with no changes */ |
| 2416 | 2506 | int onlyIfChanges = 0; /* No-op if there are no changes */ |
| 2417 | 2507 | int allowFork = 0; /* Allow the commit to fork */ |
| 2418 | 2508 | int allowOlder = 0; /* Allow a commit older than its ancestor */ |
| 2509 | + int noVerifyCom = 0; /* Allow suspicious check-in comments */ | |
| 2419 | 2510 | char *zManifestFile; /* Name of the manifest file */ |
| 2420 | 2511 | int useCksum; /* True if checksums should be computed and verified */ |
| 2421 | 2512 | int outputManifest; /* True to output "manifest" and "manifest.uuid" */ |
| 2422 | 2513 | int dryRunFlag; /* True for a test run. Debugging only */ |
| 2423 | 2514 | CheckinInfo sCiInfo; /* Information about this check-in */ |
| @@ -2439,10 +2530,11 @@ | ||
| 2439 | 2530 | int bRecheck = 0; /* Repeat fork and closed-branch checks*/ |
| 2440 | 2531 | int bIgnoreSkew = 0; /* --ignore-clock-skew flag */ |
| 2441 | 2532 | int mxSize; |
| 2442 | 2533 | char *zCurBranch = 0; /* The current branch name of checkout */ |
| 2443 | 2534 | char *zNewBranch = 0; /* The branch name after update */ |
| 2535 | + int ckComFlgs; /* Flags passed to verify_comment() */ | |
| 2444 | 2536 | |
| 2445 | 2537 | memset(&sCiInfo, 0, sizeof(sCiInfo)); |
| 2446 | 2538 | url_proxy_options(); |
| 2447 | 2539 | /* --sha1sum is an undocumented alias for --hash for backwards compatiblity */ |
| 2448 | 2540 | useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0; |
| @@ -2469,24 +2561,30 @@ | ||
| 2469 | 2561 | } |
| 2470 | 2562 | zComment = find_option("comment","m",1); |
| 2471 | 2563 | forceFlag = find_option("force", "f", 0)!=0; |
| 2472 | 2564 | allowConflict = find_option("allow-conflict",0,0)!=0; |
| 2473 | 2565 | allowEmpty = find_option("allow-empty",0,0)!=0; |
| 2566 | + noVerifyCom = find_option("no-verify-comment",0,0)!=0; | |
| 2474 | 2567 | onlyIfChanges = find_option("if-changes",0,0)!=0; |
| 2475 | 2568 | allowFork = find_option("allow-fork",0,0)!=0; |
| 2476 | 2569 | if( find_option("override-lock",0,0)!=0 ) allowFork = 1; |
| 2477 | 2570 | allowOlder = find_option("allow-older",0,0)!=0; |
| 2478 | 2571 | noPrompt = find_option("no-prompt", 0, 0)!=0; |
| 2479 | 2572 | noWarningFlag = find_option("no-warnings", 0, 0)!=0; |
| 2480 | 2573 | noVerify = find_option("no-verify",0,0)!=0; |
| 2481 | 2574 | bTrace = find_option("trace",0,0)!=0; |
| 2482 | 2575 | sCiInfo.zBranch = find_option("branch","b",1); |
| 2483 | - sCiInfo.zColor = find_option("bgcolor",0,1); | |
| 2484 | - sCiInfo.zBrClr = find_option("branchcolor",0,1); | |
| 2576 | + | |
| 2577 | + /* NB: the --bgcolor and --branchcolor flags still work, but are | |
| 2578 | + ** now undocumented, to discourage their use. --mimetype has never | |
| 2579 | + ** been used for anything, so also leave it undocumented */ | |
| 2580 | + sCiInfo.zColor = find_option("bgcolor",0,1); /* Deprecated, undocumented*/ | |
| 2581 | + sCiInfo.zBrClr = find_option("branchcolor",0,1); /* Deprecated, undocumented*/ | |
| 2582 | + sCiInfo.zMimetype = find_option("mimetype",0,1); /* Deprecated, undocumented*/ | |
| 2583 | + | |
| 2485 | 2584 | sCiInfo.closeFlag = find_option("close",0,0)!=0; |
| 2486 | 2585 | sCiInfo.integrateFlag = find_option("integrate",0,0)!=0; |
| 2487 | - sCiInfo.zMimetype = find_option("mimetype",0,1); | |
| 2488 | 2586 | sCiInfo.verboseFlag = find_option("verbose", "v", 0)!=0; |
| 2489 | 2587 | while( (zTag = find_option("tag",0,1))!=0 ){ |
| 2490 | 2588 | if( zTag[0]==0 ) continue; |
| 2491 | 2589 | sCiInfo.azTag = fossil_realloc((void*)sCiInfo.azTag, |
| 2492 | 2590 | sizeof(char*)*(nTag+2)); |
| @@ -2498,14 +2596,20 @@ | ||
| 2498 | 2596 | sCiInfo.zUserOvrd = find_option("user-override",0,1); |
| 2499 | 2597 | noSign = db_get_boolean("omitsign", 0)|noSign; |
| 2500 | 2598 | if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; } |
| 2501 | 2599 | useCksum = db_get_boolean("repo-cksum", 1); |
| 2502 | 2600 | bIgnoreSkew = find_option("ignore-clock-skew",0,0)!=0; |
| 2503 | - outputManifest = db_get_manifest_setting(); | |
| 2601 | + outputManifest = db_get_manifest_setting(0); | |
| 2504 | 2602 | mxSize = db_large_file_size(); |
| 2505 | 2603 | if( find_option("ignore-oversize",0,0)!=0 ) mxSize = 0; |
| 2506 | 2604 | verify_all_options(); |
| 2605 | + | |
| 2606 | + /* The --no-warnings flag and the --force flag each imply | |
| 2607 | + ** the --no-verify-comment flag */ | |
| 2608 | + if( noWarningFlag || forceFlag ){ | |
| 2609 | + noVerifyCom = 1; | |
| 2610 | + } | |
| 2507 | 2611 | |
| 2508 | 2612 | /* Get the ID of the parent manifest artifact */ |
| 2509 | 2613 | vid = db_lget_int("checkout", 0); |
| 2510 | 2614 | if( vid==0 ){ |
| 2511 | 2615 | useCksum = 1; |
| @@ -2606,33 +2710,35 @@ | ||
| 2606 | 2710 | fossil_exit(1); |
| 2607 | 2711 | } |
| 2608 | 2712 | } |
| 2609 | 2713 | |
| 2610 | 2714 | /* So that older versions of Fossil (that do not understand delta- |
| 2611 | - ** manifest) can continue to use this repository, do not create a new | |
| 2715 | + ** manifest) can continue to use this repository, and because | |
| 2716 | + ** delta manifests are usually a bad idea unless the repository | |
| 2717 | + ** has a really large number of files, do not create a new | |
| 2612 | 2718 | ** delta-manifest unless this repository already contains one or more |
| 2613 | 2719 | ** delta-manifests, or unless the delta-manifest is explicitly requested |
| 2614 | 2720 | ** by the --delta option. |
| 2615 | 2721 | ** |
| 2616 | - ** The forbid-delta-manifests setting prevents new delta manifests. | |
| 2722 | + ** The forbid-delta-manifests setting prevents new delta manifests, | |
| 2723 | + ** even if the --delta option is used. | |
| 2617 | 2724 | ** |
| 2618 | 2725 | ** If the remote repository sent an avoid-delta-manifests pragma on |
| 2619 | - ** the autosync above, then also try to avoid deltas, unless the | |
| 2726 | + ** the autosync above, then also forbid delta manifests, even if the | |
| 2620 | 2727 | ** --delta option is specified. The remote repo will send the |
| 2621 | - ** avoid-delta-manifests pragma if it has its "forbid-delta-manifests" | |
| 2622 | - ** setting enabled. | |
| 2728 | + ** avoid-delta-manifests pragma if its "forbid-delta-manifests" | |
| 2729 | + ** setting is enabled. | |
| 2623 | 2730 | */ |
| 2624 | - if( !db_get_boolean("seen-delta-manifest",0) | |
| 2731 | + if( !(forceDelta || db_get_boolean("seen-delta-manifest",0)) | |
| 2625 | 2732 | || db_get_boolean("forbid-delta-manifests",0) |
| 2626 | 2733 | || g.bAvoidDeltaManifests |
| 2627 | 2734 | ){ |
| 2628 | - if( !forceDelta ) forceBaseline = 1; | |
| 2735 | + forceBaseline = 1; | |
| 2629 | 2736 | } |
| 2630 | - | |
| 2631 | 2737 | |
| 2632 | 2738 | /* Require confirmation to continue with the check-in if there is |
| 2633 | - ** clock skew | |
| 2739 | + ** clock skew. This helps to prevent timewarps. | |
| 2634 | 2740 | */ |
| 2635 | 2741 | if( g.clockSkewSeen ){ |
| 2636 | 2742 | if( bIgnoreSkew!=0 ){ |
| 2637 | 2743 | cReply = 'y'; |
| 2638 | 2744 | fossil_warning("Clock skew ignored due to --ignore-clock-skew."); |
| @@ -2787,35 +2893,82 @@ | ||
| 2787 | 2893 | fossil_free(zNewBranch); |
| 2788 | 2894 | |
| 2789 | 2895 | /* Always exit the loop on the second pass */ |
| 2790 | 2896 | if( bRecheck ) break; |
| 2791 | 2897 | |
| 2898 | + | |
| 2899 | + /* Figure out how much comment verification is requested */ | |
| 2900 | + if( noVerifyCom ){ | |
| 2901 | + ckComFlgs = 0; | |
| 2902 | + }else{ | |
| 2903 | + const char *zVerComs = db_get("verify-comments","on"); | |
| 2904 | + if( is_false(zVerComs) ){ | |
| 2905 | + ckComFlgs = 0; | |
| 2906 | + }else if( strcmp(zVerComs,"preview")==0 ){ | |
| 2907 | + ckComFlgs = COMCK_PREVIEW | COMCK_MARKUP; | |
| 2908 | + }else{ | |
| 2909 | + ckComFlgs = COMCK_MARKUP; | |
| 2910 | + } | |
| 2911 | + } | |
| 2792 | 2912 | |
| 2793 | 2913 | /* Get the check-in comment. This might involve prompting the |
| 2794 | 2914 | ** user for the check-in comment, in which case we should resync |
| 2795 | 2915 | ** to renew the check-in lock and repeat the checks for conflicts. |
| 2796 | 2916 | */ |
| 2797 | 2917 | if( zComment ){ |
| 2798 | 2918 | blob_zero(&comment); |
| 2799 | 2919 | blob_append(&comment, zComment, -1); |
| 2920 | + ckComFlgs &= ~COMCK_PREVIEW; | |
| 2921 | + if( verify_comment(&comment, ckComFlgs) ){ | |
| 2922 | + fossil_fatal("Commit aborted; " | |
| 2923 | + "use --no-verify-comment to override"); | |
| 2924 | + } | |
| 2800 | 2925 | }else if( zComFile ){ |
| 2801 | 2926 | blob_zero(&comment); |
| 2802 | 2927 | blob_read_from_file(&comment, zComFile, ExtFILE); |
| 2803 | 2928 | blob_to_utf8_no_bom(&comment, 1); |
| 2929 | + ckComFlgs &= ~COMCK_PREVIEW; | |
| 2930 | + if( verify_comment(&comment, ckComFlgs) ){ | |
| 2931 | + fossil_fatal("Commit aborted; " | |
| 2932 | + "use --no-verify-comment to override"); | |
| 2933 | + } | |
| 2804 | 2934 | }else if( !noPrompt ){ |
| 2805 | - char *zInit = db_text(0,"SELECT value FROM vvar WHERE name='ci-comment'"); | |
| 2806 | - prepare_commit_comment(&comment, zInit, &sCiInfo, vid, dryRunFlag); | |
| 2807 | - if( zInit && zInit[0] && fossil_strcmp(zInit, blob_str(&comment))==0 ){ | |
| 2808 | - prompt_user("unchanged check-in comment. continue (y/N)? ", &ans); | |
| 2809 | - cReply = blob_str(&ans)[0]; | |
| 2810 | - blob_reset(&ans); | |
| 2811 | - if( cReply!='y' && cReply!='Y' ){ | |
| 2812 | - fossil_fatal("Commit aborted."); | |
| 2813 | - } | |
| 2814 | - } | |
| 2815 | - free(zInit); | |
| 2816 | - db_multi_exec("REPLACE INTO vvar VALUES('ci-comment',%B)", &comment); | |
| 2935 | + while( 1/*exit-by-break*/ ){ | |
| 2936 | + int rc; | |
| 2937 | + char *zInit; | |
| 2938 | + zInit = db_text(0,"SELECT value FROM vvar WHERE name='ci-comment'"); | |
| 2939 | + prepare_commit_comment(&comment, zInit, &sCiInfo, vid, dryRunFlag); | |
| 2940 | + db_multi_exec("REPLACE INTO vvar VALUES('ci-comment',%B)", &comment); | |
| 2941 | + if( (rc = verify_comment(&comment, ckComFlgs))!=0 ){ | |
| 2942 | + if( rc==COMCK_PREVIEW ){ | |
| 2943 | + prompt_user("Continue, abort, or edit? (C/a/e)? ", &ans); | |
| 2944 | + }else{ | |
| 2945 | + prompt_user("Edit, abort, or continue (E/a/c)? ", &ans); | |
| 2946 | + } | |
| 2947 | + cReply = blob_str(&ans)[0]; | |
| 2948 | + cReply = fossil_tolower(cReply); | |
| 2949 | + blob_reset(&ans); | |
| 2950 | + if( cReply=='a' ){ | |
| 2951 | + fossil_fatal("Commit aborted."); | |
| 2952 | + } | |
| 2953 | + if( cReply=='e' || (cReply!='c' && rc!=COMCK_PREVIEW) ){ | |
| 2954 | + fossil_free(zInit); | |
| 2955 | + continue; | |
| 2956 | + } | |
| 2957 | + } | |
| 2958 | + if( zInit && zInit[0] && fossil_strcmp(zInit, blob_str(&comment))==0 ){ | |
| 2959 | + prompt_user("unchanged check-in comment. continue (y/N)? ", &ans); | |
| 2960 | + cReply = blob_str(&ans)[0]; | |
| 2961 | + blob_reset(&ans); | |
| 2962 | + if( cReply!='y' && cReply!='Y' ){ | |
| 2963 | + fossil_fatal("Commit aborted."); | |
| 2964 | + } | |
| 2965 | + } | |
| 2966 | + fossil_free(zInit); | |
| 2967 | + break; | |
| 2968 | + } | |
| 2969 | + | |
| 2817 | 2970 | db_end_transaction(0); |
| 2818 | 2971 | db_begin_transaction(); |
| 2819 | 2972 | if( !g.markPrivate && vid!=0 && !allowFork && !forceFlag ){ |
| 2820 | 2973 | /* Do another auto-pull, renewing the check-in lock. Then set |
| 2821 | 2974 | ** bRecheck so that we loop back above to verify that the check-in |
| 2822 | 2975 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -1467,10 +1467,11 @@ | |
| 1467 | CheckinInfo *p, |
| 1468 | int parent_rid, |
| 1469 | int dryRunFlag |
| 1470 | ){ |
| 1471 | Blob prompt; |
| 1472 | #if defined(_WIN32) || defined(__CYGWIN__) |
| 1473 | int bomSize; |
| 1474 | const unsigned char *bom = get_utf8_bom(&bomSize); |
| 1475 | blob_init(&prompt, (const char *) bom, bomSize); |
| 1476 | if( zInit && zInit[0]){ |
| @@ -1479,14 +1480,37 @@ | |
| 1479 | #else |
| 1480 | blob_init(&prompt, zInit, -1); |
| 1481 | #endif |
| 1482 | blob_append(&prompt, |
| 1483 | "\n" |
| 1484 | "# Enter a commit message for this check-in." |
| 1485 | " Lines beginning with # are ignored.\n" |
| 1486 | "#\n", -1 |
| 1487 | ); |
| 1488 | if( dryRunFlag ){ |
| 1489 | blob_appendf(&prompt, "# DRY-RUN: This is a test commit. No changes " |
| 1490 | "will be made to the repository\n#\n"); |
| 1491 | } |
| 1492 | blob_appendf(&prompt, "# user: %s\n", |
| @@ -2282,41 +2306,112 @@ | |
| 2282 | char **pA = (char**)a; |
| 2283 | char **pB = (char**)b; |
| 2284 | return fossil_strcmp(pA[0], pB[0]); |
| 2285 | } |
| 2286 | |
| 2287 | /* |
| 2288 | ** COMMAND: ci# |
| 2289 | ** COMMAND: commit |
| 2290 | ** |
| 2291 | ** Usage: %fossil commit ?OPTIONS? ?FILE...? |
| 2292 | ** or: %fossil ci ?OPTIONS? ?FILE...? |
| 2293 | ** |
| 2294 | ** Create a new version containing all of the changes in the current |
| 2295 | ** check-out. You will be prompted to enter a check-in comment unless |
| 2296 | ** the comment has been specified on the command-line using "-m" or a |
| 2297 | ** file containing the comment using -M. The editor defined in the |
| 2298 | ** "editor" fossil option (see %fossil help set) will be used, or from |
| 2299 | ** the "VISUAL" or "EDITOR" environment variables (in that order) if |
| 2300 | ** no editor is set. |
| 2301 | ** |
| 2302 | ** All files that have changed will be committed unless some subset of |
| 2303 | ** files is specified on the command line. |
| 2304 | ** |
| 2305 | ** The --branch option followed by a branch name causes the new |
| 2306 | ** check-in to be placed in a newly-created branch with the name |
| 2307 | ** passed to the --branch option. |
| 2308 | ** |
| 2309 | ** Use the --branchcolor option followed by a color name (ex: |
| 2310 | ** '#ffc0c0') to specify the background color of entries in the new |
| 2311 | ** branch when shown in the web timeline interface. The use of |
| 2312 | ** the --branchcolor option is not recommended. Instead, let Fossil |
| 2313 | ** choose the branch color automatically. |
| 2314 | ** |
| 2315 | ** The --bgcolor option works like --branchcolor but only sets the |
| 2316 | ** background color for a single check-in. Subsequent check-ins revert |
| 2317 | ** to the default color. |
| 2318 | ** |
| 2319 | ** A check-in is not permitted to fork unless the --allow-fork option |
| 2320 | ** appears. An empty check-in (i.e. with nothing changed) is not |
| 2321 | ** allowed unless the --allow-empty option appears. A check-in may not |
| 2322 | ** be older than its ancestor unless the --allow-older option appears. |
| @@ -2328,21 +2423,16 @@ | |
| 2328 | ** unless the interactive user chooses to proceed. If there is no |
| 2329 | ** interactive user or these warnings should be skipped for some other |
| 2330 | ** reason, the --no-warnings option may be used. A check-in is not |
| 2331 | ** allowed against a closed leaf. |
| 2332 | ** |
| 2333 | ** If a commit message is blank, you will be prompted: |
| 2334 | ** ("continue (y/N)?") to confirm you really want to commit with a |
| 2335 | ** blank commit message. The default value is "N", do not commit. |
| 2336 | ** |
| 2337 | ** The --private option creates a private check-in that is never synced. |
| 2338 | ** Children of private check-ins are automatically private. |
| 2339 | ** |
| 2340 | ** The --tag option applies the symbolic tag name to the check-in. |
| 2341 | ** |
| 2342 | ** The --hash option detects edited files by computing each file's |
| 2343 | ** artifact hash rather than just checking for changes to its size or mtime. |
| 2344 | ** |
| 2345 | ** Options: |
| 2346 | ** --allow-conflict Allow unresolved merge conflicts |
| 2347 | ** --allow-empty Allow a commit with no changes |
| 2348 | ** --allow-fork Allow the commit to fork |
| @@ -2350,45 +2440,45 @@ | |
| 2350 | ** --baseline Use a baseline manifest in the commit process |
| 2351 | ** --bgcolor COLOR Apply COLOR to this one check-in only |
| 2352 | ** --branch NEW-BRANCH-NAME Check in to this new branch |
| 2353 | ** --branchcolor COLOR Apply given COLOR to the branch |
| 2354 | ** --close Close the branch being committed |
| 2355 | ** --date-override DATETIME DATE to use instead of 'now' |
| 2356 | ** --delta Use a delta manifest in the commit process |
| 2357 | ** --hash Verify file status using hashing rather |
| 2358 | ** than relying on file mtimes |
| 2359 | ** --if-changes Make this command a silent no-op if there |
| 2360 | ** are no changes |
| 2361 | ** --ignore-clock-skew If a clock skew is detected, ignore it and |
| 2362 | ** behave as if the user had entered 'yes' to |
| 2363 | ** the question of whether to proceed despite |
| 2364 | ** the skew. |
| 2365 | ** --ignore-oversize Do not warn the user about oversized files |
| 2366 | ** --integrate Close all merged-in branches |
| 2367 | ** -m|--comment COMMENT-TEXT Use COMMENT-TEXT as commit comment |
| 2368 | ** -M|--message-file FILE Read the commit comment from given file |
| 2369 | ** --mimetype MIMETYPE Mimetype of check-in comment |
| 2370 | ** -n|--dry-run If given, display instead of run actions |
| 2371 | ** -v|--verbose Show a diff in the commit message prompt |
| 2372 | ** --no-prompt This option disables prompting the user for |
| 2373 | ** input and assumes an answer of 'No' for every |
| 2374 | ** question. |
| 2375 | ** --no-warnings Omit all warnings about file contents |
| 2376 | ** --no-verify Do not run before-commit hooks |
| 2377 | ** --nosign Do not attempt to sign this commit with gpg |
| 2378 | ** --nosync Do not auto-sync prior to committing |
| 2379 | ** --override-lock Allow a check-in even though parent is locked |
| 2380 | ** --private Do not sync changes and their descendants |
| 2381 | ** --tag TAG-NAME Assign given tag TAG-NAME to the check-in |
| 2382 | ** --trace Debug tracing |
| 2383 | ** --user-override USER USER to use instead of the current default |
| 2384 | ** |
| 2385 | ** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in |
| 2386 | ** year-month-day form, it may be truncated, the "T" may be replaced by |
| 2387 | ** a space, and it may also name a timezone offset from UTC as "-HH:MM" |
| 2388 | ** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z" |
| 2389 | ** means UTC. |
| 2390 | ** |
| 2391 | ** See also: [[branch]], [[changes]], [[update]], [[extras]], [[sync]] |
| 2392 | */ |
| 2393 | void commit_cmd(void){ |
| 2394 | int hasChanges; /* True if unsaved changes exist */ |
| @@ -2414,10 +2504,11 @@ | |
| 2414 | int allowConflict = 0; /* Allow unresolve merge conflicts */ |
| 2415 | int allowEmpty = 0; /* Allow a commit with no changes */ |
| 2416 | int onlyIfChanges = 0; /* No-op if there are no changes */ |
| 2417 | int allowFork = 0; /* Allow the commit to fork */ |
| 2418 | int allowOlder = 0; /* Allow a commit older than its ancestor */ |
| 2419 | char *zManifestFile; /* Name of the manifest file */ |
| 2420 | int useCksum; /* True if checksums should be computed and verified */ |
| 2421 | int outputManifest; /* True to output "manifest" and "manifest.uuid" */ |
| 2422 | int dryRunFlag; /* True for a test run. Debugging only */ |
| 2423 | CheckinInfo sCiInfo; /* Information about this check-in */ |
| @@ -2439,10 +2530,11 @@ | |
| 2439 | int bRecheck = 0; /* Repeat fork and closed-branch checks*/ |
| 2440 | int bIgnoreSkew = 0; /* --ignore-clock-skew flag */ |
| 2441 | int mxSize; |
| 2442 | char *zCurBranch = 0; /* The current branch name of checkout */ |
| 2443 | char *zNewBranch = 0; /* The branch name after update */ |
| 2444 | |
| 2445 | memset(&sCiInfo, 0, sizeof(sCiInfo)); |
| 2446 | url_proxy_options(); |
| 2447 | /* --sha1sum is an undocumented alias for --hash for backwards compatiblity */ |
| 2448 | useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0; |
| @@ -2469,24 +2561,30 @@ | |
| 2469 | } |
| 2470 | zComment = find_option("comment","m",1); |
| 2471 | forceFlag = find_option("force", "f", 0)!=0; |
| 2472 | allowConflict = find_option("allow-conflict",0,0)!=0; |
| 2473 | allowEmpty = find_option("allow-empty",0,0)!=0; |
| 2474 | onlyIfChanges = find_option("if-changes",0,0)!=0; |
| 2475 | allowFork = find_option("allow-fork",0,0)!=0; |
| 2476 | if( find_option("override-lock",0,0)!=0 ) allowFork = 1; |
| 2477 | allowOlder = find_option("allow-older",0,0)!=0; |
| 2478 | noPrompt = find_option("no-prompt", 0, 0)!=0; |
| 2479 | noWarningFlag = find_option("no-warnings", 0, 0)!=0; |
| 2480 | noVerify = find_option("no-verify",0,0)!=0; |
| 2481 | bTrace = find_option("trace",0,0)!=0; |
| 2482 | sCiInfo.zBranch = find_option("branch","b",1); |
| 2483 | sCiInfo.zColor = find_option("bgcolor",0,1); |
| 2484 | sCiInfo.zBrClr = find_option("branchcolor",0,1); |
| 2485 | sCiInfo.closeFlag = find_option("close",0,0)!=0; |
| 2486 | sCiInfo.integrateFlag = find_option("integrate",0,0)!=0; |
| 2487 | sCiInfo.zMimetype = find_option("mimetype",0,1); |
| 2488 | sCiInfo.verboseFlag = find_option("verbose", "v", 0)!=0; |
| 2489 | while( (zTag = find_option("tag",0,1))!=0 ){ |
| 2490 | if( zTag[0]==0 ) continue; |
| 2491 | sCiInfo.azTag = fossil_realloc((void*)sCiInfo.azTag, |
| 2492 | sizeof(char*)*(nTag+2)); |
| @@ -2498,14 +2596,20 @@ | |
| 2498 | sCiInfo.zUserOvrd = find_option("user-override",0,1); |
| 2499 | noSign = db_get_boolean("omitsign", 0)|noSign; |
| 2500 | if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; } |
| 2501 | useCksum = db_get_boolean("repo-cksum", 1); |
| 2502 | bIgnoreSkew = find_option("ignore-clock-skew",0,0)!=0; |
| 2503 | outputManifest = db_get_manifest_setting(); |
| 2504 | mxSize = db_large_file_size(); |
| 2505 | if( find_option("ignore-oversize",0,0)!=0 ) mxSize = 0; |
| 2506 | verify_all_options(); |
| 2507 | |
| 2508 | /* Get the ID of the parent manifest artifact */ |
| 2509 | vid = db_lget_int("checkout", 0); |
| 2510 | if( vid==0 ){ |
| 2511 | useCksum = 1; |
| @@ -2606,33 +2710,35 @@ | |
| 2606 | fossil_exit(1); |
| 2607 | } |
| 2608 | } |
| 2609 | |
| 2610 | /* So that older versions of Fossil (that do not understand delta- |
| 2611 | ** manifest) can continue to use this repository, do not create a new |
| 2612 | ** delta-manifest unless this repository already contains one or more |
| 2613 | ** delta-manifests, or unless the delta-manifest is explicitly requested |
| 2614 | ** by the --delta option. |
| 2615 | ** |
| 2616 | ** The forbid-delta-manifests setting prevents new delta manifests. |
| 2617 | ** |
| 2618 | ** If the remote repository sent an avoid-delta-manifests pragma on |
| 2619 | ** the autosync above, then also try to avoid deltas, unless the |
| 2620 | ** --delta option is specified. The remote repo will send the |
| 2621 | ** avoid-delta-manifests pragma if it has its "forbid-delta-manifests" |
| 2622 | ** setting enabled. |
| 2623 | */ |
| 2624 | if( !db_get_boolean("seen-delta-manifest",0) |
| 2625 | || db_get_boolean("forbid-delta-manifests",0) |
| 2626 | || g.bAvoidDeltaManifests |
| 2627 | ){ |
| 2628 | if( !forceDelta ) forceBaseline = 1; |
| 2629 | } |
| 2630 | |
| 2631 | |
| 2632 | /* Require confirmation to continue with the check-in if there is |
| 2633 | ** clock skew |
| 2634 | */ |
| 2635 | if( g.clockSkewSeen ){ |
| 2636 | if( bIgnoreSkew!=0 ){ |
| 2637 | cReply = 'y'; |
| 2638 | fossil_warning("Clock skew ignored due to --ignore-clock-skew."); |
| @@ -2787,35 +2893,82 @@ | |
| 2787 | fossil_free(zNewBranch); |
| 2788 | |
| 2789 | /* Always exit the loop on the second pass */ |
| 2790 | if( bRecheck ) break; |
| 2791 | |
| 2792 | |
| 2793 | /* Get the check-in comment. This might involve prompting the |
| 2794 | ** user for the check-in comment, in which case we should resync |
| 2795 | ** to renew the check-in lock and repeat the checks for conflicts. |
| 2796 | */ |
| 2797 | if( zComment ){ |
| 2798 | blob_zero(&comment); |
| 2799 | blob_append(&comment, zComment, -1); |
| 2800 | }else if( zComFile ){ |
| 2801 | blob_zero(&comment); |
| 2802 | blob_read_from_file(&comment, zComFile, ExtFILE); |
| 2803 | blob_to_utf8_no_bom(&comment, 1); |
| 2804 | }else if( !noPrompt ){ |
| 2805 | char *zInit = db_text(0,"SELECT value FROM vvar WHERE name='ci-comment'"); |
| 2806 | prepare_commit_comment(&comment, zInit, &sCiInfo, vid, dryRunFlag); |
| 2807 | if( zInit && zInit[0] && fossil_strcmp(zInit, blob_str(&comment))==0 ){ |
| 2808 | prompt_user("unchanged check-in comment. continue (y/N)? ", &ans); |
| 2809 | cReply = blob_str(&ans)[0]; |
| 2810 | blob_reset(&ans); |
| 2811 | if( cReply!='y' && cReply!='Y' ){ |
| 2812 | fossil_fatal("Commit aborted."); |
| 2813 | } |
| 2814 | } |
| 2815 | free(zInit); |
| 2816 | db_multi_exec("REPLACE INTO vvar VALUES('ci-comment',%B)", &comment); |
| 2817 | db_end_transaction(0); |
| 2818 | db_begin_transaction(); |
| 2819 | if( !g.markPrivate && vid!=0 && !allowFork && !forceFlag ){ |
| 2820 | /* Do another auto-pull, renewing the check-in lock. Then set |
| 2821 | ** bRecheck so that we loop back above to verify that the check-in |
| 2822 |
| --- src/checkin.c | |
| +++ src/checkin.c | |
| @@ -1467,10 +1467,11 @@ | |
| 1467 | CheckinInfo *p, |
| 1468 | int parent_rid, |
| 1469 | int dryRunFlag |
| 1470 | ){ |
| 1471 | Blob prompt; |
| 1472 | int wikiFlags; |
| 1473 | #if defined(_WIN32) || defined(__CYGWIN__) |
| 1474 | int bomSize; |
| 1475 | const unsigned char *bom = get_utf8_bom(&bomSize); |
| 1476 | blob_init(&prompt, (const char *) bom, bomSize); |
| 1477 | if( zInit && zInit[0]){ |
| @@ -1479,14 +1480,37 @@ | |
| 1480 | #else |
| 1481 | blob_init(&prompt, zInit, -1); |
| 1482 | #endif |
| 1483 | blob_append(&prompt, |
| 1484 | "\n" |
| 1485 | "# Enter the commit message. Formatting rules:\n" |
| 1486 | "# * Lines beginning with # are ignored.\n", |
| 1487 | -1 |
| 1488 | ); |
| 1489 | wikiFlags = wiki_convert_flags(1); |
| 1490 | if( wikiFlags & WIKI_LINKSONLY ){ |
| 1491 | blob_append(&prompt,"# * Hyperlinks inside of [...]\n", -1); |
| 1492 | if( wikiFlags & WIKI_NEWLINE ){ |
| 1493 | blob_append(&prompt, |
| 1494 | "# * Newlines are significant and are displayed as written\n", -1); |
| 1495 | }else{ |
| 1496 | blob_append(&prompt, |
| 1497 | "# * Newlines are interpreted as ordinary spaces\n", |
| 1498 | -1 |
| 1499 | ); |
| 1500 | } |
| 1501 | blob_append(&prompt, |
| 1502 | "# * All other text will be displayed as written\n", -1); |
| 1503 | }else{ |
| 1504 | blob_append(&prompt, |
| 1505 | "# * Hyperlinks: [target] or [target|display-text]\n" |
| 1506 | "# * Blank lines cause a paragraph break\n" |
| 1507 | "# * Other text rendered as if it where HTML\n", -1 |
| 1508 | ); |
| 1509 | } |
| 1510 | blob_append(&prompt, "#\n", 2); |
| 1511 | |
| 1512 | if( dryRunFlag ){ |
| 1513 | blob_appendf(&prompt, "# DRY-RUN: This is a test commit. No changes " |
| 1514 | "will be made to the repository\n#\n"); |
| 1515 | } |
| 1516 | blob_appendf(&prompt, "# user: %s\n", |
| @@ -2282,41 +2306,112 @@ | |
| 2306 | char **pA = (char**)a; |
| 2307 | char **pB = (char**)b; |
| 2308 | return fossil_strcmp(pA[0], pB[0]); |
| 2309 | } |
| 2310 | |
| 2311 | /* |
| 2312 | ** SETTING: verify-comments width=8 default=on |
| 2313 | ** |
| 2314 | ** This setting determines how much sanity checking, if any, the |
| 2315 | ** "fossil commit" and "fossil amend" commands do against check-in |
| 2316 | ** comments. Recognized values: |
| 2317 | ** |
| 2318 | ** on (Default) Check for bad syntax and/or broken hyperlinks |
| 2319 | ** in check-in comments and offer the user a chance to |
| 2320 | ** continue editing for interactive sessions, or simply |
| 2321 | ** abort the commit if the comment was entered using -m or -M |
| 2322 | ** |
| 2323 | ** off Do not do syntax checking of any kind |
| 2324 | ** |
| 2325 | ** preview Do all the same checks as "on" but also always preview the |
| 2326 | ** check-in comment to the user during interactive sessions |
| 2327 | ** even if no obvious errors are found, and provide an |
| 2328 | ** opportunity to accept or re-edit |
| 2329 | */ |
| 2330 | |
| 2331 | #if INTERFACE |
| 2332 | #define COMCK_MARKUP 0x01 /* Check for mistakes */ |
| 2333 | #define COMCK_PREVIEW 0x02 /* Always preview, even if no issues found */ |
| 2334 | #endif /* INTERFACE */ |
| 2335 | |
| 2336 | /* |
| 2337 | ** Check for possible formatting errors in the comment string pComment. |
| 2338 | ** |
| 2339 | ** If issues are found, write an appropriate error notice, probably also |
| 2340 | ** including the complete text of the comment formatted to highlight the |
| 2341 | ** problem, to stdout and return non-zero. The return value is some |
| 2342 | ** combination of the COMCK_* flags, depending on what went wrong. |
| 2343 | ** |
| 2344 | ** If no issues are seen, do not output anything and return zero. |
| 2345 | */ |
| 2346 | int verify_comment(Blob *pComment, int mFlags){ |
| 2347 | Blob in, html; |
| 2348 | int mResult; |
| 2349 | int rc = mFlags & COMCK_PREVIEW; |
| 2350 | int wFlags; |
| 2351 | |
| 2352 | if( mFlags==0 ) return 0; |
| 2353 | blob_init(&in, blob_str(pComment), -1); |
| 2354 | blob_init(&html, 0, 0); |
| 2355 | wFlags = wiki_convert_flags(0); |
| 2356 | wFlags &= ~WIKI_NOBADLINKS; |
| 2357 | wFlags |= WIKI_MARK; |
| 2358 | mResult = wiki_convert(&in, &html, wFlags); |
| 2359 | if( mResult & RENDER_ANYERROR ) rc |= COMCK_MARKUP; |
| 2360 | if( rc ){ |
| 2361 | int htot = ((wFlags & WIKI_NEWLINE)!=0 ? 0 : HTOT_FLOW)|HTOT_TRIM; |
| 2362 | Blob txt; |
| 2363 | if( terminal_is_vt100() ) htot |= HTOT_VT100; |
| 2364 | blob_init(&txt, 0, 0); |
| 2365 | html_to_plaintext(blob_str(&html), &txt, htot); |
| 2366 | if( rc & COMCK_MARKUP ){ |
| 2367 | fossil_print("Possible format errors in the check-in comment:\n\n "); |
| 2368 | }else{ |
| 2369 | fossil_print("Preview of the check-in comment:\n\n "); |
| 2370 | } |
| 2371 | if( wFlags & WIKI_NEWLINE ){ |
| 2372 | Blob line; |
| 2373 | char *zIndent = ""; |
| 2374 | while( blob_line(&txt, &line) ){ |
| 2375 | fossil_print("%s%b", zIndent, &line); |
| 2376 | zIndent = " "; |
| 2377 | } |
| 2378 | fossil_print("\n"); |
| 2379 | }else{ |
| 2380 | comment_print(blob_str(&txt), 0, 3, -1, get_comment_format()); |
| 2381 | } |
| 2382 | fossil_print("\n"); |
| 2383 | fflush(stdout); |
| 2384 | blob_reset(&txt); |
| 2385 | } |
| 2386 | blob_reset(&html); |
| 2387 | blob_reset(&in); |
| 2388 | return rc; |
| 2389 | } |
| 2390 | |
| 2391 | /* |
| 2392 | ** COMMAND: ci# |
| 2393 | ** COMMAND: commit |
| 2394 | ** |
| 2395 | ** Usage: %fossil commit ?OPTIONS? ?FILE...? |
| 2396 | ** or: %fossil ci ?OPTIONS? ?FILE...? |
| 2397 | ** |
| 2398 | ** Create a new check-in containing all of the changes in the current |
| 2399 | ** check-out. All changes are committed unless some subset of files |
| 2400 | ** is specified on the command line, in which case only the named files |
| 2401 | ** become part of the new check-in. |
| 2402 | ** |
| 2403 | ** You will be prompted to enter a check-in comment unless the comment |
| 2404 | ** has been specified on the command-line using "-m" or "-M". The |
| 2405 | ** text editor used is determined by the "editor" setting, or by the |
| 2406 | ** "VISUAL" or "EDITOR" environment variables. Commit message text is |
| 2407 | ** interpreted as fossil-wiki format. Potentially misformatted check-in |
| 2408 | ** comment text is detected and reported unless the --no-verify-comment |
| 2409 | ** option is used. |
| 2410 | ** |
| 2411 | ** The --branch option followed by a branch name causes the new |
| 2412 | ** check-in to be placed in a newly-created branch with name specified. |
| 2413 | ** |
| 2414 | ** A check-in is not permitted to fork unless the --allow-fork option |
| 2415 | ** appears. An empty check-in (i.e. with nothing changed) is not |
| 2416 | ** allowed unless the --allow-empty option appears. A check-in may not |
| 2417 | ** be older than its ancestor unless the --allow-older option appears. |
| @@ -2328,21 +2423,16 @@ | |
| 2423 | ** unless the interactive user chooses to proceed. If there is no |
| 2424 | ** interactive user or these warnings should be skipped for some other |
| 2425 | ** reason, the --no-warnings option may be used. A check-in is not |
| 2426 | ** allowed against a closed leaf. |
| 2427 | ** |
| 2428 | ** The --private option creates a private check-in that is never synced. |
| 2429 | ** Children of private check-ins are automatically private. |
| 2430 | ** |
| 2431 | ** The --tag option applies the symbolic tag name to the check-in. |
| 2432 | ** The --tag option can be repeated to assign multiple tags to a check-in. |
| 2433 | ** For example: "... --tag release --tag version-1.2.3 ..." |
| 2434 | ** |
| 2435 | ** Options: |
| 2436 | ** --allow-conflict Allow unresolved merge conflicts |
| 2437 | ** --allow-empty Allow a commit with no changes |
| 2438 | ** --allow-fork Allow the commit to fork |
| @@ -2350,45 +2440,45 @@ | |
| 2440 | ** --baseline Use a baseline manifest in the commit process |
| 2441 | ** --bgcolor COLOR Apply COLOR to this one check-in only |
| 2442 | ** --branch NEW-BRANCH-NAME Check in to this new branch |
| 2443 | ** --branchcolor COLOR Apply given COLOR to the branch |
| 2444 | ** --close Close the branch being committed |
| 2445 | ** --date-override DATETIME Make DATETIME the time of the check-in. |
| 2446 | ** Useful when importing historical check-ins |
| 2447 | ** from another version control system. |
| 2448 | ** --delta Use a delta manifest in the commit process |
| 2449 | ** --hash Verify file status using hashing rather |
| 2450 | ** than relying on filesystem mtimes |
| 2451 | ** --if-changes Make this command a silent no-op if there |
| 2452 | ** are no changes |
| 2453 | ** --ignore-clock-skew If a clock skew is detected, ignore it and |
| 2454 | ** behave as if the user had entered 'yes' to |
| 2455 | ** the question of whether to proceed despite |
| 2456 | ** the skew. |
| 2457 | ** --ignore-oversize Do not warn the user about oversized files |
| 2458 | ** --integrate Close all merged-in branches |
| 2459 | ** -m|--comment COMMENT-TEXT Use COMMENT-TEXT as the check-in comment |
| 2460 | ** -M|--message-file FILE Read the check-in comment from FILE |
| 2461 | ** -n|--dry-run Do not actually create a new check-in. Just |
| 2462 | ** show what would have happened. For debugging. |
| 2463 | ** -v|--verbose Show a diff in the commit message prompt |
| 2464 | ** --no-prompt This option disables prompting the user for |
| 2465 | ** input and assumes an answer of 'No' for every |
| 2466 | ** question. |
| 2467 | ** --no-warnings Omit all warnings about file contents |
| 2468 | ** --no-verify Do not run before-commit hooks |
| 2469 | ** --no-verify-comment Do not validate the check-in comment |
| 2470 | ** --nosign Do not attempt to sign this commit with gpg |
| 2471 | ** --nosync Do not auto-sync prior to committing |
| 2472 | ** --override-lock Allow a check-in even though parent is locked |
| 2473 | ** --private Never sync the resulting check-in and make |
| 2474 | ** all descendants private too. |
| 2475 | ** --proxy PROXY Use PROXY as http proxy during sync operation |
| 2476 | ** --tag TAG-NAME Add TAG-NAME to the check-in. May be repeated. |
| 2477 | ** --trace Debug tracing |
| 2478 | ** --user-override USER Record USER as the login that created the |
| 2479 | ** new check-in, rather that the current user. |
| 2480 | ** |
| 2481 | ** See also: [[branch]], [[changes]], [[update]], [[extras]], [[sync]] |
| 2482 | */ |
| 2483 | void commit_cmd(void){ |
| 2484 | int hasChanges; /* True if unsaved changes exist */ |
| @@ -2414,10 +2504,11 @@ | |
| 2504 | int allowConflict = 0; /* Allow unresolve merge conflicts */ |
| 2505 | int allowEmpty = 0; /* Allow a commit with no changes */ |
| 2506 | int onlyIfChanges = 0; /* No-op if there are no changes */ |
| 2507 | int allowFork = 0; /* Allow the commit to fork */ |
| 2508 | int allowOlder = 0; /* Allow a commit older than its ancestor */ |
| 2509 | int noVerifyCom = 0; /* Allow suspicious check-in comments */ |
| 2510 | char *zManifestFile; /* Name of the manifest file */ |
| 2511 | int useCksum; /* True if checksums should be computed and verified */ |
| 2512 | int outputManifest; /* True to output "manifest" and "manifest.uuid" */ |
| 2513 | int dryRunFlag; /* True for a test run. Debugging only */ |
| 2514 | CheckinInfo sCiInfo; /* Information about this check-in */ |
| @@ -2439,10 +2530,11 @@ | |
| 2530 | int bRecheck = 0; /* Repeat fork and closed-branch checks*/ |
| 2531 | int bIgnoreSkew = 0; /* --ignore-clock-skew flag */ |
| 2532 | int mxSize; |
| 2533 | char *zCurBranch = 0; /* The current branch name of checkout */ |
| 2534 | char *zNewBranch = 0; /* The branch name after update */ |
| 2535 | int ckComFlgs; /* Flags passed to verify_comment() */ |
| 2536 | |
| 2537 | memset(&sCiInfo, 0, sizeof(sCiInfo)); |
| 2538 | url_proxy_options(); |
| 2539 | /* --sha1sum is an undocumented alias for --hash for backwards compatiblity */ |
| 2540 | useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0; |
| @@ -2469,24 +2561,30 @@ | |
| 2561 | } |
| 2562 | zComment = find_option("comment","m",1); |
| 2563 | forceFlag = find_option("force", "f", 0)!=0; |
| 2564 | allowConflict = find_option("allow-conflict",0,0)!=0; |
| 2565 | allowEmpty = find_option("allow-empty",0,0)!=0; |
| 2566 | noVerifyCom = find_option("no-verify-comment",0,0)!=0; |
| 2567 | onlyIfChanges = find_option("if-changes",0,0)!=0; |
| 2568 | allowFork = find_option("allow-fork",0,0)!=0; |
| 2569 | if( find_option("override-lock",0,0)!=0 ) allowFork = 1; |
| 2570 | allowOlder = find_option("allow-older",0,0)!=0; |
| 2571 | noPrompt = find_option("no-prompt", 0, 0)!=0; |
| 2572 | noWarningFlag = find_option("no-warnings", 0, 0)!=0; |
| 2573 | noVerify = find_option("no-verify",0,0)!=0; |
| 2574 | bTrace = find_option("trace",0,0)!=0; |
| 2575 | sCiInfo.zBranch = find_option("branch","b",1); |
| 2576 | |
| 2577 | /* NB: the --bgcolor and --branchcolor flags still work, but are |
| 2578 | ** now undocumented, to discourage their use. --mimetype has never |
| 2579 | ** been used for anything, so also leave it undocumented */ |
| 2580 | sCiInfo.zColor = find_option("bgcolor",0,1); /* Deprecated, undocumented*/ |
| 2581 | sCiInfo.zBrClr = find_option("branchcolor",0,1); /* Deprecated, undocumented*/ |
| 2582 | sCiInfo.zMimetype = find_option("mimetype",0,1); /* Deprecated, undocumented*/ |
| 2583 | |
| 2584 | sCiInfo.closeFlag = find_option("close",0,0)!=0; |
| 2585 | sCiInfo.integrateFlag = find_option("integrate",0,0)!=0; |
| 2586 | sCiInfo.verboseFlag = find_option("verbose", "v", 0)!=0; |
| 2587 | while( (zTag = find_option("tag",0,1))!=0 ){ |
| 2588 | if( zTag[0]==0 ) continue; |
| 2589 | sCiInfo.azTag = fossil_realloc((void*)sCiInfo.azTag, |
| 2590 | sizeof(char*)*(nTag+2)); |
| @@ -2498,14 +2596,20 @@ | |
| 2596 | sCiInfo.zUserOvrd = find_option("user-override",0,1); |
| 2597 | noSign = db_get_boolean("omitsign", 0)|noSign; |
| 2598 | if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; } |
| 2599 | useCksum = db_get_boolean("repo-cksum", 1); |
| 2600 | bIgnoreSkew = find_option("ignore-clock-skew",0,0)!=0; |
| 2601 | outputManifest = db_get_manifest_setting(0); |
| 2602 | mxSize = db_large_file_size(); |
| 2603 | if( find_option("ignore-oversize",0,0)!=0 ) mxSize = 0; |
| 2604 | verify_all_options(); |
| 2605 | |
| 2606 | /* The --no-warnings flag and the --force flag each imply |
| 2607 | ** the --no-verify-comment flag */ |
| 2608 | if( noWarningFlag || forceFlag ){ |
| 2609 | noVerifyCom = 1; |
| 2610 | } |
| 2611 | |
| 2612 | /* Get the ID of the parent manifest artifact */ |
| 2613 | vid = db_lget_int("checkout", 0); |
| 2614 | if( vid==0 ){ |
| 2615 | useCksum = 1; |
| @@ -2606,33 +2710,35 @@ | |
| 2710 | fossil_exit(1); |
| 2711 | } |
| 2712 | } |
| 2713 | |
| 2714 | /* So that older versions of Fossil (that do not understand delta- |
| 2715 | ** manifest) can continue to use this repository, and because |
| 2716 | ** delta manifests are usually a bad idea unless the repository |
| 2717 | ** has a really large number of files, do not create a new |
| 2718 | ** delta-manifest unless this repository already contains one or more |
| 2719 | ** delta-manifests, or unless the delta-manifest is explicitly requested |
| 2720 | ** by the --delta option. |
| 2721 | ** |
| 2722 | ** The forbid-delta-manifests setting prevents new delta manifests, |
| 2723 | ** even if the --delta option is used. |
| 2724 | ** |
| 2725 | ** If the remote repository sent an avoid-delta-manifests pragma on |
| 2726 | ** the autosync above, then also forbid delta manifests, even if the |
| 2727 | ** --delta option is specified. The remote repo will send the |
| 2728 | ** avoid-delta-manifests pragma if its "forbid-delta-manifests" |
| 2729 | ** setting is enabled. |
| 2730 | */ |
| 2731 | if( !(forceDelta || db_get_boolean("seen-delta-manifest",0)) |
| 2732 | || db_get_boolean("forbid-delta-manifests",0) |
| 2733 | || g.bAvoidDeltaManifests |
| 2734 | ){ |
| 2735 | forceBaseline = 1; |
| 2736 | } |
| 2737 | |
| 2738 | /* Require confirmation to continue with the check-in if there is |
| 2739 | ** clock skew. This helps to prevent timewarps. |
| 2740 | */ |
| 2741 | if( g.clockSkewSeen ){ |
| 2742 | if( bIgnoreSkew!=0 ){ |
| 2743 | cReply = 'y'; |
| 2744 | fossil_warning("Clock skew ignored due to --ignore-clock-skew."); |
| @@ -2787,35 +2893,82 @@ | |
| 2893 | fossil_free(zNewBranch); |
| 2894 | |
| 2895 | /* Always exit the loop on the second pass */ |
| 2896 | if( bRecheck ) break; |
| 2897 | |
| 2898 | |
| 2899 | /* Figure out how much comment verification is requested */ |
| 2900 | if( noVerifyCom ){ |
| 2901 | ckComFlgs = 0; |
| 2902 | }else{ |
| 2903 | const char *zVerComs = db_get("verify-comments","on"); |
| 2904 | if( is_false(zVerComs) ){ |
| 2905 | ckComFlgs = 0; |
| 2906 | }else if( strcmp(zVerComs,"preview")==0 ){ |
| 2907 | ckComFlgs = COMCK_PREVIEW | COMCK_MARKUP; |
| 2908 | }else{ |
| 2909 | ckComFlgs = COMCK_MARKUP; |
| 2910 | } |
| 2911 | } |
| 2912 | |
| 2913 | /* Get the check-in comment. This might involve prompting the |
| 2914 | ** user for the check-in comment, in which case we should resync |
| 2915 | ** to renew the check-in lock and repeat the checks for conflicts. |
| 2916 | */ |
| 2917 | if( zComment ){ |
| 2918 | blob_zero(&comment); |
| 2919 | blob_append(&comment, zComment, -1); |
| 2920 | ckComFlgs &= ~COMCK_PREVIEW; |
| 2921 | if( verify_comment(&comment, ckComFlgs) ){ |
| 2922 | fossil_fatal("Commit aborted; " |
| 2923 | "use --no-verify-comment to override"); |
| 2924 | } |
| 2925 | }else if( zComFile ){ |
| 2926 | blob_zero(&comment); |
| 2927 | blob_read_from_file(&comment, zComFile, ExtFILE); |
| 2928 | blob_to_utf8_no_bom(&comment, 1); |
| 2929 | ckComFlgs &= ~COMCK_PREVIEW; |
| 2930 | if( verify_comment(&comment, ckComFlgs) ){ |
| 2931 | fossil_fatal("Commit aborted; " |
| 2932 | "use --no-verify-comment to override"); |
| 2933 | } |
| 2934 | }else if( !noPrompt ){ |
| 2935 | while( 1/*exit-by-break*/ ){ |
| 2936 | int rc; |
| 2937 | char *zInit; |
| 2938 | zInit = db_text(0,"SELECT value FROM vvar WHERE name='ci-comment'"); |
| 2939 | prepare_commit_comment(&comment, zInit, &sCiInfo, vid, dryRunFlag); |
| 2940 | db_multi_exec("REPLACE INTO vvar VALUES('ci-comment',%B)", &comment); |
| 2941 | if( (rc = verify_comment(&comment, ckComFlgs))!=0 ){ |
| 2942 | if( rc==COMCK_PREVIEW ){ |
| 2943 | prompt_user("Continue, abort, or edit? (C/a/e)? ", &ans); |
| 2944 | }else{ |
| 2945 | prompt_user("Edit, abort, or continue (E/a/c)? ", &ans); |
| 2946 | } |
| 2947 | cReply = blob_str(&ans)[0]; |
| 2948 | cReply = fossil_tolower(cReply); |
| 2949 | blob_reset(&ans); |
| 2950 | if( cReply=='a' ){ |
| 2951 | fossil_fatal("Commit aborted."); |
| 2952 | } |
| 2953 | if( cReply=='e' || (cReply!='c' && rc!=COMCK_PREVIEW) ){ |
| 2954 | fossil_free(zInit); |
| 2955 | continue; |
| 2956 | } |
| 2957 | } |
| 2958 | if( zInit && zInit[0] && fossil_strcmp(zInit, blob_str(&comment))==0 ){ |
| 2959 | prompt_user("unchanged check-in comment. continue (y/N)? ", &ans); |
| 2960 | cReply = blob_str(&ans)[0]; |
| 2961 | blob_reset(&ans); |
| 2962 | if( cReply!='y' && cReply!='Y' ){ |
| 2963 | fossil_fatal("Commit aborted."); |
| 2964 | } |
| 2965 | } |
| 2966 | fossil_free(zInit); |
| 2967 | break; |
| 2968 | } |
| 2969 | |
| 2970 | db_end_transaction(0); |
| 2971 | db_begin_transaction(); |
| 2972 | if( !g.markPrivate && vid!=0 && !allowFork && !forceFlag ){ |
| 2973 | /* Do another auto-pull, renewing the check-in lock. Then set |
| 2974 | ** bRecheck so that we loop back above to verify that the check-in |
| 2975 |
+11
-5
| --- src/checkout.c | ||
| +++ src/checkout.c | ||
| @@ -173,11 +173,11 @@ | ||
| 173 | 173 | */ |
| 174 | 174 | void manifest_to_disk(int vid){ |
| 175 | 175 | char *zManFile; |
| 176 | 176 | int flg; |
| 177 | 177 | |
| 178 | - flg = db_get_manifest_setting(); | |
| 178 | + flg = db_get_manifest_setting(0); | |
| 179 | 179 | |
| 180 | 180 | if( flg & MFESTFLG_RAW ){ |
| 181 | 181 | Blob manifest = BLOB_INITIALIZER; |
| 182 | 182 | content_get(vid, &manifest); |
| 183 | 183 | sterilize_manifest(&manifest, CFTYPE_MANIFEST); |
| @@ -276,13 +276,14 @@ | ||
| 276 | 276 | ** |
| 277 | 277 | ** The --latest flag can be used in place of VERSION to check-out the |
| 278 | 278 | ** latest version in the repository. |
| 279 | 279 | ** |
| 280 | 280 | ** Options: |
| 281 | -** --force Ignore edited files in the current check-out | |
| 282 | -** --keep Only update the manifest file(s) | |
| 281 | +** -f|--force Ignore edited files in the current check-out | |
| 282 | +** -k|--keep Only update the manifest file(s) | |
| 283 | 283 | ** --force-missing Force check-out even if content is missing |
| 284 | +** --prompt Prompt before overwriting when --force is used | |
| 284 | 285 | ** --setmtime Set timestamps of all files to match their SCM-side |
| 285 | 286 | ** times (the timestamp of the last check-in which modified |
| 286 | 287 | ** them) |
| 287 | 288 | ** |
| 288 | 289 | ** See also: [[update]] |
| @@ -298,16 +299,21 @@ | ||
| 298 | 299 | int setmtimeFlag; /* --setmtime. Set mtimes on files */ |
| 299 | 300 | Blob cksum1, cksum1b, cksum2; |
| 300 | 301 | |
| 301 | 302 | db_must_be_within_tree(); |
| 302 | 303 | db_begin_transaction(); |
| 303 | - forceFlag = find_option("force","f",0)!=0; | |
| 304 | 304 | forceMissingFlag = find_option("force-missing",0,0)!=0; |
| 305 | - keepFlag = find_option("keep",0,0)!=0; | |
| 305 | + keepFlag = find_option("keep","k",0)!=0; | |
| 306 | + forceFlag = find_option("force","f",0)!=0; | |
| 306 | 307 | latestFlag = find_option("latest",0,0)!=0; |
| 307 | 308 | promptFlag = find_option("prompt",0,0)!=0 || forceFlag==0; |
| 308 | 309 | setmtimeFlag = find_option("setmtime",0,0)!=0; |
| 310 | + | |
| 311 | + if( keepFlag != 0 ){ | |
| 312 | + /* After flag collection, in order not to affect promptFlag */ | |
| 313 | + forceFlag=1; | |
| 314 | + } | |
| 309 | 315 | |
| 310 | 316 | /* We should be done with options.. */ |
| 311 | 317 | verify_all_options(); |
| 312 | 318 | |
| 313 | 319 | if( (latestFlag!=0 && g.argc!=2) || (latestFlag==0 && g.argc!=3) ){ |
| 314 | 320 |
| --- src/checkout.c | |
| +++ src/checkout.c | |
| @@ -173,11 +173,11 @@ | |
| 173 | */ |
| 174 | void manifest_to_disk(int vid){ |
| 175 | char *zManFile; |
| 176 | int flg; |
| 177 | |
| 178 | flg = db_get_manifest_setting(); |
| 179 | |
| 180 | if( flg & MFESTFLG_RAW ){ |
| 181 | Blob manifest = BLOB_INITIALIZER; |
| 182 | content_get(vid, &manifest); |
| 183 | sterilize_manifest(&manifest, CFTYPE_MANIFEST); |
| @@ -276,13 +276,14 @@ | |
| 276 | ** |
| 277 | ** The --latest flag can be used in place of VERSION to check-out the |
| 278 | ** latest version in the repository. |
| 279 | ** |
| 280 | ** Options: |
| 281 | ** --force Ignore edited files in the current check-out |
| 282 | ** --keep Only update the manifest file(s) |
| 283 | ** --force-missing Force check-out even if content is missing |
| 284 | ** --setmtime Set timestamps of all files to match their SCM-side |
| 285 | ** times (the timestamp of the last check-in which modified |
| 286 | ** them) |
| 287 | ** |
| 288 | ** See also: [[update]] |
| @@ -298,16 +299,21 @@ | |
| 298 | int setmtimeFlag; /* --setmtime. Set mtimes on files */ |
| 299 | Blob cksum1, cksum1b, cksum2; |
| 300 | |
| 301 | db_must_be_within_tree(); |
| 302 | db_begin_transaction(); |
| 303 | forceFlag = find_option("force","f",0)!=0; |
| 304 | forceMissingFlag = find_option("force-missing",0,0)!=0; |
| 305 | keepFlag = find_option("keep",0,0)!=0; |
| 306 | latestFlag = find_option("latest",0,0)!=0; |
| 307 | promptFlag = find_option("prompt",0,0)!=0 || forceFlag==0; |
| 308 | setmtimeFlag = find_option("setmtime",0,0)!=0; |
| 309 | |
| 310 | /* We should be done with options.. */ |
| 311 | verify_all_options(); |
| 312 | |
| 313 | if( (latestFlag!=0 && g.argc!=2) || (latestFlag==0 && g.argc!=3) ){ |
| 314 |
| --- src/checkout.c | |
| +++ src/checkout.c | |
| @@ -173,11 +173,11 @@ | |
| 173 | */ |
| 174 | void manifest_to_disk(int vid){ |
| 175 | char *zManFile; |
| 176 | int flg; |
| 177 | |
| 178 | flg = db_get_manifest_setting(0); |
| 179 | |
| 180 | if( flg & MFESTFLG_RAW ){ |
| 181 | Blob manifest = BLOB_INITIALIZER; |
| 182 | content_get(vid, &manifest); |
| 183 | sterilize_manifest(&manifest, CFTYPE_MANIFEST); |
| @@ -276,13 +276,14 @@ | |
| 276 | ** |
| 277 | ** The --latest flag can be used in place of VERSION to check-out the |
| 278 | ** latest version in the repository. |
| 279 | ** |
| 280 | ** Options: |
| 281 | ** -f|--force Ignore edited files in the current check-out |
| 282 | ** -k|--keep Only update the manifest file(s) |
| 283 | ** --force-missing Force check-out even if content is missing |
| 284 | ** --prompt Prompt before overwriting when --force is used |
| 285 | ** --setmtime Set timestamps of all files to match their SCM-side |
| 286 | ** times (the timestamp of the last check-in which modified |
| 287 | ** them) |
| 288 | ** |
| 289 | ** See also: [[update]] |
| @@ -298,16 +299,21 @@ | |
| 299 | int setmtimeFlag; /* --setmtime. Set mtimes on files */ |
| 300 | Blob cksum1, cksum1b, cksum2; |
| 301 | |
| 302 | db_must_be_within_tree(); |
| 303 | db_begin_transaction(); |
| 304 | forceMissingFlag = find_option("force-missing",0,0)!=0; |
| 305 | keepFlag = find_option("keep","k",0)!=0; |
| 306 | forceFlag = find_option("force","f",0)!=0; |
| 307 | latestFlag = find_option("latest",0,0)!=0; |
| 308 | promptFlag = find_option("prompt",0,0)!=0 || forceFlag==0; |
| 309 | setmtimeFlag = find_option("setmtime",0,0)!=0; |
| 310 | |
| 311 | if( keepFlag != 0 ){ |
| 312 | /* After flag collection, in order not to affect promptFlag */ |
| 313 | forceFlag=1; |
| 314 | } |
| 315 | |
| 316 | /* We should be done with options.. */ |
| 317 | verify_all_options(); |
| 318 | |
| 319 | if( (latestFlag!=0 && g.argc!=2) || (latestFlag==0 && g.argc!=3) ){ |
| 320 |
+1
| --- src/clone.c | ||
| +++ src/clone.c | ||
| @@ -129,10 +129,11 @@ | ||
| 129 | 129 | ** check-out |
| 130 | 130 | ** --nocompress Omit extra delta compression |
| 131 | 131 | ** --no-open Clone only. Do not open a check-out. |
| 132 | 132 | ** --once Don't remember the URI. |
| 133 | 133 | ** --private Also clone private branches |
| 134 | +** --proxy PROXY Use the specified HTTP proxy | |
| 134 | 135 | ** --save-http-password Remember the HTTP password without asking |
| 135 | 136 | ** -c|--ssh-command SSH Use SSH as the "ssh" command |
| 136 | 137 | ** --ssl-identity FILENAME Use the SSL identity if requested by the server |
| 137 | 138 | ** --transport-command CMD Use CMD to move messages to the server and back |
| 138 | 139 | ** -u|--unversioned Also sync unversioned content |
| 139 | 140 |
| --- src/clone.c | |
| +++ src/clone.c | |
| @@ -129,10 +129,11 @@ | |
| 129 | ** check-out |
| 130 | ** --nocompress Omit extra delta compression |
| 131 | ** --no-open Clone only. Do not open a check-out. |
| 132 | ** --once Don't remember the URI. |
| 133 | ** --private Also clone private branches |
| 134 | ** --save-http-password Remember the HTTP password without asking |
| 135 | ** -c|--ssh-command SSH Use SSH as the "ssh" command |
| 136 | ** --ssl-identity FILENAME Use the SSL identity if requested by the server |
| 137 | ** --transport-command CMD Use CMD to move messages to the server and back |
| 138 | ** -u|--unversioned Also sync unversioned content |
| 139 |
| --- src/clone.c | |
| +++ src/clone.c | |
| @@ -129,10 +129,11 @@ | |
| 129 | ** check-out |
| 130 | ** --nocompress Omit extra delta compression |
| 131 | ** --no-open Clone only. Do not open a check-out. |
| 132 | ** --once Don't remember the URI. |
| 133 | ** --private Also clone private branches |
| 134 | ** --proxy PROXY Use the specified HTTP proxy |
| 135 | ** --save-http-password Remember the HTTP password without asking |
| 136 | ** -c|--ssh-command SSH Use SSH as the "ssh" command |
| 137 | ** --ssl-identity FILENAME Use the SSL identity if requested by the server |
| 138 | ** --transport-command CMD Use CMD to move messages to the server and back |
| 139 | ** -u|--unversioned Also sync unversioned content |
| 140 |
+333
| --- src/color.c | ||
| +++ src/color.c | ||
| @@ -20,10 +20,262 @@ | ||
| 20 | 20 | ** |
| 21 | 21 | */ |
| 22 | 22 | #include "config.h" |
| 23 | 23 | #include <string.h> |
| 24 | 24 | #include "color.h" |
| 25 | + | |
| 26 | +/* | |
| 27 | +** 140 standard CSS color names and their corresponding RGB values, | |
| 28 | +** in alphabetical order by name so that we can do a binary search | |
| 29 | +** for lookup. | |
| 30 | +*/ | |
| 31 | +static const struct CssColors { | |
| 32 | + const char *zName; /* CSS Color name, lower case */ | |
| 33 | + unsigned int iRGB; /* Corresponding RGB value */ | |
| 34 | +} aCssColors[] = { | |
| 35 | + { "aliceblue", 0xf0f8ff }, | |
| 36 | + { "antiquewhite", 0xfaebd7 }, | |
| 37 | + { "aqua", 0x00ffff }, | |
| 38 | + { "aquamarine", 0x7fffd4 }, | |
| 39 | + { "azure", 0xf0ffff }, | |
| 40 | + { "beige", 0xf5f5dc }, | |
| 41 | + { "bisque", 0xffe4c4 }, | |
| 42 | + { "black", 0x000000 }, | |
| 43 | + { "blanchedalmond", 0xffebcd }, | |
| 44 | + { "blue", 0x0000ff }, | |
| 45 | + { "blueviolet", 0x8a2be2 }, | |
| 46 | + { "brown", 0xa52a2a }, | |
| 47 | + { "burlywood", 0xdeb887 }, | |
| 48 | + { "cadetblue", 0x5f9ea0 }, | |
| 49 | + { "chartreuse", 0x7fff00 }, | |
| 50 | + { "chocolate", 0xd2691e }, | |
| 51 | + { "coral", 0xff7f50 }, | |
| 52 | + { "cornflowerblue", 0x6495ed }, | |
| 53 | + { "cornsilk", 0xfff8dc }, | |
| 54 | + { "crimson", 0xdc143c }, | |
| 55 | + { "cyan", 0x00ffff }, | |
| 56 | + { "darkblue", 0x00008b }, | |
| 57 | + { "darkcyan", 0x008b8b }, | |
| 58 | + { "darkgoldenrod", 0xb8860b }, | |
| 59 | + { "darkgray", 0xa9a9a9 }, | |
| 60 | + { "darkgreen", 0x006400 }, | |
| 61 | + { "darkkhaki", 0xbdb76b }, | |
| 62 | + { "darkmagenta", 0x8b008b }, | |
| 63 | + { "darkolivegreen", 0x556b2f }, | |
| 64 | + { "darkorange", 0xff8c00 }, | |
| 65 | + { "darkorchid", 0x9932cc }, | |
| 66 | + { "darkred", 0x8b0000 }, | |
| 67 | + { "darksalmon", 0xe9967a }, | |
| 68 | + { "darkseagreen", 0x8fbc8f }, | |
| 69 | + { "darkslateblue", 0x483d8b }, | |
| 70 | + { "darkslategray", 0x2f4f4f }, | |
| 71 | + { "darkturquoise", 0x00ced1 }, | |
| 72 | + { "darkviolet", 0x9400d3 }, | |
| 73 | + { "deeppink", 0xff1493 }, | |
| 74 | + { "deepskyblue", 0x00bfff }, | |
| 75 | + { "dimgray", 0x696969 }, | |
| 76 | + { "dodgerblue", 0x1e90ff }, | |
| 77 | + { "firebrick", 0xb22222 }, | |
| 78 | + { "floralwhite", 0xfffaf0 }, | |
| 79 | + { "forestgreen", 0x228b22 }, | |
| 80 | + { "fuchsia", 0xff00ff }, | |
| 81 | + { "gainsboro", 0xdcdcdc }, | |
| 82 | + { "ghostwhite", 0xf8f8ff }, | |
| 83 | + { "gold", 0xffd700 }, | |
| 84 | + { "goldenrod", 0xdaa520 }, | |
| 85 | + { "gray", 0x808080 }, | |
| 86 | + { "green", 0x008000 }, | |
| 87 | + { "greenyellow", 0xadff2f }, | |
| 88 | + { "honeydew", 0xf0fff0 }, | |
| 89 | + { "hotpink", 0xff69b4 }, | |
| 90 | + { "indianred", 0xcd5c5c }, | |
| 91 | + { "indigo", 0x4b0082 }, | |
| 92 | + { "ivory", 0xfffff0 }, | |
| 93 | + { "khaki", 0xf0e68c }, | |
| 94 | + { "lavender", 0xe6e6fa }, | |
| 95 | + { "lavenderblush", 0xfff0f5 }, | |
| 96 | + { "lawngreen", 0x7cfc00 }, | |
| 97 | + { "lemonchiffon", 0xfffacd }, | |
| 98 | + { "lightblue", 0xadd8e6 }, | |
| 99 | + { "lightcoral", 0xf08080 }, | |
| 100 | + { "lightcyan", 0xe0ffff }, | |
| 101 | + { "lightgoldenrodyellow", 0xfafad2 }, | |
| 102 | + { "lightgrey", 0xd3d3d3 }, | |
| 103 | + { "lightgreen", 0x90ee90 }, | |
| 104 | + { "lightpink", 0xffb6c1 }, | |
| 105 | + { "lightsalmon", 0xffa07a }, | |
| 106 | + { "lightseagreen", 0x20b2aa }, | |
| 107 | + { "lightskyblue", 0x87cefa }, | |
| 108 | + { "lightslategray", 0x778899 }, | |
| 109 | + { "lightsteelblue", 0xb0c4de }, | |
| 110 | + { "lightyellow", 0xffffe0 }, | |
| 111 | + { "lime", 0x00ff00 }, | |
| 112 | + { "limegreen", 0x32cd32 }, | |
| 113 | + { "linen", 0xfaf0e6 }, | |
| 114 | + { "magenta", 0xff00ff }, | |
| 115 | + { "maroon", 0x800000 }, | |
| 116 | + { "mediumaquamarine", 0x66cdaa }, | |
| 117 | + { "mediumblue", 0x0000cd }, | |
| 118 | + { "mediumorchid", 0xba55d3 }, | |
| 119 | + { "mediumpurple", 0x9370d8 }, | |
| 120 | + { "mediumseagreen", 0x3cb371 }, | |
| 121 | + { "mediumslateblue", 0x7b68ee }, | |
| 122 | + { "mediumspringgreen", 0x00fa9a }, | |
| 123 | + { "mediumturquoise", 0x48d1cc }, | |
| 124 | + { "mediumvioletred", 0xc71585 }, | |
| 125 | + { "midnightblue", 0x191970 }, | |
| 126 | + { "mintcream", 0xf5fffa }, | |
| 127 | + { "mistyrose", 0xffe4e1 }, | |
| 128 | + { "moccasin", 0xffe4b5 }, | |
| 129 | + { "navajowhite", 0xffdead }, | |
| 130 | + { "navy", 0x000080 }, | |
| 131 | + { "oldlace", 0xfdf5e6 }, | |
| 132 | + { "olive", 0x808000 }, | |
| 133 | + { "olivedrab", 0x6b8e23 }, | |
| 134 | + { "orange", 0xffa500 }, | |
| 135 | + { "orangered", 0xff4500 }, | |
| 136 | + { "orchid", 0xda70d6 }, | |
| 137 | + { "palegoldenrod", 0xeee8aa }, | |
| 138 | + { "palegreen", 0x98fb98 }, | |
| 139 | + { "paleturquoise", 0xafeeee }, | |
| 140 | + { "palevioletred", 0xd87093 }, | |
| 141 | + { "papayawhip", 0xffefd5 }, | |
| 142 | + { "peachpuff", 0xffdab9 }, | |
| 143 | + { "peru", 0xcd853f }, | |
| 144 | + { "pink", 0xffc0cb }, | |
| 145 | + { "plum", 0xdda0dd }, | |
| 146 | + { "powderblue", 0xb0e0e6 }, | |
| 147 | + { "purple", 0x800080 }, | |
| 148 | + { "red", 0xff0000 }, | |
| 149 | + { "rosybrown", 0xbc8f8f }, | |
| 150 | + { "royalblue", 0x4169e1 }, | |
| 151 | + { "saddlebrown", 0x8b4513 }, | |
| 152 | + { "salmon", 0xfa8072 }, | |
| 153 | + { "sandybrown", 0xf4a460 }, | |
| 154 | + { "seagreen", 0x2e8b57 }, | |
| 155 | + { "seashell", 0xfff5ee }, | |
| 156 | + { "sienna", 0xa0522d }, | |
| 157 | + { "silver", 0xc0c0c0 }, | |
| 158 | + { "skyblue", 0x87ceeb }, | |
| 159 | + { "slateblue", 0x6a5acd }, | |
| 160 | + { "slategray", 0x708090 }, | |
| 161 | + { "snow", 0xfffafa }, | |
| 162 | + { "springgreen", 0x00ff7f }, | |
| 163 | + { "steelblue", 0x4682b4 }, | |
| 164 | + { "tan", 0xd2b48c }, | |
| 165 | + { "teal", 0x008080 }, | |
| 166 | + { "thistle", 0xd8bfd8 }, | |
| 167 | + { "tomato", 0xff6347 }, | |
| 168 | + { "turquoise", 0x40e0d0 }, | |
| 169 | + { "violet", 0xee82ee }, | |
| 170 | + { "wheat", 0xf5deb3 }, | |
| 171 | + { "white", 0xffffff }, | |
| 172 | + { "whitesmoke", 0xf5f5f5 }, | |
| 173 | + { "yellow", 0xffff00 }, | |
| 174 | + { "yellowgreen", 0x9acd32 }, | |
| 175 | +}; | |
| 176 | + | |
| 177 | +/* | |
| 178 | +** Attempt to translate a CSS color name into an integer that | |
| 179 | +** represents the equivalent RGB value. Ignore alpha if provided. | |
| 180 | +** If the name cannot be translated, return -1. | |
| 181 | +*/ | |
| 182 | +int color_name_to_rgb(const char *zName){ | |
| 183 | + if( zName==0 || zName[0]==0 ) return -1; | |
| 184 | + if( zName[0]=='#' ){ | |
| 185 | + int i, v = 0; | |
| 186 | + for(i=1; i<=6 && fossil_isxdigit(zName[i]); i++){ | |
| 187 | + v = v*16 + fossil_hexvalue(zName[i]); | |
| 188 | + } | |
| 189 | + if( i==4 ){ | |
| 190 | + v = fossil_hexvalue(zName[1])*0x110000 + | |
| 191 | + fossil_hexvalue(zName[2])*0x1100 + | |
| 192 | + fossil_hexvalue(zName[3])*0x11; | |
| 193 | + return v; | |
| 194 | + } | |
| 195 | + if( i==7 ){ | |
| 196 | + return v; | |
| 197 | + } | |
| 198 | + return -1; | |
| 199 | + }else{ | |
| 200 | + int iMin = 0; | |
| 201 | + int iMax = count(aCssColors)-1; | |
| 202 | + while( iMin<=iMax ){ | |
| 203 | + int iMid = (iMin+iMax)/2; | |
| 204 | + int c = sqlite3_stricmp(aCssColors[iMid].zName, zName); | |
| 205 | + if( c==0 ) return aCssColors[iMid].iRGB; | |
| 206 | + if( c<0 ){ | |
| 207 | + iMin = iMid+1; | |
| 208 | + }else{ | |
| 209 | + iMax = iMid-1; | |
| 210 | + } | |
| 211 | + } | |
| 212 | + return -1; | |
| 213 | + } | |
| 214 | +} | |
| 215 | + | |
| 216 | +/* | |
| 217 | +** SETTING: raw-bgcolor boolean default=off | |
| 218 | +** | |
| 219 | +** Fossil usually tries to adjust user-specified background colors | |
| 220 | +** for checkins so that the text is readable and so that the color | |
| 221 | +** is not too garish. This setting disables that filter. When | |
| 222 | +** this setting is on, the user-selected background colors are shown | |
| 223 | +** exactly as requested. | |
| 224 | +*/ | |
| 225 | + | |
| 226 | +/* | |
| 227 | +** Shift a color provided by the user so that it is suitable | |
| 228 | +** for use as a background color in the current skin. | |
| 229 | +** | |
| 230 | +** The return value is a #HHHHHH color name contained in | |
| 231 | +** static space that is overwritten on the next call. | |
| 232 | +** | |
| 233 | +** If we cannot make sense of the background color recommendation | |
| 234 | +** that is the input, then return NULL. | |
| 235 | +** | |
| 236 | +** The iFgClr parameter is normally 0. But for testing purposes, set | |
| 237 | +** it to 1 for a black foregrounds and 2 for a white foreground. | |
| 238 | +*/ | |
| 239 | +const char *reasonable_bg_color(const char *zRequested, int iFgClr){ | |
| 240 | + int iRGB = color_name_to_rgb(zRequested); | |
| 241 | + int r, g, b; /* RGB components of requested color */ | |
| 242 | + static int systemFg = 0; /* 1==black-foreground 2==white-foreground */ | |
| 243 | + int fg; /* Foreground color to actually use */ | |
| 244 | + static char zColor[10]; /* Return value */ | |
| 245 | + | |
| 246 | + if( iFgClr ){ | |
| 247 | + fg = iFgClr; | |
| 248 | + }else if( systemFg==0 ){ | |
| 249 | + if( db_get_boolean("raw-bgcolor",0) ){ | |
| 250 | + fg = systemFg = 3; | |
| 251 | + }else{ | |
| 252 | + fg = systemFg = skin_detail_boolean("white-foreground") ? 2 : 1; | |
| 253 | + } | |
| 254 | + }else{ | |
| 255 | + fg = systemFg; | |
| 256 | + } | |
| 257 | + if( fg>=3 ) return zRequested; | |
| 258 | + | |
| 259 | + if( iRGB<0 ) return 0; | |
| 260 | + r = (iRGB>>16) & 0xff; | |
| 261 | + g = (iRGB>>8) & 0xff; | |
| 262 | + b = iRGB & 0xff; | |
| 263 | + if( fg==1 ){ | |
| 264 | + const int K = 70; | |
| 265 | + r = (K*r)/255 + (255-K); | |
| 266 | + g = (K*g)/255 + (255-K); | |
| 267 | + b = (K*b)/255 + (255-K); | |
| 268 | + }else{ | |
| 269 | + const int K = 90; | |
| 270 | + r = (K*r)/255; | |
| 271 | + g = (K*g)/255; | |
| 272 | + b = (K*b)/255; | |
| 273 | + } | |
| 274 | + sqlite3_snprintf(8, zColor, "#%02x%02x%02x", r,g,b); | |
| 275 | + return zColor; | |
| 276 | +} | |
| 25 | 277 | |
| 26 | 278 | /* |
| 27 | 279 | ** Compute a hash on a branch or user name |
| 28 | 280 | */ |
| 29 | 281 | static unsigned int hash_of_name(const char *z){ |
| @@ -185,5 +437,86 @@ | ||
| 185 | 437 | @ <input type="submit" value="Submit"> |
| 186 | 438 | @ <input type="submit" name="rand" value="Random"> |
| 187 | 439 | @ </form> |
| 188 | 440 | style_finish_page(); |
| 189 | 441 | } |
| 442 | + | |
| 443 | +/* | |
| 444 | +** WEBPAGE: test-bgcolor | |
| 445 | +** | |
| 446 | +** Show how user-specified background colors will be rendered | |
| 447 | +** using the reasonable_bg_color() algorithm. | |
| 448 | +*/ | |
| 449 | +void test_bgcolor_page(void){ | |
| 450 | + const char *zReq; /* Requested color name */ | |
| 451 | + const char *zBG; /* Actual color provided */ | |
| 452 | + const char *zBg1; | |
| 453 | + char zNm[10]; | |
| 454 | + static const char *azDflt[] = { | |
| 455 | + "red", "orange", "yellow", "green", "blue", "indigo", "violet", | |
| 456 | + "tan", "brown", "gray" | |
| 457 | + }; | |
| 458 | + int i, cnt, iClr, r, g, b; | |
| 459 | + char *zFg; | |
| 460 | + login_check_credentials(); | |
| 461 | + style_set_current_feature("test"); | |
| 462 | + style_header("Background Color Test"); | |
| 463 | + for(i=cnt=0; i<10; i++){ | |
| 464 | + sqlite3_snprintf(sizeof(zNm),zNm,"b%d",i); | |
| 465 | + zReq = PD(zNm,azDflt[i]); | |
| 466 | + if( zReq==0 || zReq[0]==0 ) continue; | |
| 467 | + if( cnt==0 ){ | |
| 468 | + @ <table border="1" cellspacing="0" cellpadding="10"> | |
| 469 | + @ <tr> | |
| 470 | + @ <th>Requested Background | |
| 471 | + @ <th>Light mode | |
| 472 | + @ <th>Dark mode | |
| 473 | + @ </tr> | |
| 474 | + } | |
| 475 | + cnt++; | |
| 476 | + zBG = reasonable_bg_color(zReq, 0); | |
| 477 | + if( zBG==0 ){ | |
| 478 | + @ <tr><td colspan="3" align="center">\ | |
| 479 | + @ "%h(zReq)" is not a recognized color name</td></tr> | |
| 480 | + continue; | |
| 481 | + } | |
| 482 | + iClr = color_name_to_rgb(zReq); | |
| 483 | + r = (iClr>>16) & 0xff; | |
| 484 | + g = (iClr>>8) & 0xff; | |
| 485 | + b = iClr & 0xff; | |
| 486 | + if( 3*r + 7*g + b > 6*255 ){ | |
| 487 | + zFg = "black"; | |
| 488 | + }else{ | |
| 489 | + zFg = "white"; | |
| 490 | + } | |
| 491 | + if( zReq[0]!='#' ){ | |
| 492 | + char zReqRGB[12]; | |
| 493 | + sqlite3_snprintf(sizeof(zReqRGB),zReqRGB,"#%06x",color_name_to_rgb(zReq)); | |
| 494 | + @ <tr><td style='color:%h(zFg);background-color:%h(zReq);'>\ | |
| 495 | + @ Requested color "%h(zReq)" (%h(zReqRGB))</td> | |
| 496 | + }else{ | |
| 497 | + @ <tr><td style='color:%h(zFg);background-color:%s(zReq);'>\ | |
| 498 | + @ Requested color "%h(zReq)"</td> | |
| 499 | + } | |
| 500 | + zBg1 = reasonable_bg_color(zReq,1); | |
| 501 | + @ <td style='color:black;background-color:%h(zBg1);'>\ | |
| 502 | + @ Background color for dark text: %h(zBg1)</td> | |
| 503 | + zBg1 = reasonable_bg_color(zReq,2); | |
| 504 | + @ <td style='color:white;background-color:%h(zBg1);'>\ | |
| 505 | + @ Background color for light text: %h(zBg1)</td></tr> | |
| 506 | + } | |
| 507 | + if( cnt ){ | |
| 508 | + @ </table> | |
| 509 | + @ <hr> | |
| 510 | + } | |
| 511 | + @ <form method="POST"> | |
| 512 | + @ <p>Enter CSS color names below and see them shifted into corresponding | |
| 513 | + @ background colors above.</p> | |
| 514 | + for(i=0; i<10; i++){ | |
| 515 | + sqlite3_snprintf(sizeof(zNm),zNm,"b%d",i); | |
| 516 | + @ <input type="text" size="30" name='%s(zNm)' \ | |
| 517 | + @ value='%h(PD(zNm,azDflt[i]))'><br> | |
| 518 | + } | |
| 519 | + @ <input type="submit" value="Submit"> | |
| 520 | + @ </form> | |
| 521 | + style_finish_page(); | |
| 522 | +} | |
| 190 | 523 |
| --- src/color.c | |
| +++ src/color.c | |
| @@ -20,10 +20,262 @@ | |
| 20 | ** |
| 21 | */ |
| 22 | #include "config.h" |
| 23 | #include <string.h> |
| 24 | #include "color.h" |
| 25 | |
| 26 | /* |
| 27 | ** Compute a hash on a branch or user name |
| 28 | */ |
| 29 | static unsigned int hash_of_name(const char *z){ |
| @@ -185,5 +437,86 @@ | |
| 185 | @ <input type="submit" value="Submit"> |
| 186 | @ <input type="submit" name="rand" value="Random"> |
| 187 | @ </form> |
| 188 | style_finish_page(); |
| 189 | } |
| 190 |
| --- src/color.c | |
| +++ src/color.c | |
| @@ -20,10 +20,262 @@ | |
| 20 | ** |
| 21 | */ |
| 22 | #include "config.h" |
| 23 | #include <string.h> |
| 24 | #include "color.h" |
| 25 | |
| 26 | /* |
| 27 | ** 140 standard CSS color names and their corresponding RGB values, |
| 28 | ** in alphabetical order by name so that we can do a binary search |
| 29 | ** for lookup. |
| 30 | */ |
| 31 | static const struct CssColors { |
| 32 | const char *zName; /* CSS Color name, lower case */ |
| 33 | unsigned int iRGB; /* Corresponding RGB value */ |
| 34 | } aCssColors[] = { |
| 35 | { "aliceblue", 0xf0f8ff }, |
| 36 | { "antiquewhite", 0xfaebd7 }, |
| 37 | { "aqua", 0x00ffff }, |
| 38 | { "aquamarine", 0x7fffd4 }, |
| 39 | { "azure", 0xf0ffff }, |
| 40 | { "beige", 0xf5f5dc }, |
| 41 | { "bisque", 0xffe4c4 }, |
| 42 | { "black", 0x000000 }, |
| 43 | { "blanchedalmond", 0xffebcd }, |
| 44 | { "blue", 0x0000ff }, |
| 45 | { "blueviolet", 0x8a2be2 }, |
| 46 | { "brown", 0xa52a2a }, |
| 47 | { "burlywood", 0xdeb887 }, |
| 48 | { "cadetblue", 0x5f9ea0 }, |
| 49 | { "chartreuse", 0x7fff00 }, |
| 50 | { "chocolate", 0xd2691e }, |
| 51 | { "coral", 0xff7f50 }, |
| 52 | { "cornflowerblue", 0x6495ed }, |
| 53 | { "cornsilk", 0xfff8dc }, |
| 54 | { "crimson", 0xdc143c }, |
| 55 | { "cyan", 0x00ffff }, |
| 56 | { "darkblue", 0x00008b }, |
| 57 | { "darkcyan", 0x008b8b }, |
| 58 | { "darkgoldenrod", 0xb8860b }, |
| 59 | { "darkgray", 0xa9a9a9 }, |
| 60 | { "darkgreen", 0x006400 }, |
| 61 | { "darkkhaki", 0xbdb76b }, |
| 62 | { "darkmagenta", 0x8b008b }, |
| 63 | { "darkolivegreen", 0x556b2f }, |
| 64 | { "darkorange", 0xff8c00 }, |
| 65 | { "darkorchid", 0x9932cc }, |
| 66 | { "darkred", 0x8b0000 }, |
| 67 | { "darksalmon", 0xe9967a }, |
| 68 | { "darkseagreen", 0x8fbc8f }, |
| 69 | { "darkslateblue", 0x483d8b }, |
| 70 | { "darkslategray", 0x2f4f4f }, |
| 71 | { "darkturquoise", 0x00ced1 }, |
| 72 | { "darkviolet", 0x9400d3 }, |
| 73 | { "deeppink", 0xff1493 }, |
| 74 | { "deepskyblue", 0x00bfff }, |
| 75 | { "dimgray", 0x696969 }, |
| 76 | { "dodgerblue", 0x1e90ff }, |
| 77 | { "firebrick", 0xb22222 }, |
| 78 | { "floralwhite", 0xfffaf0 }, |
| 79 | { "forestgreen", 0x228b22 }, |
| 80 | { "fuchsia", 0xff00ff }, |
| 81 | { "gainsboro", 0xdcdcdc }, |
| 82 | { "ghostwhite", 0xf8f8ff }, |
| 83 | { "gold", 0xffd700 }, |
| 84 | { "goldenrod", 0xdaa520 }, |
| 85 | { "gray", 0x808080 }, |
| 86 | { "green", 0x008000 }, |
| 87 | { "greenyellow", 0xadff2f }, |
| 88 | { "honeydew", 0xf0fff0 }, |
| 89 | { "hotpink", 0xff69b4 }, |
| 90 | { "indianred", 0xcd5c5c }, |
| 91 | { "indigo", 0x4b0082 }, |
| 92 | { "ivory", 0xfffff0 }, |
| 93 | { "khaki", 0xf0e68c }, |
| 94 | { "lavender", 0xe6e6fa }, |
| 95 | { "lavenderblush", 0xfff0f5 }, |
| 96 | { "lawngreen", 0x7cfc00 }, |
| 97 | { "lemonchiffon", 0xfffacd }, |
| 98 | { "lightblue", 0xadd8e6 }, |
| 99 | { "lightcoral", 0xf08080 }, |
| 100 | { "lightcyan", 0xe0ffff }, |
| 101 | { "lightgoldenrodyellow", 0xfafad2 }, |
| 102 | { "lightgrey", 0xd3d3d3 }, |
| 103 | { "lightgreen", 0x90ee90 }, |
| 104 | { "lightpink", 0xffb6c1 }, |
| 105 | { "lightsalmon", 0xffa07a }, |
| 106 | { "lightseagreen", 0x20b2aa }, |
| 107 | { "lightskyblue", 0x87cefa }, |
| 108 | { "lightslategray", 0x778899 }, |
| 109 | { "lightsteelblue", 0xb0c4de }, |
| 110 | { "lightyellow", 0xffffe0 }, |
| 111 | { "lime", 0x00ff00 }, |
| 112 | { "limegreen", 0x32cd32 }, |
| 113 | { "linen", 0xfaf0e6 }, |
| 114 | { "magenta", 0xff00ff }, |
| 115 | { "maroon", 0x800000 }, |
| 116 | { "mediumaquamarine", 0x66cdaa }, |
| 117 | { "mediumblue", 0x0000cd }, |
| 118 | { "mediumorchid", 0xba55d3 }, |
| 119 | { "mediumpurple", 0x9370d8 }, |
| 120 | { "mediumseagreen", 0x3cb371 }, |
| 121 | { "mediumslateblue", 0x7b68ee }, |
| 122 | { "mediumspringgreen", 0x00fa9a }, |
| 123 | { "mediumturquoise", 0x48d1cc }, |
| 124 | { "mediumvioletred", 0xc71585 }, |
| 125 | { "midnightblue", 0x191970 }, |
| 126 | { "mintcream", 0xf5fffa }, |
| 127 | { "mistyrose", 0xffe4e1 }, |
| 128 | { "moccasin", 0xffe4b5 }, |
| 129 | { "navajowhite", 0xffdead }, |
| 130 | { "navy", 0x000080 }, |
| 131 | { "oldlace", 0xfdf5e6 }, |
| 132 | { "olive", 0x808000 }, |
| 133 | { "olivedrab", 0x6b8e23 }, |
| 134 | { "orange", 0xffa500 }, |
| 135 | { "orangered", 0xff4500 }, |
| 136 | { "orchid", 0xda70d6 }, |
| 137 | { "palegoldenrod", 0xeee8aa }, |
| 138 | { "palegreen", 0x98fb98 }, |
| 139 | { "paleturquoise", 0xafeeee }, |
| 140 | { "palevioletred", 0xd87093 }, |
| 141 | { "papayawhip", 0xffefd5 }, |
| 142 | { "peachpuff", 0xffdab9 }, |
| 143 | { "peru", 0xcd853f }, |
| 144 | { "pink", 0xffc0cb }, |
| 145 | { "plum", 0xdda0dd }, |
| 146 | { "powderblue", 0xb0e0e6 }, |
| 147 | { "purple", 0x800080 }, |
| 148 | { "red", 0xff0000 }, |
| 149 | { "rosybrown", 0xbc8f8f }, |
| 150 | { "royalblue", 0x4169e1 }, |
| 151 | { "saddlebrown", 0x8b4513 }, |
| 152 | { "salmon", 0xfa8072 }, |
| 153 | { "sandybrown", 0xf4a460 }, |
| 154 | { "seagreen", 0x2e8b57 }, |
| 155 | { "seashell", 0xfff5ee }, |
| 156 | { "sienna", 0xa0522d }, |
| 157 | { "silver", 0xc0c0c0 }, |
| 158 | { "skyblue", 0x87ceeb }, |
| 159 | { "slateblue", 0x6a5acd }, |
| 160 | { "slategray", 0x708090 }, |
| 161 | { "snow", 0xfffafa }, |
| 162 | { "springgreen", 0x00ff7f }, |
| 163 | { "steelblue", 0x4682b4 }, |
| 164 | { "tan", 0xd2b48c }, |
| 165 | { "teal", 0x008080 }, |
| 166 | { "thistle", 0xd8bfd8 }, |
| 167 | { "tomato", 0xff6347 }, |
| 168 | { "turquoise", 0x40e0d0 }, |
| 169 | { "violet", 0xee82ee }, |
| 170 | { "wheat", 0xf5deb3 }, |
| 171 | { "white", 0xffffff }, |
| 172 | { "whitesmoke", 0xf5f5f5 }, |
| 173 | { "yellow", 0xffff00 }, |
| 174 | { "yellowgreen", 0x9acd32 }, |
| 175 | }; |
| 176 | |
| 177 | /* |
| 178 | ** Attempt to translate a CSS color name into an integer that |
| 179 | ** represents the equivalent RGB value. Ignore alpha if provided. |
| 180 | ** If the name cannot be translated, return -1. |
| 181 | */ |
| 182 | int color_name_to_rgb(const char *zName){ |
| 183 | if( zName==0 || zName[0]==0 ) return -1; |
| 184 | if( zName[0]=='#' ){ |
| 185 | int i, v = 0; |
| 186 | for(i=1; i<=6 && fossil_isxdigit(zName[i]); i++){ |
| 187 | v = v*16 + fossil_hexvalue(zName[i]); |
| 188 | } |
| 189 | if( i==4 ){ |
| 190 | v = fossil_hexvalue(zName[1])*0x110000 + |
| 191 | fossil_hexvalue(zName[2])*0x1100 + |
| 192 | fossil_hexvalue(zName[3])*0x11; |
| 193 | return v; |
| 194 | } |
| 195 | if( i==7 ){ |
| 196 | return v; |
| 197 | } |
| 198 | return -1; |
| 199 | }else{ |
| 200 | int iMin = 0; |
| 201 | int iMax = count(aCssColors)-1; |
| 202 | while( iMin<=iMax ){ |
| 203 | int iMid = (iMin+iMax)/2; |
| 204 | int c = sqlite3_stricmp(aCssColors[iMid].zName, zName); |
| 205 | if( c==0 ) return aCssColors[iMid].iRGB; |
| 206 | if( c<0 ){ |
| 207 | iMin = iMid+1; |
| 208 | }else{ |
| 209 | iMax = iMid-1; |
| 210 | } |
| 211 | } |
| 212 | return -1; |
| 213 | } |
| 214 | } |
| 215 | |
| 216 | /* |
| 217 | ** SETTING: raw-bgcolor boolean default=off |
| 218 | ** |
| 219 | ** Fossil usually tries to adjust user-specified background colors |
| 220 | ** for checkins so that the text is readable and so that the color |
| 221 | ** is not too garish. This setting disables that filter. When |
| 222 | ** this setting is on, the user-selected background colors are shown |
| 223 | ** exactly as requested. |
| 224 | */ |
| 225 | |
| 226 | /* |
| 227 | ** Shift a color provided by the user so that it is suitable |
| 228 | ** for use as a background color in the current skin. |
| 229 | ** |
| 230 | ** The return value is a #HHHHHH color name contained in |
| 231 | ** static space that is overwritten on the next call. |
| 232 | ** |
| 233 | ** If we cannot make sense of the background color recommendation |
| 234 | ** that is the input, then return NULL. |
| 235 | ** |
| 236 | ** The iFgClr parameter is normally 0. But for testing purposes, set |
| 237 | ** it to 1 for a black foregrounds and 2 for a white foreground. |
| 238 | */ |
| 239 | const char *reasonable_bg_color(const char *zRequested, int iFgClr){ |
| 240 | int iRGB = color_name_to_rgb(zRequested); |
| 241 | int r, g, b; /* RGB components of requested color */ |
| 242 | static int systemFg = 0; /* 1==black-foreground 2==white-foreground */ |
| 243 | int fg; /* Foreground color to actually use */ |
| 244 | static char zColor[10]; /* Return value */ |
| 245 | |
| 246 | if( iFgClr ){ |
| 247 | fg = iFgClr; |
| 248 | }else if( systemFg==0 ){ |
| 249 | if( db_get_boolean("raw-bgcolor",0) ){ |
| 250 | fg = systemFg = 3; |
| 251 | }else{ |
| 252 | fg = systemFg = skin_detail_boolean("white-foreground") ? 2 : 1; |
| 253 | } |
| 254 | }else{ |
| 255 | fg = systemFg; |
| 256 | } |
| 257 | if( fg>=3 ) return zRequested; |
| 258 | |
| 259 | if( iRGB<0 ) return 0; |
| 260 | r = (iRGB>>16) & 0xff; |
| 261 | g = (iRGB>>8) & 0xff; |
| 262 | b = iRGB & 0xff; |
| 263 | if( fg==1 ){ |
| 264 | const int K = 70; |
| 265 | r = (K*r)/255 + (255-K); |
| 266 | g = (K*g)/255 + (255-K); |
| 267 | b = (K*b)/255 + (255-K); |
| 268 | }else{ |
| 269 | const int K = 90; |
| 270 | r = (K*r)/255; |
| 271 | g = (K*g)/255; |
| 272 | b = (K*b)/255; |
| 273 | } |
| 274 | sqlite3_snprintf(8, zColor, "#%02x%02x%02x", r,g,b); |
| 275 | return zColor; |
| 276 | } |
| 277 | |
| 278 | /* |
| 279 | ** Compute a hash on a branch or user name |
| 280 | */ |
| 281 | static unsigned int hash_of_name(const char *z){ |
| @@ -185,5 +437,86 @@ | |
| 437 | @ <input type="submit" value="Submit"> |
| 438 | @ <input type="submit" name="rand" value="Random"> |
| 439 | @ </form> |
| 440 | style_finish_page(); |
| 441 | } |
| 442 | |
| 443 | /* |
| 444 | ** WEBPAGE: test-bgcolor |
| 445 | ** |
| 446 | ** Show how user-specified background colors will be rendered |
| 447 | ** using the reasonable_bg_color() algorithm. |
| 448 | */ |
| 449 | void test_bgcolor_page(void){ |
| 450 | const char *zReq; /* Requested color name */ |
| 451 | const char *zBG; /* Actual color provided */ |
| 452 | const char *zBg1; |
| 453 | char zNm[10]; |
| 454 | static const char *azDflt[] = { |
| 455 | "red", "orange", "yellow", "green", "blue", "indigo", "violet", |
| 456 | "tan", "brown", "gray" |
| 457 | }; |
| 458 | int i, cnt, iClr, r, g, b; |
| 459 | char *zFg; |
| 460 | login_check_credentials(); |
| 461 | style_set_current_feature("test"); |
| 462 | style_header("Background Color Test"); |
| 463 | for(i=cnt=0; i<10; i++){ |
| 464 | sqlite3_snprintf(sizeof(zNm),zNm,"b%d",i); |
| 465 | zReq = PD(zNm,azDflt[i]); |
| 466 | if( zReq==0 || zReq[0]==0 ) continue; |
| 467 | if( cnt==0 ){ |
| 468 | @ <table border="1" cellspacing="0" cellpadding="10"> |
| 469 | @ <tr> |
| 470 | @ <th>Requested Background |
| 471 | @ <th>Light mode |
| 472 | @ <th>Dark mode |
| 473 | @ </tr> |
| 474 | } |
| 475 | cnt++; |
| 476 | zBG = reasonable_bg_color(zReq, 0); |
| 477 | if( zBG==0 ){ |
| 478 | @ <tr><td colspan="3" align="center">\ |
| 479 | @ "%h(zReq)" is not a recognized color name</td></tr> |
| 480 | continue; |
| 481 | } |
| 482 | iClr = color_name_to_rgb(zReq); |
| 483 | r = (iClr>>16) & 0xff; |
| 484 | g = (iClr>>8) & 0xff; |
| 485 | b = iClr & 0xff; |
| 486 | if( 3*r + 7*g + b > 6*255 ){ |
| 487 | zFg = "black"; |
| 488 | }else{ |
| 489 | zFg = "white"; |
| 490 | } |
| 491 | if( zReq[0]!='#' ){ |
| 492 | char zReqRGB[12]; |
| 493 | sqlite3_snprintf(sizeof(zReqRGB),zReqRGB,"#%06x",color_name_to_rgb(zReq)); |
| 494 | @ <tr><td style='color:%h(zFg);background-color:%h(zReq);'>\ |
| 495 | @ Requested color "%h(zReq)" (%h(zReqRGB))</td> |
| 496 | }else{ |
| 497 | @ <tr><td style='color:%h(zFg);background-color:%s(zReq);'>\ |
| 498 | @ Requested color "%h(zReq)"</td> |
| 499 | } |
| 500 | zBg1 = reasonable_bg_color(zReq,1); |
| 501 | @ <td style='color:black;background-color:%h(zBg1);'>\ |
| 502 | @ Background color for dark text: %h(zBg1)</td> |
| 503 | zBg1 = reasonable_bg_color(zReq,2); |
| 504 | @ <td style='color:white;background-color:%h(zBg1);'>\ |
| 505 | @ Background color for light text: %h(zBg1)</td></tr> |
| 506 | } |
| 507 | if( cnt ){ |
| 508 | @ </table> |
| 509 | @ <hr> |
| 510 | } |
| 511 | @ <form method="POST"> |
| 512 | @ <p>Enter CSS color names below and see them shifted into corresponding |
| 513 | @ background colors above.</p> |
| 514 | for(i=0; i<10; i++){ |
| 515 | sqlite3_snprintf(sizeof(zNm),zNm,"b%d",i); |
| 516 | @ <input type="text" size="30" name='%s(zNm)' \ |
| 517 | @ value='%h(PD(zNm,azDflt[i]))'><br> |
| 518 | } |
| 519 | @ <input type="submit" value="Submit"> |
| 520 | @ </form> |
| 521 | style_finish_page(); |
| 522 | } |
| 523 |
+165
-103
| --- src/comformat.c | ||
| +++ src/comformat.c | ||
| @@ -21,18 +21,22 @@ | ||
| 21 | 21 | #include "config.h" |
| 22 | 22 | #include "comformat.h" |
| 23 | 23 | #include <assert.h> |
| 24 | 24 | |
| 25 | 25 | #if INTERFACE |
| 26 | -#define COMMENT_PRINT_NONE ((u32)0x00000000) /* No flags = non-legacy. */ | |
| 27 | -#define COMMENT_PRINT_LEGACY ((u32)0x00000001) /* Use legacy algorithm. */ | |
| 26 | +#define COMMENT_PRINT_NONE ((u32)0x00000000) /* No flags */ | |
| 27 | +#define COMMENT_PRINT_CANONICAL ((u32)0x00000001) /* Use canonical algorithm */ | |
| 28 | +#define COMMENT_PRINT_DEFAULT COMMENT_PRINT_CANONICAL /* Default */ | |
| 29 | +#define COMMENT_PRINT_UNSET (-1) /* Not initialized */ | |
| 30 | + | |
| 31 | +/* The canonical comment printing algorithm is recommended. We make | |
| 32 | +** no promise of on-going support for any of the following flags: | |
| 33 | +*/ | |
| 28 | 34 | #define COMMENT_PRINT_TRIM_CRLF ((u32)0x00000002) /* Trim leading CR/LF. */ |
| 29 | 35 | #define COMMENT_PRINT_TRIM_SPACE ((u32)0x00000004) /* Trim leading/trailing. */ |
| 30 | 36 | #define COMMENT_PRINT_WORD_BREAK ((u32)0x00000008) /* Break lines on words. */ |
| 31 | 37 | #define COMMENT_PRINT_ORIG_BREAK ((u32)0x00000010) /* Break before original. */ |
| 32 | -#define COMMENT_PRINT_DEFAULT (COMMENT_PRINT_LEGACY) /* Defaults. */ | |
| 33 | -#define COMMENT_PRINT_UNSET (-1) /* Not initialized. */ | |
| 34 | 38 | #endif |
| 35 | 39 | |
| 36 | 40 | /********* Code copied from SQLite src/shell.c.in on 2024-09-30 **********/ |
| 37 | 41 | /* Lookup table to estimate the number of columns consumed by a Unicode |
| 38 | 42 | ** character. |
| @@ -220,10 +224,11 @@ | ||
| 220 | 224 | int maxChars, /* [in] Optimization hint to abort before space found. */ |
| 221 | 225 | int *sumWidth /* [out] Summated width of all characters to next space. */ |
| 222 | 226 | ){ |
| 223 | 227 | int cchUTF8, utf32, wcwidth = 0; |
| 224 | 228 | int nextIndex = index; |
| 229 | + if( zLine[index]==0 ) return index; | |
| 225 | 230 | for(;;){ |
| 226 | 231 | char_info_utf8(&zLine[nextIndex],&cchUTF8,&utf32); |
| 227 | 232 | nextIndex += cchUTF8; |
| 228 | 233 | wcwidth += cli_wcwidth(utf32); |
| 229 | 234 | if( zLine[nextIndex]==0 || fossil_isspace(zLine[nextIndex]) || |
| @@ -234,29 +239,46 @@ | ||
| 234 | 239 | } |
| 235 | 240 | return 0; /* NOT REACHED */ |
| 236 | 241 | } |
| 237 | 242 | |
| 238 | 243 | /* |
| 239 | -** Return information about the next (single- or multi-byte) character in the | |
| 240 | -** specified UTF-8 string: The number of UTF-8 code units (in this case: bytes) | |
| 241 | -** and the decoded UTF-32 code point. Incomplete, ill-formed and overlong | |
| 242 | -** sequences are consumed together as one invalid code point. The invalid lead | |
| 243 | -** bytes 0xC0 to 0xC1 and 0xF5 to 0xF7 are allowed to initiate (ill-formed) 2- | |
| 244 | -** and 4-byte sequences, respectively, the other invalid lead bytes 0xF8 to 0xFF | |
| 245 | -** are treated as invalid 1-byte sequences (as lone trail bytes), all resulting | |
| 246 | -** in one invalid code point. Invalid UTF-8 sequences encoding a non-scalar code | |
| 247 | -** point (UTF-16 surrogates U+D800 to U+DFFF) are allowed. | |
| 244 | +** Return information about the next (single- or multi-byte) character in | |
| 245 | +** z[0]. Two values are computed: | |
| 246 | +** | |
| 247 | +** * The number of bytes needed to represent the character. | |
| 248 | +** * The UTF code point value. | |
| 249 | +** | |
| 250 | +** Incomplete, ill-formed and overlong sequences are consumed together as | |
| 251 | +** one invalid code point. The invalid lead bytes 0xC0 to 0xC1 and 0xF5 to | |
| 252 | +** 0xF7 are allowed to initiate (ill-formed) 2- and 4-byte sequences, | |
| 253 | +** respectively, the other invalid lead bytes 0xF8 to 0xFF are treated | |
| 254 | +** as invalid 1-byte sequences (as lone trail bytes), all resulting | |
| 255 | +** in one invalid code point. Invalid UTF-8 sequences encoding a | |
| 256 | +** non-scalar code point (UTF-16 surrogates U+D800 to U+DFFF) are allowed. | |
| 257 | +** | |
| 258 | +** ANSI escape sequences of the form "\033[...X" are interpreted as a | |
| 259 | +** zero-width character. | |
| 248 | 260 | */ |
| 249 | 261 | void char_info_utf8( |
| 250 | - const char *z, | |
| 251 | - int *pCchUTF8, | |
| 252 | - int *pUtf32 | |
| 262 | + const char *z, /* The character to be analyzed */ | |
| 263 | + int *pCchUTF8, /* OUT: The number of bytes used by this character */ | |
| 264 | + int *pUtf32 /* OUT: The UTF8 code point (used to determine width) */ | |
| 253 | 265 | ){ |
| 254 | 266 | int i = 0; /* Counted bytes. */ |
| 255 | 267 | int cchUTF8 = 1; /* Code units consumed. */ |
| 256 | 268 | int maxUTF8 = 1; /* Expected sequence length. */ |
| 257 | 269 | char c = z[i++]; |
| 270 | + if( c==0x1b && z[i]=='[' ){ | |
| 271 | + i++; | |
| 272 | + while( z[i]>=0x30 && z[i]<=0x3f ){ i++; } | |
| 273 | + while( z[i]>=0x20 && z[i]<=0x2f ){ i++; } | |
| 274 | + if( z[i]>=0x40 && z[i]<=0x7e ){ | |
| 275 | + *pCchUTF8 = i+1; | |
| 276 | + *pUtf32 = 0x301; /* A zero-width character */ | |
| 277 | + return; | |
| 278 | + } | |
| 279 | + } | |
| 258 | 280 | if( (c&0x80)==0x00 ){ /* 7-bit ASCII character. */ |
| 259 | 281 | *pCchUTF8 = 1; |
| 260 | 282 | *pUtf32 = (int)z[0]; |
| 261 | 283 | return; |
| 262 | 284 | } |
| @@ -465,21 +487,29 @@ | ||
| 465 | 487 | *pzLine = zLine + index; |
| 466 | 488 | } |
| 467 | 489 | } |
| 468 | 490 | |
| 469 | 491 | /* |
| 470 | -** This is the legacy comment printing algorithm. It is being retained | |
| 471 | -** for backward compatibility. | |
| 492 | +** This is the canonical comment printing algorithm. This is the algorithm | |
| 493 | +** that is recommended and that is used unless the administrator has made | |
| 494 | +** special arrangements to use a customized algorithm. | |
| 472 | 495 | ** |
| 473 | 496 | ** Given a comment string, format that string for printing on a TTY. |
| 474 | -** Assume that the output cursors is indent spaces from the left margin | |
| 497 | +** Assume that the output cursor is indent spaces from the left margin | |
| 475 | 498 | ** and that a single line can contain no more than 'width' characters. |
| 476 | 499 | ** Indent all subsequent lines by 'indent'. |
| 477 | 500 | ** |
| 501 | +** Formatting features: | |
| 502 | +** | |
| 503 | +** * Leading whitespace is removed. | |
| 504 | +** * Internal whitespace sequences are changed into a single space (0x20) | |
| 505 | +** character. | |
| 506 | +** * Lines are broken at a space, or at a hyphen ("-") whenever possible. | |
| 507 | +** | |
| 478 | 508 | ** Returns the number of new lines emitted. |
| 479 | 509 | */ |
| 480 | -static int comment_print_legacy( | |
| 510 | +static int comment_print_canonical( | |
| 481 | 511 | const char *zText, /* The comment text to be printed. */ |
| 482 | 512 | int indent, /* Number of spaces to indent each non-initial line. */ |
| 483 | 513 | int width /* Maximum number of characters per line. */ |
| 484 | 514 | ){ |
| 485 | 515 | int maxChars = width - indent; |
| @@ -564,13 +594,18 @@ | ||
| 564 | 594 | ** This is the comment printing function. The comment printing algorithm |
| 565 | 595 | ** contained within it attempts to preserve the formatting present within |
| 566 | 596 | ** the comment string itself while honoring line width limitations. There |
| 567 | 597 | ** are several flags that modify the default behavior of this function: |
| 568 | 598 | ** |
| 569 | -** COMMENT_PRINT_LEGACY: Forces use of the legacy comment printing | |
| 570 | -** algorithm. For backward compatibility, | |
| 571 | -** this is the default. | |
| 599 | +** COMMENT_PRINT_CANONICAL: Use the canonical printing algorithm: | |
| 600 | +** * Omit leading and trailing whitespace | |
| 601 | +** * Collapse internal whitespace into a | |
| 602 | +** single space (0x20) character. | |
| 603 | +** * Attempt to break lines at whitespace | |
| 604 | +** or hyphens. | |
| 605 | +** This is the recommended algorithm and is | |
| 606 | +** used in most cases. | |
| 572 | 607 | ** |
| 573 | 608 | ** COMMENT_PRINT_TRIM_CRLF: Trims leading and trailing carriage-returns |
| 574 | 609 | ** and line-feeds where they do not materially |
| 575 | 610 | ** impact pre-existing formatting (i.e. at the |
| 576 | 611 | ** start of the comment string -AND- right |
| @@ -613,56 +648,61 @@ | ||
| 613 | 648 | int indent, /* Spaces to indent each non-initial line. */ |
| 614 | 649 | int width, /* Maximum number of characters per line. */ |
| 615 | 650 | int flags /* Zero or more "COMMENT_PRINT_*" flags. */ |
| 616 | 651 | ){ |
| 617 | 652 | int maxChars = width - indent; |
| 618 | - int legacy = flags & COMMENT_PRINT_LEGACY; | |
| 619 | - int trimCrLf = flags & COMMENT_PRINT_TRIM_CRLF; | |
| 620 | - int trimSpace = flags & COMMENT_PRINT_TRIM_SPACE; | |
| 621 | - int wordBreak = flags & COMMENT_PRINT_WORD_BREAK; | |
| 622 | - int origBreak = flags & COMMENT_PRINT_ORIG_BREAK; | |
| 623 | - int lineCnt = 0; | |
| 624 | - const char *zLine; | |
| 625 | - | |
| 626 | - if( legacy ){ | |
| 627 | - return comment_print_legacy(zText, indent, width); | |
| 628 | - } | |
| 629 | - if( width<0 ){ | |
| 630 | - comment_set_maxchars(indent, &maxChars); | |
| 631 | - } | |
| 632 | - if( zText==0 ) zText = "(NULL)"; | |
| 633 | - if( maxChars<=0 ){ | |
| 634 | - maxChars = strlen(zText); | |
| 635 | - } | |
| 636 | - if( trimSpace ){ | |
| 637 | - while( fossil_isspace(zText[0]) ){ zText++; } | |
| 638 | - } | |
| 639 | - if( zText[0]==0 ){ | |
| 640 | - fossil_print("\n"); | |
| 641 | - lineCnt++; | |
| 642 | - return lineCnt; | |
| 643 | - } | |
| 644 | - zLine = zText; | |
| 645 | - for(;;){ | |
| 646 | - comment_print_line(zOrigText, zLine, indent, zLine>zText ? indent : 0, | |
| 647 | - maxChars, trimCrLf, trimSpace, wordBreak, origBreak, | |
| 648 | - &lineCnt, &zLine); | |
| 649 | - if( zLine==0 ) break; | |
| 650 | - while( fossil_isspace(zLine[0]) ) zLine++; | |
| 651 | - if( zLine[0]==0 ) break; | |
| 652 | - } | |
| 653 | - return lineCnt; | |
| 653 | + | |
| 654 | + if( flags & COMMENT_PRINT_CANONICAL ){ | |
| 655 | + /* Use the canonical algorithm. This is what happens in almost | |
| 656 | + ** all cases. */ | |
| 657 | + return comment_print_canonical(zText, indent, width); | |
| 658 | + }else{ | |
| 659 | + /* The remaining is a more complex formatting algorithm that is very | |
| 660 | + ** seldom used and is considered deprecated. | |
| 661 | + */ | |
| 662 | + int trimCrLf = flags & COMMENT_PRINT_TRIM_CRLF; | |
| 663 | + int trimSpace = flags & COMMENT_PRINT_TRIM_SPACE; | |
| 664 | + int wordBreak = flags & COMMENT_PRINT_WORD_BREAK; | |
| 665 | + int origBreak = flags & COMMENT_PRINT_ORIG_BREAK; | |
| 666 | + int lineCnt = 0; | |
| 667 | + const char *zLine; | |
| 668 | + | |
| 669 | + if( width<0 ){ | |
| 670 | + comment_set_maxchars(indent, &maxChars); | |
| 671 | + } | |
| 672 | + if( zText==0 ) zText = "(NULL)"; | |
| 673 | + if( maxChars<=0 ){ | |
| 674 | + maxChars = strlen(zText); | |
| 675 | + } | |
| 676 | + if( trimSpace ){ | |
| 677 | + while( fossil_isspace(zText[0]) ){ zText++; } | |
| 678 | + } | |
| 679 | + if( zText[0]==0 ){ | |
| 680 | + fossil_print("\n"); | |
| 681 | + lineCnt++; | |
| 682 | + return lineCnt; | |
| 683 | + } | |
| 684 | + zLine = zText; | |
| 685 | + for(;;){ | |
| 686 | + comment_print_line(zOrigText, zLine, indent, zLine>zText ? indent : 0, | |
| 687 | + maxChars, trimCrLf, trimSpace, wordBreak, origBreak, | |
| 688 | + &lineCnt, &zLine); | |
| 689 | + if( zLine==0 ) break; | |
| 690 | + while( fossil_isspace(zLine[0]) ) zLine++; | |
| 691 | + if( zLine[0]==0 ) break; | |
| 692 | + } | |
| 693 | + return lineCnt; | |
| 694 | + } | |
| 654 | 695 | } |
| 655 | 696 | |
| 656 | 697 | /* |
| 657 | 698 | ** Return the "COMMENT_PRINT_*" flags specified by the following sources, |
| 658 | 699 | ** evaluated in the following cascading order: |
| 659 | 700 | ** |
| 660 | -** 1. The global --comfmtflags (alias --comment-format) command-line option. | |
| 661 | -** 2. The local (per-repository) "comment-format" setting. | |
| 662 | -** 3. The global (all-repositories) "comment-format" setting. | |
| 663 | -** 4. The default value COMMENT_PRINT_DEFAULT. | |
| 701 | +** 1. The local (per-repository) "comment-format" setting. | |
| 702 | +** 2. The global (all-repositories) "comment-format" setting. | |
| 703 | +** 3. The default value COMMENT_PRINT_DEFAULT. | |
| 664 | 704 | */ |
| 665 | 705 | int get_comment_format(){ |
| 666 | 706 | int comFmtFlags; |
| 667 | 707 | |
| 668 | 708 | /* We must cache this result, else running the timeline can end up |
| @@ -689,54 +729,70 @@ | ||
| 689 | 729 | |
| 690 | 730 | /* |
| 691 | 731 | ** |
| 692 | 732 | ** COMMAND: test-comment-format |
| 693 | 733 | ** |
| 694 | -** Usage: %fossil test-comment-format ?OPTIONS? PREFIX TEXT ?ORIGTEXT? | |
| 734 | +** Usage: %fossil test-comment-format [OPTIONS] TEXT [PREFIX] [ORIGTEXT] | |
| 695 | 735 | ** |
| 696 | 736 | ** Test comment formatting and printing. Use for testing only. |
| 697 | 737 | ** |
| 738 | +** The default (canonical) formatting algorithm is: | |
| 739 | +** | |
| 740 | +** * Omit leading/trailing whitespace | |
| 741 | +** * Collapse internal whitespace into a single space character. | |
| 742 | +** * Attempt to break lines at whitespace or at a hyphen. | |
| 743 | +** | |
| 744 | +** Use --whitespace, --origbreak, --trimcrlf, --trimspace, | |
| 745 | +** and/or --wordbreak to disable the canonical processing and do | |
| 746 | +** the special processing specified by those other options. | |
| 747 | +** | |
| 698 | 748 | ** Options: |
| 699 | -** --file The comment text is really just a file name to | |
| 700 | -** read it from | |
| 701 | -** --decode Decode the text using the same method used when | |
| 702 | -** handling the value of a C-card from a manifest. | |
| 703 | -** --legacy Use the legacy comment printing algorithm | |
| 704 | -** --trimcrlf Enable trimming of leading/trailing CR/LF | |
| 705 | -** --trimspace Enable trimming of leading/trailing spaces | |
| 706 | -** --wordbreak Attempt to break lines on word boundaries | |
| 707 | -** --origbreak Attempt to break when the original comment text | |
| 708 | -** is detected | |
| 709 | -** --indent Number of spaces to indent (default (-1) is to | |
| 710 | -** auto-detect). Zero means no indent. | |
| 711 | -** -W|--width NUM Width of lines (default (-1) is to auto-detect). | |
| 712 | -** Zero means no limit. | |
| 749 | +** --decode Decode the text using the same method used when | |
| 750 | +** handling the value of a C-card from a manifest. | |
| 751 | +** --file FILE Omit the TEXT argument and read the comment text | |
| 752 | +** from FILE. | |
| 753 | +** --indent Number of spaces to indent (default (-1) is to | |
| 754 | +** auto-detect). Zero means no indent. | |
| 755 | +** --orig FILE Take the value for the ORIGTEXT argumetn from FILE. | |
| 756 | +** --origbreak Attempt to break when the original comment text | |
| 757 | +** is detected. | |
| 758 | +** --trimcrlf Enable trimming of leading/trailing CR/LF. | |
| 759 | +** --trimspace Enable trimming of leading/trailing spaces. | |
| 760 | +** --whitespace Keep all internal whitespace. | |
| 761 | +** --wordbreak Attempt to break lines on word boundaries. | |
| 762 | +** -W|--width NUM Width of lines (default (-1) is to auto-detect). | |
| 763 | +** Zero means no limit. | |
| 713 | 764 | */ |
| 714 | 765 | void test_comment_format(void){ |
| 715 | 766 | const char *zWidth; |
| 716 | 767 | const char *zIndent; |
| 717 | - const char *zPrefix; | |
| 718 | - char *zText; | |
| 719 | - char *zOrigText; | |
| 768 | + const char *zPrefix = 0; | |
| 769 | + char *zText = 0; | |
| 770 | + char *zOrigText = 0; | |
| 720 | 771 | int indent, width; |
| 721 | - int fromFile = find_option("file", 0, 0)!=0; | |
| 772 | + int i; | |
| 773 | + const char *fromFile = find_option("file", 0, 1); | |
| 722 | 774 | int decode = find_option("decode", 0, 0)!=0; |
| 723 | - int flags = COMMENT_PRINT_NONE; | |
| 724 | - if( find_option("legacy", 0, 0) ){ | |
| 725 | - flags |= COMMENT_PRINT_LEGACY; | |
| 775 | + int flags = COMMENT_PRINT_CANONICAL; | |
| 776 | + const char *fromOrig = find_option("orig", 0, 1); | |
| 777 | + if( find_option("whitespace",0,0) ){ | |
| 778 | + flags = 0; | |
| 726 | 779 | } |
| 727 | 780 | if( find_option("trimcrlf", 0, 0) ){ |
| 728 | - flags |= COMMENT_PRINT_TRIM_CRLF; | |
| 781 | + flags = COMMENT_PRINT_TRIM_CRLF; | |
| 729 | 782 | } |
| 730 | 783 | if( find_option("trimspace", 0, 0) ){ |
| 731 | 784 | flags |= COMMENT_PRINT_TRIM_SPACE; |
| 785 | + flags &= COMMENT_PRINT_CANONICAL; | |
| 732 | 786 | } |
| 733 | 787 | if( find_option("wordbreak", 0, 0) ){ |
| 734 | 788 | flags |= COMMENT_PRINT_WORD_BREAK; |
| 789 | + flags &= COMMENT_PRINT_CANONICAL; | |
| 735 | 790 | } |
| 736 | 791 | if( find_option("origbreak", 0, 0) ){ |
| 737 | 792 | flags |= COMMENT_PRINT_ORIG_BREAK; |
| 793 | + flags &= COMMENT_PRINT_CANONICAL; | |
| 738 | 794 | } |
| 739 | 795 | zWidth = find_option("width","W",1); |
| 740 | 796 | if( zWidth ){ |
| 741 | 797 | width = atoi(zWidth); |
| 742 | 798 | }else{ |
| @@ -747,45 +803,51 @@ | ||
| 747 | 803 | indent = atoi(zIndent); |
| 748 | 804 | }else{ |
| 749 | 805 | indent = -1; /* automatic */ |
| 750 | 806 | } |
| 751 | 807 | verify_all_options(); |
| 752 | - if( g.argc!=4 && g.argc!=5 ){ | |
| 753 | - usage("?OPTIONS? PREFIX TEXT ?ORIGTEXT?"); | |
| 754 | - } | |
| 755 | - zPrefix = g.argv[2]; | |
| 756 | - zText = g.argv[3]; | |
| 757 | - if( g.argc==5 ){ | |
| 758 | - zOrigText = g.argv[4]; | |
| 759 | - }else{ | |
| 760 | - zOrigText = 0; | |
| 761 | - } | |
| 808 | + zPrefix = zText = zOrigText = 0; | |
| 762 | 809 | if( fromFile ){ |
| 763 | 810 | Blob fileData; |
| 764 | - blob_read_from_file(&fileData, zText, ExtFILE); | |
| 811 | + blob_read_from_file(&fileData, fromFile, ExtFILE); | |
| 765 | 812 | zText = mprintf("%s", blob_str(&fileData)); |
| 766 | 813 | blob_reset(&fileData); |
| 767 | - if( zOrigText ){ | |
| 768 | - blob_read_from_file(&fileData, zOrigText, ExtFILE); | |
| 769 | - zOrigText = mprintf("%s", blob_str(&fileData)); | |
| 770 | - blob_reset(&fileData); | |
| 814 | + } | |
| 815 | + if( fromOrig ){ | |
| 816 | + Blob fileData; | |
| 817 | + blob_read_from_file(&fileData, fromOrig, ExtFILE); | |
| 818 | + zOrigText = mprintf("%s", blob_str(&fileData)); | |
| 819 | + blob_reset(&fileData); | |
| 820 | + } | |
| 821 | + for(i=2; i<g.argc; i++){ | |
| 822 | + if( zText==0 ){ | |
| 823 | + zText = g.argv[i]; | |
| 824 | + continue; | |
| 825 | + } | |
| 826 | + if( zPrefix==0 ){ | |
| 827 | + zPrefix = g.argv[i]; | |
| 828 | + continue; | |
| 829 | + } | |
| 830 | + if( zOrigText==0 ){ | |
| 831 | + zOrigText = g.argv[i]; | |
| 832 | + continue; | |
| 771 | 833 | } |
| 834 | + usage("[OPTIONS] TEXT [PREFIX] [ORIGTEXT]"); | |
| 772 | 835 | } |
| 773 | 836 | if( decode ){ |
| 774 | 837 | zText = mprintf(fromFile?"%z":"%s" /*works-like:"%s"*/, zText); |
| 775 | 838 | defossilize(zText); |
| 776 | 839 | if( zOrigText ){ |
| 777 | 840 | zOrigText = mprintf(fromFile?"%z":"%s" /*works-like:"%s"*/, zOrigText); |
| 778 | 841 | defossilize(zOrigText); |
| 779 | 842 | } |
| 780 | 843 | } |
| 844 | + if( zPrefix==0 ) zPrefix = "00:00:00 "; | |
| 781 | 845 | if( indent<0 ){ |
| 782 | 846 | indent = strlen(zPrefix); |
| 783 | 847 | } |
| 784 | 848 | if( zPrefix && *zPrefix ){ |
| 785 | 849 | fossil_print("%s", zPrefix); |
| 786 | 850 | } |
| 787 | 851 | fossil_print("(%d lines output)\n", |
| 788 | 852 | comment_print(zText, zOrigText, indent, width, flags)); |
| 789 | - if( zOrigText && zOrigText!=g.argv[4] ) fossil_free(zOrigText); | |
| 790 | - if( zText && zText!=g.argv[3] ) fossil_free(zText); | |
| 791 | 853 | } |
| 792 | 854 |
| --- src/comformat.c | |
| +++ src/comformat.c | |
| @@ -21,18 +21,22 @@ | |
| 21 | #include "config.h" |
| 22 | #include "comformat.h" |
| 23 | #include <assert.h> |
| 24 | |
| 25 | #if INTERFACE |
| 26 | #define COMMENT_PRINT_NONE ((u32)0x00000000) /* No flags = non-legacy. */ |
| 27 | #define COMMENT_PRINT_LEGACY ((u32)0x00000001) /* Use legacy algorithm. */ |
| 28 | #define COMMENT_PRINT_TRIM_CRLF ((u32)0x00000002) /* Trim leading CR/LF. */ |
| 29 | #define COMMENT_PRINT_TRIM_SPACE ((u32)0x00000004) /* Trim leading/trailing. */ |
| 30 | #define COMMENT_PRINT_WORD_BREAK ((u32)0x00000008) /* Break lines on words. */ |
| 31 | #define COMMENT_PRINT_ORIG_BREAK ((u32)0x00000010) /* Break before original. */ |
| 32 | #define COMMENT_PRINT_DEFAULT (COMMENT_PRINT_LEGACY) /* Defaults. */ |
| 33 | #define COMMENT_PRINT_UNSET (-1) /* Not initialized. */ |
| 34 | #endif |
| 35 | |
| 36 | /********* Code copied from SQLite src/shell.c.in on 2024-09-30 **********/ |
| 37 | /* Lookup table to estimate the number of columns consumed by a Unicode |
| 38 | ** character. |
| @@ -220,10 +224,11 @@ | |
| 220 | int maxChars, /* [in] Optimization hint to abort before space found. */ |
| 221 | int *sumWidth /* [out] Summated width of all characters to next space. */ |
| 222 | ){ |
| 223 | int cchUTF8, utf32, wcwidth = 0; |
| 224 | int nextIndex = index; |
| 225 | for(;;){ |
| 226 | char_info_utf8(&zLine[nextIndex],&cchUTF8,&utf32); |
| 227 | nextIndex += cchUTF8; |
| 228 | wcwidth += cli_wcwidth(utf32); |
| 229 | if( zLine[nextIndex]==0 || fossil_isspace(zLine[nextIndex]) || |
| @@ -234,29 +239,46 @@ | |
| 234 | } |
| 235 | return 0; /* NOT REACHED */ |
| 236 | } |
| 237 | |
| 238 | /* |
| 239 | ** Return information about the next (single- or multi-byte) character in the |
| 240 | ** specified UTF-8 string: The number of UTF-8 code units (in this case: bytes) |
| 241 | ** and the decoded UTF-32 code point. Incomplete, ill-formed and overlong |
| 242 | ** sequences are consumed together as one invalid code point. The invalid lead |
| 243 | ** bytes 0xC0 to 0xC1 and 0xF5 to 0xF7 are allowed to initiate (ill-formed) 2- |
| 244 | ** and 4-byte sequences, respectively, the other invalid lead bytes 0xF8 to 0xFF |
| 245 | ** are treated as invalid 1-byte sequences (as lone trail bytes), all resulting |
| 246 | ** in one invalid code point. Invalid UTF-8 sequences encoding a non-scalar code |
| 247 | ** point (UTF-16 surrogates U+D800 to U+DFFF) are allowed. |
| 248 | */ |
| 249 | void char_info_utf8( |
| 250 | const char *z, |
| 251 | int *pCchUTF8, |
| 252 | int *pUtf32 |
| 253 | ){ |
| 254 | int i = 0; /* Counted bytes. */ |
| 255 | int cchUTF8 = 1; /* Code units consumed. */ |
| 256 | int maxUTF8 = 1; /* Expected sequence length. */ |
| 257 | char c = z[i++]; |
| 258 | if( (c&0x80)==0x00 ){ /* 7-bit ASCII character. */ |
| 259 | *pCchUTF8 = 1; |
| 260 | *pUtf32 = (int)z[0]; |
| 261 | return; |
| 262 | } |
| @@ -465,21 +487,29 @@ | |
| 465 | *pzLine = zLine + index; |
| 466 | } |
| 467 | } |
| 468 | |
| 469 | /* |
| 470 | ** This is the legacy comment printing algorithm. It is being retained |
| 471 | ** for backward compatibility. |
| 472 | ** |
| 473 | ** Given a comment string, format that string for printing on a TTY. |
| 474 | ** Assume that the output cursors is indent spaces from the left margin |
| 475 | ** and that a single line can contain no more than 'width' characters. |
| 476 | ** Indent all subsequent lines by 'indent'. |
| 477 | ** |
| 478 | ** Returns the number of new lines emitted. |
| 479 | */ |
| 480 | static int comment_print_legacy( |
| 481 | const char *zText, /* The comment text to be printed. */ |
| 482 | int indent, /* Number of spaces to indent each non-initial line. */ |
| 483 | int width /* Maximum number of characters per line. */ |
| 484 | ){ |
| 485 | int maxChars = width - indent; |
| @@ -564,13 +594,18 @@ | |
| 564 | ** This is the comment printing function. The comment printing algorithm |
| 565 | ** contained within it attempts to preserve the formatting present within |
| 566 | ** the comment string itself while honoring line width limitations. There |
| 567 | ** are several flags that modify the default behavior of this function: |
| 568 | ** |
| 569 | ** COMMENT_PRINT_LEGACY: Forces use of the legacy comment printing |
| 570 | ** algorithm. For backward compatibility, |
| 571 | ** this is the default. |
| 572 | ** |
| 573 | ** COMMENT_PRINT_TRIM_CRLF: Trims leading and trailing carriage-returns |
| 574 | ** and line-feeds where they do not materially |
| 575 | ** impact pre-existing formatting (i.e. at the |
| 576 | ** start of the comment string -AND- right |
| @@ -613,56 +648,61 @@ | |
| 613 | int indent, /* Spaces to indent each non-initial line. */ |
| 614 | int width, /* Maximum number of characters per line. */ |
| 615 | int flags /* Zero or more "COMMENT_PRINT_*" flags. */ |
| 616 | ){ |
| 617 | int maxChars = width - indent; |
| 618 | int legacy = flags & COMMENT_PRINT_LEGACY; |
| 619 | int trimCrLf = flags & COMMENT_PRINT_TRIM_CRLF; |
| 620 | int trimSpace = flags & COMMENT_PRINT_TRIM_SPACE; |
| 621 | int wordBreak = flags & COMMENT_PRINT_WORD_BREAK; |
| 622 | int origBreak = flags & COMMENT_PRINT_ORIG_BREAK; |
| 623 | int lineCnt = 0; |
| 624 | const char *zLine; |
| 625 | |
| 626 | if( legacy ){ |
| 627 | return comment_print_legacy(zText, indent, width); |
| 628 | } |
| 629 | if( width<0 ){ |
| 630 | comment_set_maxchars(indent, &maxChars); |
| 631 | } |
| 632 | if( zText==0 ) zText = "(NULL)"; |
| 633 | if( maxChars<=0 ){ |
| 634 | maxChars = strlen(zText); |
| 635 | } |
| 636 | if( trimSpace ){ |
| 637 | while( fossil_isspace(zText[0]) ){ zText++; } |
| 638 | } |
| 639 | if( zText[0]==0 ){ |
| 640 | fossil_print("\n"); |
| 641 | lineCnt++; |
| 642 | return lineCnt; |
| 643 | } |
| 644 | zLine = zText; |
| 645 | for(;;){ |
| 646 | comment_print_line(zOrigText, zLine, indent, zLine>zText ? indent : 0, |
| 647 | maxChars, trimCrLf, trimSpace, wordBreak, origBreak, |
| 648 | &lineCnt, &zLine); |
| 649 | if( zLine==0 ) break; |
| 650 | while( fossil_isspace(zLine[0]) ) zLine++; |
| 651 | if( zLine[0]==0 ) break; |
| 652 | } |
| 653 | return lineCnt; |
| 654 | } |
| 655 | |
| 656 | /* |
| 657 | ** Return the "COMMENT_PRINT_*" flags specified by the following sources, |
| 658 | ** evaluated in the following cascading order: |
| 659 | ** |
| 660 | ** 1. The global --comfmtflags (alias --comment-format) command-line option. |
| 661 | ** 2. The local (per-repository) "comment-format" setting. |
| 662 | ** 3. The global (all-repositories) "comment-format" setting. |
| 663 | ** 4. The default value COMMENT_PRINT_DEFAULT. |
| 664 | */ |
| 665 | int get_comment_format(){ |
| 666 | int comFmtFlags; |
| 667 | |
| 668 | /* We must cache this result, else running the timeline can end up |
| @@ -689,54 +729,70 @@ | |
| 689 | |
| 690 | /* |
| 691 | ** |
| 692 | ** COMMAND: test-comment-format |
| 693 | ** |
| 694 | ** Usage: %fossil test-comment-format ?OPTIONS? PREFIX TEXT ?ORIGTEXT? |
| 695 | ** |
| 696 | ** Test comment formatting and printing. Use for testing only. |
| 697 | ** |
| 698 | ** Options: |
| 699 | ** --file The comment text is really just a file name to |
| 700 | ** read it from |
| 701 | ** --decode Decode the text using the same method used when |
| 702 | ** handling the value of a C-card from a manifest. |
| 703 | ** --legacy Use the legacy comment printing algorithm |
| 704 | ** --trimcrlf Enable trimming of leading/trailing CR/LF |
| 705 | ** --trimspace Enable trimming of leading/trailing spaces |
| 706 | ** --wordbreak Attempt to break lines on word boundaries |
| 707 | ** --origbreak Attempt to break when the original comment text |
| 708 | ** is detected |
| 709 | ** --indent Number of spaces to indent (default (-1) is to |
| 710 | ** auto-detect). Zero means no indent. |
| 711 | ** -W|--width NUM Width of lines (default (-1) is to auto-detect). |
| 712 | ** Zero means no limit. |
| 713 | */ |
| 714 | void test_comment_format(void){ |
| 715 | const char *zWidth; |
| 716 | const char *zIndent; |
| 717 | const char *zPrefix; |
| 718 | char *zText; |
| 719 | char *zOrigText; |
| 720 | int indent, width; |
| 721 | int fromFile = find_option("file", 0, 0)!=0; |
| 722 | int decode = find_option("decode", 0, 0)!=0; |
| 723 | int flags = COMMENT_PRINT_NONE; |
| 724 | if( find_option("legacy", 0, 0) ){ |
| 725 | flags |= COMMENT_PRINT_LEGACY; |
| 726 | } |
| 727 | if( find_option("trimcrlf", 0, 0) ){ |
| 728 | flags |= COMMENT_PRINT_TRIM_CRLF; |
| 729 | } |
| 730 | if( find_option("trimspace", 0, 0) ){ |
| 731 | flags |= COMMENT_PRINT_TRIM_SPACE; |
| 732 | } |
| 733 | if( find_option("wordbreak", 0, 0) ){ |
| 734 | flags |= COMMENT_PRINT_WORD_BREAK; |
| 735 | } |
| 736 | if( find_option("origbreak", 0, 0) ){ |
| 737 | flags |= COMMENT_PRINT_ORIG_BREAK; |
| 738 | } |
| 739 | zWidth = find_option("width","W",1); |
| 740 | if( zWidth ){ |
| 741 | width = atoi(zWidth); |
| 742 | }else{ |
| @@ -747,45 +803,51 @@ | |
| 747 | indent = atoi(zIndent); |
| 748 | }else{ |
| 749 | indent = -1; /* automatic */ |
| 750 | } |
| 751 | verify_all_options(); |
| 752 | if( g.argc!=4 && g.argc!=5 ){ |
| 753 | usage("?OPTIONS? PREFIX TEXT ?ORIGTEXT?"); |
| 754 | } |
| 755 | zPrefix = g.argv[2]; |
| 756 | zText = g.argv[3]; |
| 757 | if( g.argc==5 ){ |
| 758 | zOrigText = g.argv[4]; |
| 759 | }else{ |
| 760 | zOrigText = 0; |
| 761 | } |
| 762 | if( fromFile ){ |
| 763 | Blob fileData; |
| 764 | blob_read_from_file(&fileData, zText, ExtFILE); |
| 765 | zText = mprintf("%s", blob_str(&fileData)); |
| 766 | blob_reset(&fileData); |
| 767 | if( zOrigText ){ |
| 768 | blob_read_from_file(&fileData, zOrigText, ExtFILE); |
| 769 | zOrigText = mprintf("%s", blob_str(&fileData)); |
| 770 | blob_reset(&fileData); |
| 771 | } |
| 772 | } |
| 773 | if( decode ){ |
| 774 | zText = mprintf(fromFile?"%z":"%s" /*works-like:"%s"*/, zText); |
| 775 | defossilize(zText); |
| 776 | if( zOrigText ){ |
| 777 | zOrigText = mprintf(fromFile?"%z":"%s" /*works-like:"%s"*/, zOrigText); |
| 778 | defossilize(zOrigText); |
| 779 | } |
| 780 | } |
| 781 | if( indent<0 ){ |
| 782 | indent = strlen(zPrefix); |
| 783 | } |
| 784 | if( zPrefix && *zPrefix ){ |
| 785 | fossil_print("%s", zPrefix); |
| 786 | } |
| 787 | fossil_print("(%d lines output)\n", |
| 788 | comment_print(zText, zOrigText, indent, width, flags)); |
| 789 | if( zOrigText && zOrigText!=g.argv[4] ) fossil_free(zOrigText); |
| 790 | if( zText && zText!=g.argv[3] ) fossil_free(zText); |
| 791 | } |
| 792 |
| --- src/comformat.c | |
| +++ src/comformat.c | |
| @@ -21,18 +21,22 @@ | |
| 21 | #include "config.h" |
| 22 | #include "comformat.h" |
| 23 | #include <assert.h> |
| 24 | |
| 25 | #if INTERFACE |
| 26 | #define COMMENT_PRINT_NONE ((u32)0x00000000) /* No flags */ |
| 27 | #define COMMENT_PRINT_CANONICAL ((u32)0x00000001) /* Use canonical algorithm */ |
| 28 | #define COMMENT_PRINT_DEFAULT COMMENT_PRINT_CANONICAL /* Default */ |
| 29 | #define COMMENT_PRINT_UNSET (-1) /* Not initialized */ |
| 30 | |
| 31 | /* The canonical comment printing algorithm is recommended. We make |
| 32 | ** no promise of on-going support for any of the following flags: |
| 33 | */ |
| 34 | #define COMMENT_PRINT_TRIM_CRLF ((u32)0x00000002) /* Trim leading CR/LF. */ |
| 35 | #define COMMENT_PRINT_TRIM_SPACE ((u32)0x00000004) /* Trim leading/trailing. */ |
| 36 | #define COMMENT_PRINT_WORD_BREAK ((u32)0x00000008) /* Break lines on words. */ |
| 37 | #define COMMENT_PRINT_ORIG_BREAK ((u32)0x00000010) /* Break before original. */ |
| 38 | #endif |
| 39 | |
| 40 | /********* Code copied from SQLite src/shell.c.in on 2024-09-30 **********/ |
| 41 | /* Lookup table to estimate the number of columns consumed by a Unicode |
| 42 | ** character. |
| @@ -220,10 +224,11 @@ | |
| 224 | int maxChars, /* [in] Optimization hint to abort before space found. */ |
| 225 | int *sumWidth /* [out] Summated width of all characters to next space. */ |
| 226 | ){ |
| 227 | int cchUTF8, utf32, wcwidth = 0; |
| 228 | int nextIndex = index; |
| 229 | if( zLine[index]==0 ) return index; |
| 230 | for(;;){ |
| 231 | char_info_utf8(&zLine[nextIndex],&cchUTF8,&utf32); |
| 232 | nextIndex += cchUTF8; |
| 233 | wcwidth += cli_wcwidth(utf32); |
| 234 | if( zLine[nextIndex]==0 || fossil_isspace(zLine[nextIndex]) || |
| @@ -234,29 +239,46 @@ | |
| 239 | } |
| 240 | return 0; /* NOT REACHED */ |
| 241 | } |
| 242 | |
| 243 | /* |
| 244 | ** Return information about the next (single- or multi-byte) character in |
| 245 | ** z[0]. Two values are computed: |
| 246 | ** |
| 247 | ** * The number of bytes needed to represent the character. |
| 248 | ** * The UTF code point value. |
| 249 | ** |
| 250 | ** Incomplete, ill-formed and overlong sequences are consumed together as |
| 251 | ** one invalid code point. The invalid lead bytes 0xC0 to 0xC1 and 0xF5 to |
| 252 | ** 0xF7 are allowed to initiate (ill-formed) 2- and 4-byte sequences, |
| 253 | ** respectively, the other invalid lead bytes 0xF8 to 0xFF are treated |
| 254 | ** as invalid 1-byte sequences (as lone trail bytes), all resulting |
| 255 | ** in one invalid code point. Invalid UTF-8 sequences encoding a |
| 256 | ** non-scalar code point (UTF-16 surrogates U+D800 to U+DFFF) are allowed. |
| 257 | ** |
| 258 | ** ANSI escape sequences of the form "\033[...X" are interpreted as a |
| 259 | ** zero-width character. |
| 260 | */ |
| 261 | void char_info_utf8( |
| 262 | const char *z, /* The character to be analyzed */ |
| 263 | int *pCchUTF8, /* OUT: The number of bytes used by this character */ |
| 264 | int *pUtf32 /* OUT: The UTF8 code point (used to determine width) */ |
| 265 | ){ |
| 266 | int i = 0; /* Counted bytes. */ |
| 267 | int cchUTF8 = 1; /* Code units consumed. */ |
| 268 | int maxUTF8 = 1; /* Expected sequence length. */ |
| 269 | char c = z[i++]; |
| 270 | if( c==0x1b && z[i]=='[' ){ |
| 271 | i++; |
| 272 | while( z[i]>=0x30 && z[i]<=0x3f ){ i++; } |
| 273 | while( z[i]>=0x20 && z[i]<=0x2f ){ i++; } |
| 274 | if( z[i]>=0x40 && z[i]<=0x7e ){ |
| 275 | *pCchUTF8 = i+1; |
| 276 | *pUtf32 = 0x301; /* A zero-width character */ |
| 277 | return; |
| 278 | } |
| 279 | } |
| 280 | if( (c&0x80)==0x00 ){ /* 7-bit ASCII character. */ |
| 281 | *pCchUTF8 = 1; |
| 282 | *pUtf32 = (int)z[0]; |
| 283 | return; |
| 284 | } |
| @@ -465,21 +487,29 @@ | |
| 487 | *pzLine = zLine + index; |
| 488 | } |
| 489 | } |
| 490 | |
| 491 | /* |
| 492 | ** This is the canonical comment printing algorithm. This is the algorithm |
| 493 | ** that is recommended and that is used unless the administrator has made |
| 494 | ** special arrangements to use a customized algorithm. |
| 495 | ** |
| 496 | ** Given a comment string, format that string for printing on a TTY. |
| 497 | ** Assume that the output cursor is indent spaces from the left margin |
| 498 | ** and that a single line can contain no more than 'width' characters. |
| 499 | ** Indent all subsequent lines by 'indent'. |
| 500 | ** |
| 501 | ** Formatting features: |
| 502 | ** |
| 503 | ** * Leading whitespace is removed. |
| 504 | ** * Internal whitespace sequences are changed into a single space (0x20) |
| 505 | ** character. |
| 506 | ** * Lines are broken at a space, or at a hyphen ("-") whenever possible. |
| 507 | ** |
| 508 | ** Returns the number of new lines emitted. |
| 509 | */ |
| 510 | static int comment_print_canonical( |
| 511 | const char *zText, /* The comment text to be printed. */ |
| 512 | int indent, /* Number of spaces to indent each non-initial line. */ |
| 513 | int width /* Maximum number of characters per line. */ |
| 514 | ){ |
| 515 | int maxChars = width - indent; |
| @@ -564,13 +594,18 @@ | |
| 594 | ** This is the comment printing function. The comment printing algorithm |
| 595 | ** contained within it attempts to preserve the formatting present within |
| 596 | ** the comment string itself while honoring line width limitations. There |
| 597 | ** are several flags that modify the default behavior of this function: |
| 598 | ** |
| 599 | ** COMMENT_PRINT_CANONICAL: Use the canonical printing algorithm: |
| 600 | ** * Omit leading and trailing whitespace |
| 601 | ** * Collapse internal whitespace into a |
| 602 | ** single space (0x20) character. |
| 603 | ** * Attempt to break lines at whitespace |
| 604 | ** or hyphens. |
| 605 | ** This is the recommended algorithm and is |
| 606 | ** used in most cases. |
| 607 | ** |
| 608 | ** COMMENT_PRINT_TRIM_CRLF: Trims leading and trailing carriage-returns |
| 609 | ** and line-feeds where they do not materially |
| 610 | ** impact pre-existing formatting (i.e. at the |
| 611 | ** start of the comment string -AND- right |
| @@ -613,56 +648,61 @@ | |
| 648 | int indent, /* Spaces to indent each non-initial line. */ |
| 649 | int width, /* Maximum number of characters per line. */ |
| 650 | int flags /* Zero or more "COMMENT_PRINT_*" flags. */ |
| 651 | ){ |
| 652 | int maxChars = width - indent; |
| 653 | |
| 654 | if( flags & COMMENT_PRINT_CANONICAL ){ |
| 655 | /* Use the canonical algorithm. This is what happens in almost |
| 656 | ** all cases. */ |
| 657 | return comment_print_canonical(zText, indent, width); |
| 658 | }else{ |
| 659 | /* The remaining is a more complex formatting algorithm that is very |
| 660 | ** seldom used and is considered deprecated. |
| 661 | */ |
| 662 | int trimCrLf = flags & COMMENT_PRINT_TRIM_CRLF; |
| 663 | int trimSpace = flags & COMMENT_PRINT_TRIM_SPACE; |
| 664 | int wordBreak = flags & COMMENT_PRINT_WORD_BREAK; |
| 665 | int origBreak = flags & COMMENT_PRINT_ORIG_BREAK; |
| 666 | int lineCnt = 0; |
| 667 | const char *zLine; |
| 668 | |
| 669 | if( width<0 ){ |
| 670 | comment_set_maxchars(indent, &maxChars); |
| 671 | } |
| 672 | if( zText==0 ) zText = "(NULL)"; |
| 673 | if( maxChars<=0 ){ |
| 674 | maxChars = strlen(zText); |
| 675 | } |
| 676 | if( trimSpace ){ |
| 677 | while( fossil_isspace(zText[0]) ){ zText++; } |
| 678 | } |
| 679 | if( zText[0]==0 ){ |
| 680 | fossil_print("\n"); |
| 681 | lineCnt++; |
| 682 | return lineCnt; |
| 683 | } |
| 684 | zLine = zText; |
| 685 | for(;;){ |
| 686 | comment_print_line(zOrigText, zLine, indent, zLine>zText ? indent : 0, |
| 687 | maxChars, trimCrLf, trimSpace, wordBreak, origBreak, |
| 688 | &lineCnt, &zLine); |
| 689 | if( zLine==0 ) break; |
| 690 | while( fossil_isspace(zLine[0]) ) zLine++; |
| 691 | if( zLine[0]==0 ) break; |
| 692 | } |
| 693 | return lineCnt; |
| 694 | } |
| 695 | } |
| 696 | |
| 697 | /* |
| 698 | ** Return the "COMMENT_PRINT_*" flags specified by the following sources, |
| 699 | ** evaluated in the following cascading order: |
| 700 | ** |
| 701 | ** 1. The local (per-repository) "comment-format" setting. |
| 702 | ** 2. The global (all-repositories) "comment-format" setting. |
| 703 | ** 3. The default value COMMENT_PRINT_DEFAULT. |
| 704 | */ |
| 705 | int get_comment_format(){ |
| 706 | int comFmtFlags; |
| 707 | |
| 708 | /* We must cache this result, else running the timeline can end up |
| @@ -689,54 +729,70 @@ | |
| 729 | |
| 730 | /* |
| 731 | ** |
| 732 | ** COMMAND: test-comment-format |
| 733 | ** |
| 734 | ** Usage: %fossil test-comment-format [OPTIONS] TEXT [PREFIX] [ORIGTEXT] |
| 735 | ** |
| 736 | ** Test comment formatting and printing. Use for testing only. |
| 737 | ** |
| 738 | ** The default (canonical) formatting algorithm is: |
| 739 | ** |
| 740 | ** * Omit leading/trailing whitespace |
| 741 | ** * Collapse internal whitespace into a single space character. |
| 742 | ** * Attempt to break lines at whitespace or at a hyphen. |
| 743 | ** |
| 744 | ** Use --whitespace, --origbreak, --trimcrlf, --trimspace, |
| 745 | ** and/or --wordbreak to disable the canonical processing and do |
| 746 | ** the special processing specified by those other options. |
| 747 | ** |
| 748 | ** Options: |
| 749 | ** --decode Decode the text using the same method used when |
| 750 | ** handling the value of a C-card from a manifest. |
| 751 | ** --file FILE Omit the TEXT argument and read the comment text |
| 752 | ** from FILE. |
| 753 | ** --indent Number of spaces to indent (default (-1) is to |
| 754 | ** auto-detect). Zero means no indent. |
| 755 | ** --orig FILE Take the value for the ORIGTEXT argumetn from FILE. |
| 756 | ** --origbreak Attempt to break when the original comment text |
| 757 | ** is detected. |
| 758 | ** --trimcrlf Enable trimming of leading/trailing CR/LF. |
| 759 | ** --trimspace Enable trimming of leading/trailing spaces. |
| 760 | ** --whitespace Keep all internal whitespace. |
| 761 | ** --wordbreak Attempt to break lines on word boundaries. |
| 762 | ** -W|--width NUM Width of lines (default (-1) is to auto-detect). |
| 763 | ** Zero means no limit. |
| 764 | */ |
| 765 | void test_comment_format(void){ |
| 766 | const char *zWidth; |
| 767 | const char *zIndent; |
| 768 | const char *zPrefix = 0; |
| 769 | char *zText = 0; |
| 770 | char *zOrigText = 0; |
| 771 | int indent, width; |
| 772 | int i; |
| 773 | const char *fromFile = find_option("file", 0, 1); |
| 774 | int decode = find_option("decode", 0, 0)!=0; |
| 775 | int flags = COMMENT_PRINT_CANONICAL; |
| 776 | const char *fromOrig = find_option("orig", 0, 1); |
| 777 | if( find_option("whitespace",0,0) ){ |
| 778 | flags = 0; |
| 779 | } |
| 780 | if( find_option("trimcrlf", 0, 0) ){ |
| 781 | flags = COMMENT_PRINT_TRIM_CRLF; |
| 782 | } |
| 783 | if( find_option("trimspace", 0, 0) ){ |
| 784 | flags |= COMMENT_PRINT_TRIM_SPACE; |
| 785 | flags &= COMMENT_PRINT_CANONICAL; |
| 786 | } |
| 787 | if( find_option("wordbreak", 0, 0) ){ |
| 788 | flags |= COMMENT_PRINT_WORD_BREAK; |
| 789 | flags &= COMMENT_PRINT_CANONICAL; |
| 790 | } |
| 791 | if( find_option("origbreak", 0, 0) ){ |
| 792 | flags |= COMMENT_PRINT_ORIG_BREAK; |
| 793 | flags &= COMMENT_PRINT_CANONICAL; |
| 794 | } |
| 795 | zWidth = find_option("width","W",1); |
| 796 | if( zWidth ){ |
| 797 | width = atoi(zWidth); |
| 798 | }else{ |
| @@ -747,45 +803,51 @@ | |
| 803 | indent = atoi(zIndent); |
| 804 | }else{ |
| 805 | indent = -1; /* automatic */ |
| 806 | } |
| 807 | verify_all_options(); |
| 808 | zPrefix = zText = zOrigText = 0; |
| 809 | if( fromFile ){ |
| 810 | Blob fileData; |
| 811 | blob_read_from_file(&fileData, fromFile, ExtFILE); |
| 812 | zText = mprintf("%s", blob_str(&fileData)); |
| 813 | blob_reset(&fileData); |
| 814 | } |
| 815 | if( fromOrig ){ |
| 816 | Blob fileData; |
| 817 | blob_read_from_file(&fileData, fromOrig, ExtFILE); |
| 818 | zOrigText = mprintf("%s", blob_str(&fileData)); |
| 819 | blob_reset(&fileData); |
| 820 | } |
| 821 | for(i=2; i<g.argc; i++){ |
| 822 | if( zText==0 ){ |
| 823 | zText = g.argv[i]; |
| 824 | continue; |
| 825 | } |
| 826 | if( zPrefix==0 ){ |
| 827 | zPrefix = g.argv[i]; |
| 828 | continue; |
| 829 | } |
| 830 | if( zOrigText==0 ){ |
| 831 | zOrigText = g.argv[i]; |
| 832 | continue; |
| 833 | } |
| 834 | usage("[OPTIONS] TEXT [PREFIX] [ORIGTEXT]"); |
| 835 | } |
| 836 | if( decode ){ |
| 837 | zText = mprintf(fromFile?"%z":"%s" /*works-like:"%s"*/, zText); |
| 838 | defossilize(zText); |
| 839 | if( zOrigText ){ |
| 840 | zOrigText = mprintf(fromFile?"%z":"%s" /*works-like:"%s"*/, zOrigText); |
| 841 | defossilize(zOrigText); |
| 842 | } |
| 843 | } |
| 844 | if( zPrefix==0 ) zPrefix = "00:00:00 "; |
| 845 | if( indent<0 ){ |
| 846 | indent = strlen(zPrefix); |
| 847 | } |
| 848 | if( zPrefix && *zPrefix ){ |
| 849 | fossil_print("%s", zPrefix); |
| 850 | } |
| 851 | fossil_print("(%d lines output)\n", |
| 852 | comment_print(zText, zOrigText, indent, width, flags)); |
| 853 | } |
| 854 |
+3
-1
| --- src/configure.c | ||
| +++ src/configure.c | ||
| @@ -100,11 +100,10 @@ | ||
| 100 | 100 | { "logo-image", CONFIGSET_SKIN }, |
| 101 | 101 | { "background-mimetype", CONFIGSET_SKIN }, |
| 102 | 102 | { "background-image", CONFIGSET_SKIN }, |
| 103 | 103 | { "icon-mimetype", CONFIGSET_SKIN }, |
| 104 | 104 | { "icon-image", CONFIGSET_SKIN }, |
| 105 | - { "timeline-block-markup", CONFIGSET_SKIN }, | |
| 106 | 105 | { "timeline-date-format", CONFIGSET_SKIN }, |
| 107 | 106 | { "timeline-default-style", CONFIGSET_SKIN }, |
| 108 | 107 | { "timeline-dwelltime", CONFIGSET_SKIN }, |
| 109 | 108 | { "timeline-closetime", CONFIGSET_SKIN }, |
| 110 | 109 | { "timeline-hard-newlines", CONFIGSET_SKIN }, |
| @@ -817,10 +816,12 @@ | ||
| 817 | 816 | ** |
| 818 | 817 | ** Synchronize configuration changes in the local repository with |
| 819 | 818 | ** the remote repository at URL. |
| 820 | 819 | ** |
| 821 | 820 | ** Options: |
| 821 | +** --proxy PROXY Use PROXY as http proxy during sync operation | |
| 822 | +** (used by pull, push and sync subcommands) | |
| 822 | 823 | ** -R|--repository REPO Affect repository REPO with changes |
| 823 | 824 | ** |
| 824 | 825 | ** See also: [[settings]], [[unset]] |
| 825 | 826 | */ |
| 826 | 827 | void configuration_cmd(void){ |
| @@ -889,10 +890,11 @@ | ||
| 889 | 890 | } |
| 890 | 891 | url_parse(zServer, URL_PROMPT_PW|URL_USE_CONFIG); |
| 891 | 892 | if( g.url.protocol==0 ) fossil_fatal("no server URL specified"); |
| 892 | 893 | user_select(); |
| 893 | 894 | url_enable_proxy("via proxy: "); |
| 895 | + g.zHttpAuth = get_httpauth(); | |
| 894 | 896 | if( overwriteFlag ) mask |= CONFIGSET_OVERWRITE; |
| 895 | 897 | if( strncmp(zMethod, "push", n)==0 ){ |
| 896 | 898 | client_sync(0,0,(unsigned)mask,0,0); |
| 897 | 899 | }else if( strncmp(zMethod, "pull", n)==0 ){ |
| 898 | 900 | if( overwriteFlag ) db_unprotect(PROTECT_USER); |
| 899 | 901 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -100,11 +100,10 @@ | |
| 100 | { "logo-image", CONFIGSET_SKIN }, |
| 101 | { "background-mimetype", CONFIGSET_SKIN }, |
| 102 | { "background-image", CONFIGSET_SKIN }, |
| 103 | { "icon-mimetype", CONFIGSET_SKIN }, |
| 104 | { "icon-image", CONFIGSET_SKIN }, |
| 105 | { "timeline-block-markup", CONFIGSET_SKIN }, |
| 106 | { "timeline-date-format", CONFIGSET_SKIN }, |
| 107 | { "timeline-default-style", CONFIGSET_SKIN }, |
| 108 | { "timeline-dwelltime", CONFIGSET_SKIN }, |
| 109 | { "timeline-closetime", CONFIGSET_SKIN }, |
| 110 | { "timeline-hard-newlines", CONFIGSET_SKIN }, |
| @@ -817,10 +816,12 @@ | |
| 817 | ** |
| 818 | ** Synchronize configuration changes in the local repository with |
| 819 | ** the remote repository at URL. |
| 820 | ** |
| 821 | ** Options: |
| 822 | ** -R|--repository REPO Affect repository REPO with changes |
| 823 | ** |
| 824 | ** See also: [[settings]], [[unset]] |
| 825 | */ |
| 826 | void configuration_cmd(void){ |
| @@ -889,10 +890,11 @@ | |
| 889 | } |
| 890 | url_parse(zServer, URL_PROMPT_PW|URL_USE_CONFIG); |
| 891 | if( g.url.protocol==0 ) fossil_fatal("no server URL specified"); |
| 892 | user_select(); |
| 893 | url_enable_proxy("via proxy: "); |
| 894 | if( overwriteFlag ) mask |= CONFIGSET_OVERWRITE; |
| 895 | if( strncmp(zMethod, "push", n)==0 ){ |
| 896 | client_sync(0,0,(unsigned)mask,0,0); |
| 897 | }else if( strncmp(zMethod, "pull", n)==0 ){ |
| 898 | if( overwriteFlag ) db_unprotect(PROTECT_USER); |
| 899 |
| --- src/configure.c | |
| +++ src/configure.c | |
| @@ -100,11 +100,10 @@ | |
| 100 | { "logo-image", CONFIGSET_SKIN }, |
| 101 | { "background-mimetype", CONFIGSET_SKIN }, |
| 102 | { "background-image", CONFIGSET_SKIN }, |
| 103 | { "icon-mimetype", CONFIGSET_SKIN }, |
| 104 | { "icon-image", CONFIGSET_SKIN }, |
| 105 | { "timeline-date-format", CONFIGSET_SKIN }, |
| 106 | { "timeline-default-style", CONFIGSET_SKIN }, |
| 107 | { "timeline-dwelltime", CONFIGSET_SKIN }, |
| 108 | { "timeline-closetime", CONFIGSET_SKIN }, |
| 109 | { "timeline-hard-newlines", CONFIGSET_SKIN }, |
| @@ -817,10 +816,12 @@ | |
| 816 | ** |
| 817 | ** Synchronize configuration changes in the local repository with |
| 818 | ** the remote repository at URL. |
| 819 | ** |
| 820 | ** Options: |
| 821 | ** --proxy PROXY Use PROXY as http proxy during sync operation |
| 822 | ** (used by pull, push and sync subcommands) |
| 823 | ** -R|--repository REPO Affect repository REPO with changes |
| 824 | ** |
| 825 | ** See also: [[settings]], [[unset]] |
| 826 | */ |
| 827 | void configuration_cmd(void){ |
| @@ -889,10 +890,11 @@ | |
| 890 | } |
| 891 | url_parse(zServer, URL_PROMPT_PW|URL_USE_CONFIG); |
| 892 | if( g.url.protocol==0 ) fossil_fatal("no server URL specified"); |
| 893 | user_select(); |
| 894 | url_enable_proxy("via proxy: "); |
| 895 | g.zHttpAuth = get_httpauth(); |
| 896 | if( overwriteFlag ) mask |= CONFIGSET_OVERWRITE; |
| 897 | if( strncmp(zMethod, "push", n)==0 ){ |
| 898 | client_sync(0,0,(unsigned)mask,0,0); |
| 899 | }else if( strncmp(zMethod, "pull", n)==0 ){ |
| 900 | if( overwriteFlag ) db_unprotect(PROTECT_USER); |
| 901 |
M
src/db.c
+207
-85
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -1629,10 +1629,12 @@ | ||
| 1629 | 1629 | sqlite3_create_function(db, "chat_msg_from_event", 4, |
| 1630 | 1630 | SQLITE_UTF8 | SQLITE_INNOCUOUS, 0, |
| 1631 | 1631 | chat_msg_from_event, 0, 0); |
| 1632 | 1632 | sqlite3_create_function(db, "inode", 1, SQLITE_UTF8, 0, |
| 1633 | 1633 | file_inode_sql_func,0,0); |
| 1634 | + sqlite3_create_function(db, "artifact_to_json", 1, SQLITE_UTF8, 0, | |
| 1635 | + artifact_to_json_sql_func,0,0); | |
| 1634 | 1636 | |
| 1635 | 1637 | } |
| 1636 | 1638 | |
| 1637 | 1639 | #if USE_SEE |
| 1638 | 1640 | /* |
| @@ -3204,23 +3206,16 @@ | ||
| 3204 | 3206 | |
| 3205 | 3207 | db_unprotect(PROTECT_ALL); |
| 3206 | 3208 | db_set("content-schema", CONTENT_SCHEMA, 0); |
| 3207 | 3209 | db_set("aux-schema", AUX_SCHEMA_MAX, 0); |
| 3208 | 3210 | db_set("rebuilt", get_version(), 0); |
| 3209 | - db_set("admin-log", "1", 0); | |
| 3210 | - db_set("access-log", "1", 0); | |
| 3211 | 3211 | db_multi_exec( |
| 3212 | 3212 | "INSERT INTO config(name,value,mtime)" |
| 3213 | 3213 | " VALUES('server-code', lower(hex(randomblob(20))),now());" |
| 3214 | 3214 | "INSERT INTO config(name,value,mtime)" |
| 3215 | 3215 | " VALUES('project-code', lower(hex(randomblob(20))),now());" |
| 3216 | 3216 | ); |
| 3217 | - if( !db_is_global("autosync") ) db_set_int("autosync", 1, 0); | |
| 3218 | - if( !db_is_global("localauth") ) db_set_int("localauth", 0, 0); | |
| 3219 | - if( !db_is_global("timeline-plaintext") ){ | |
| 3220 | - db_set_int("timeline-plaintext", 1, 0); | |
| 3221 | - } | |
| 3222 | 3217 | db_create_default_users(0, zDefaultUser); |
| 3223 | 3218 | if( zDefaultUser ) g.zLogin = zDefaultUser; |
| 3224 | 3219 | user_select(); |
| 3225 | 3220 | |
| 3226 | 3221 | if( zTemplate ){ |
| @@ -3647,66 +3642,79 @@ | ||
| 3647 | 3642 | ** |
| 3648 | 3643 | ** If the zNonVersionedSetting parameter is not NULL then it holds the |
| 3649 | 3644 | ** non-versioned value for this setting. If both a versioned and a |
| 3650 | 3645 | ** non-versioned value exist and are not equal, then a warning message |
| 3651 | 3646 | ** might be generated. |
| 3647 | +** | |
| 3648 | +** zCkin is normally NULL. In that case, the versioned setting is | |
| 3649 | +** take from the local check-out, if a local checkout exists, or from | |
| 3650 | +** checkin named by the g.zOpenRevision global variable. If zCkin is | |
| 3651 | +** not NULL, then zCkin is the name of the specific checkin from which | |
| 3652 | +** versioned setting value is taken. When zCkin is not NULL, the cache | |
| 3653 | +** is bypassed. | |
| 3652 | 3654 | */ |
| 3653 | -char *db_get_versioned(const char *zName, char *zNonVersionedSetting){ | |
| 3655 | +char *db_get_versioned( | |
| 3656 | + const char *zName, | |
| 3657 | + char *zNonVersionedSetting, | |
| 3658 | + const char *zCkin | |
| 3659 | +){ | |
| 3654 | 3660 | char *zVersionedSetting = 0; |
| 3655 | 3661 | int noWarn = 0; |
| 3656 | 3662 | int found = 0; |
| 3657 | 3663 | struct _cacheEntry { |
| 3658 | 3664 | struct _cacheEntry *next; |
| 3659 | 3665 | const char *zName, *zValue; |
| 3660 | 3666 | } *cacheEntry = 0; |
| 3661 | 3667 | static struct _cacheEntry *cache = 0; |
| 3662 | 3668 | |
| 3663 | - if( !g.localOpen && g.zOpenRevision==0 ) return zNonVersionedSetting; | |
| 3669 | + if( !g.localOpen && g.zOpenRevision==0 && zCkin==0 ){ | |
| 3670 | + return zNonVersionedSetting; | |
| 3671 | + } | |
| 3672 | + | |
| 3664 | 3673 | /* Look up name in cache */ |
| 3665 | - cacheEntry = cache; | |
| 3666 | - while( cacheEntry!=0 ){ | |
| 3667 | - if( fossil_strcmp(cacheEntry->zName, zName)==0 ){ | |
| 3668 | - zVersionedSetting = fossil_strdup(cacheEntry->zValue); | |
| 3669 | - break; | |
| 3670 | - } | |
| 3671 | - cacheEntry = cacheEntry->next; | |
| 3672 | - } | |
| 3674 | + if( zCkin==0 ){ | |
| 3675 | + cacheEntry = cache; | |
| 3676 | + while( cacheEntry!=0 ){ | |
| 3677 | + if( fossil_strcmp(cacheEntry->zName, zName)==0 ){ | |
| 3678 | + zVersionedSetting = fossil_strdup(cacheEntry->zValue); | |
| 3679 | + break; | |
| 3680 | + } | |
| 3681 | + cacheEntry = cacheEntry->next; | |
| 3682 | + } | |
| 3683 | + } | |
| 3684 | + | |
| 3673 | 3685 | /* Attempt to read value from file in check-out if there wasn't a cache hit.*/ |
| 3674 | 3686 | if( cacheEntry==0 ){ |
| 3675 | 3687 | Blob versionedPathname; |
| 3676 | 3688 | Blob setting; |
| 3677 | - blob_zero(&versionedPathname); | |
| 3678 | - blob_zero(&setting); | |
| 3679 | - blob_appendf(&versionedPathname, "%s.fossil-settings/%s", | |
| 3680 | - g.zLocalRoot, zName); | |
| 3681 | - if( !g.localOpen ){ | |
| 3682 | - /* Repository is in the process of being opened, but files have not been | |
| 3683 | - * written to disk. Load from the database. */ | |
| 3684 | - Blob noWarnFile; | |
| 3685 | - if( historical_blob(g.zOpenRevision, blob_str(&versionedPathname), | |
| 3686 | - &setting, 0) ){ | |
| 3687 | - found = 1; | |
| 3688 | - } | |
| 3689 | - /* See if there's a no-warn flag */ | |
| 3690 | - blob_append(&versionedPathname, ".no-warn", -1); | |
| 3691 | - blob_zero(&noWarnFile); | |
| 3692 | - if( historical_blob(g.zOpenRevision, blob_str(&versionedPathname), | |
| 3693 | - &noWarnFile, 0) ){ | |
| 3694 | - noWarn = 1; | |
| 3695 | - } | |
| 3696 | - blob_reset(&noWarnFile); | |
| 3697 | - }else if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){ | |
| 3698 | - /* File exists, and contains the value for this setting. Load from | |
| 3699 | - ** the file. */ | |
| 3700 | - const char *zFile = blob_str(&versionedPathname); | |
| 3701 | - if( blob_read_from_file(&setting, zFile, ExtFILE)>=0 ){ | |
| 3702 | - found = 1; | |
| 3703 | - } | |
| 3704 | - /* See if there's a no-warn flag */ | |
| 3705 | - blob_append(&versionedPathname, ".no-warn", -1); | |
| 3706 | - if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){ | |
| 3707 | - noWarn = 1; | |
| 3689 | + blob_init(&versionedPathname, 0, 0); | |
| 3690 | + blob_init(&setting, 0, 0); | |
| 3691 | + if( !g.localOpen || zCkin!=0 ){ | |
| 3692 | + /* Repository is in the process of being opened, but files have not been | |
| 3693 | + * written to disk. Load from the database. */ | |
| 3694 | + blob_appendf(&versionedPathname, ".fossil-settings/%s", zName); | |
| 3695 | + if( historical_blob(zCkin ? zCkin : g.zOpenRevision, | |
| 3696 | + blob_str(&versionedPathname), | |
| 3697 | + &setting, 0) | |
| 3698 | + ){ | |
| 3699 | + found = 1; | |
| 3700 | + } | |
| 3701 | + }else{ | |
| 3702 | + blob_appendf(&versionedPathname, "%s.fossil-settings/%s", | |
| 3703 | + g.zLocalRoot, zName); | |
| 3704 | + if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){ | |
| 3705 | + /* File exists, and contains the value for this setting. Load from | |
| 3706 | + ** the file. */ | |
| 3707 | + const char *zFile = blob_str(&versionedPathname); | |
| 3708 | + if( blob_read_from_file(&setting, zFile, ExtFILE)>=0 ){ | |
| 3709 | + found = 1; | |
| 3710 | + } | |
| 3711 | + /* See if there's a no-warn flag */ | |
| 3712 | + blob_append(&versionedPathname, ".no-warn", -1); | |
| 3713 | + if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){ | |
| 3714 | + noWarn = 1; | |
| 3715 | + } | |
| 3708 | 3716 | } |
| 3709 | 3717 | } |
| 3710 | 3718 | blob_reset(&versionedPathname); |
| 3711 | 3719 | if( found ){ |
| 3712 | 3720 | blob_strip_comment_lines(&setting, &setting); |
| @@ -3713,20 +3721,27 @@ | ||
| 3713 | 3721 | blob_trim(&setting); /* Avoid non-obvious problems with line endings |
| 3714 | 3722 | ** on boolean properties */ |
| 3715 | 3723 | zVersionedSetting = fossil_strdup(blob_str(&setting)); |
| 3716 | 3724 | } |
| 3717 | 3725 | blob_reset(&setting); |
| 3726 | + | |
| 3718 | 3727 | /* Store result in cache, which can be the value or 0 if not found */ |
| 3719 | - cacheEntry = (struct _cacheEntry*)fossil_malloc(sizeof(struct _cacheEntry)); | |
| 3720 | - cacheEntry->next = cache; | |
| 3721 | - cacheEntry->zName = zName; | |
| 3722 | - cacheEntry->zValue = fossil_strdup(zVersionedSetting); | |
| 3723 | - cache = cacheEntry; | |
| 3728 | + if( zCkin==0 ){ | |
| 3729 | + cacheEntry = (struct _cacheEntry*)fossil_malloc(sizeof(*cacheEntry)); | |
| 3730 | + cacheEntry->next = cache; | |
| 3731 | + cacheEntry->zName = zName; | |
| 3732 | + cacheEntry->zValue = fossil_strdup(zVersionedSetting); | |
| 3733 | + cache = cacheEntry; | |
| 3734 | + } | |
| 3724 | 3735 | } |
| 3736 | + | |
| 3725 | 3737 | /* Display a warning? */ |
| 3726 | - if( zVersionedSetting!=0 && zNonVersionedSetting!=0 | |
| 3727 | - && zNonVersionedSetting[0]!='\0' && !noWarn | |
| 3738 | + if( zVersionedSetting!=0 | |
| 3739 | + && zNonVersionedSetting!=0 | |
| 3740 | + && zNonVersionedSetting[0]!='\0' | |
| 3741 | + && zCkin==0 | |
| 3742 | + && !noWarn | |
| 3728 | 3743 | ){ |
| 3729 | 3744 | /* There's a versioned setting, and a non-versioned setting. Tell |
| 3730 | 3745 | ** the user about the conflict */ |
| 3731 | 3746 | fossil_warning( |
| 3732 | 3747 | "setting %s has both versioned and non-versioned values: using " |
| @@ -3735,10 +3750,11 @@ | ||
| 3735 | 3750 | "\"%/.fossil-settings/%s.no-warn\" in the check-out root, or delete " |
| 3736 | 3751 | "the non-versioned setting with \"fossil unset %s\")", zName, |
| 3737 | 3752 | g.zLocalRoot, zName, g.zLocalRoot, zName, zName |
| 3738 | 3753 | ); |
| 3739 | 3754 | } |
| 3755 | + | |
| 3740 | 3756 | /* Prefer the versioned setting */ |
| 3741 | 3757 | return ( zVersionedSetting!=0 ) ? zVersionedSetting : zNonVersionedSetting; |
| 3742 | 3758 | } |
| 3743 | 3759 | |
| 3744 | 3760 | |
| @@ -3778,11 +3794,11 @@ | ||
| 3778 | 3794 | } |
| 3779 | 3795 | if( pSetting!=0 && pSetting->versionable ){ |
| 3780 | 3796 | /* This is a versionable setting, try and get the info from a |
| 3781 | 3797 | ** checked-out file */ |
| 3782 | 3798 | char * zZ = z; |
| 3783 | - z = db_get_versioned(zName, z); | |
| 3799 | + z = db_get_versioned(zName, z, 0); | |
| 3784 | 3800 | if(zZ != z){ |
| 3785 | 3801 | fossil_free(zZ); |
| 3786 | 3802 | } |
| 3787 | 3803 | } |
| 3788 | 3804 | if( z==0 ){ |
| @@ -3919,11 +3935,11 @@ | ||
| 3919 | 3935 | } |
| 3920 | 3936 | fossil_free(zVal); |
| 3921 | 3937 | return dflt; |
| 3922 | 3938 | } |
| 3923 | 3939 | int db_get_versioned_boolean(const char *zName, int dflt){ |
| 3924 | - char *zVal = db_get_versioned(zName, 0); | |
| 3940 | + char *zVal = db_get_versioned(zName, 0, 0); | |
| 3925 | 3941 | if( zVal==0 ) return dflt; |
| 3926 | 3942 | if( is_truth(zVal) ) return 1; |
| 3927 | 3943 | if( is_false(zVal) ) return 0; |
| 3928 | 3944 | return dflt; |
| 3929 | 3945 | } |
| @@ -4048,14 +4064,30 @@ | ||
| 4048 | 4064 | ** Get the manifest setting. For backwards compatibility first check if the |
| 4049 | 4065 | ** value is a boolean. If it's not a boolean, treat each character as a flag |
| 4050 | 4066 | ** to enable a manifest type. This system puts certain boundary conditions on |
| 4051 | 4067 | ** which letters can be used to represent flags (any permutation of flags must |
| 4052 | 4068 | ** not be able to fully form one of the boolean values). |
| 4069 | +** | |
| 4070 | +** "manifest" is a versionable setting. But we do not issue a warning | |
| 4071 | +** if there is a conflict. Instead, the value returned is the value for | |
| 4072 | +** the versioned setting if the versioned setting exists, or the ordinary | |
| 4073 | +** setting otherwise. | |
| 4074 | +** | |
| 4075 | +** The argument zCkin is the specific check-in for which we want the | |
| 4076 | +** manifest setting. | |
| 4053 | 4077 | */ |
| 4054 | -int db_get_manifest_setting(void){ | |
| 4078 | +int db_get_manifest_setting(const char *zCkin){ | |
| 4055 | 4079 | int flg; |
| 4056 | - char *zVal = db_get("manifest", 0); | |
| 4080 | + char *zVal; | |
| 4081 | + | |
| 4082 | + /* Look for the versioned setting first */ | |
| 4083 | + zVal = db_get_versioned("manifest", 0, zCkin); | |
| 4084 | + | |
| 4085 | + if( zVal==0 && g.repositoryOpen ){ | |
| 4086 | + /* No versioned setting, look for the repository setting second */ | |
| 4087 | + zVal = db_text(0, "SELECT value FROM config WHERE name='manifest'"); | |
| 4088 | + } | |
| 4057 | 4089 | if( zVal==0 || is_false(zVal) ){ |
| 4058 | 4090 | return 0; |
| 4059 | 4091 | }else if( is_truth(zVal) ){ |
| 4060 | 4092 | return MFESTFLG_RAW|MFESTFLG_UUID; |
| 4061 | 4093 | } |
| @@ -4068,10 +4100,37 @@ | ||
| 4068 | 4100 | } |
| 4069 | 4101 | zVal++; |
| 4070 | 4102 | } |
| 4071 | 4103 | return flg; |
| 4072 | 4104 | } |
| 4105 | + | |
| 4106 | +/* | |
| 4107 | +** COMMAND: test-manifest-setting | |
| 4108 | +** | |
| 4109 | +** Usage: %fossil test-manifest-setting VERSION VERSION ... | |
| 4110 | +** | |
| 4111 | +** Display the value for the "manifest" setting for various versions | |
| 4112 | +** of the repository. | |
| 4113 | +*/ | |
| 4114 | +void test_manfest_setting_cmd(void){ | |
| 4115 | + int i; | |
| 4116 | + db_find_and_open_repository(0, 0); | |
| 4117 | + for(i=2; i<g.argc; i++){ | |
| 4118 | + int m = db_get_manifest_setting(g.argv[i]); | |
| 4119 | + fossil_print("%s:\n", g.argv[i]); | |
| 4120 | + fossil_print(" flags = 0x%02x\n", m); | |
| 4121 | + if( m & MFESTFLG_RAW ){ | |
| 4122 | + fossil_print(" manifest\n"); | |
| 4123 | + } | |
| 4124 | + if( m & MFESTFLG_UUID ){ | |
| 4125 | + fossil_print(" manifest.uuid\n"); | |
| 4126 | + } | |
| 4127 | + if( m & MFESTFLG_TAGS ){ | |
| 4128 | + fossil_print(" manifest.tags\n"); | |
| 4129 | + } | |
| 4130 | + } | |
| 4131 | +} | |
| 4073 | 4132 | |
| 4074 | 4133 | |
| 4075 | 4134 | /* |
| 4076 | 4135 | ** Record the name of a local repository in the global_config() database. |
| 4077 | 4136 | ** The repository filename %s is recorded as an entry with a "name" field |
| @@ -4181,10 +4240,11 @@ | ||
| 4181 | 4240 | ** --force-missing Force opening a repository with missing content |
| 4182 | 4241 | ** -k|--keep Only modify the manifest file(s) |
| 4183 | 4242 | ** --nested Allow opening a repository inside an opened check-out |
| 4184 | 4243 | ** --nosync Do not auto-sync the repository prior to opening even |
| 4185 | 4244 | ** if the autosync setting is on. |
| 4245 | +** --proxy PROXY Use PROXY as http proxy during sync operation | |
| 4186 | 4246 | ** --repodir DIR If REPOSITORY is a URI that will be cloned, store |
| 4187 | 4247 | ** the clone in DIR rather than in "." |
| 4188 | 4248 | ** --setmtime Set timestamps of all files to match their SCM-side |
| 4189 | 4249 | ** times (the timestamp of the last check-in which modified |
| 4190 | 4250 | ** them). |
| @@ -4370,16 +4430,37 @@ | ||
| 4370 | 4430 | } |
| 4371 | 4431 | } |
| 4372 | 4432 | g.argc = 2; |
| 4373 | 4433 | info_cmd(); |
| 4374 | 4434 | } |
| 4435 | + | |
| 4436 | +/* | |
| 4437 | +** Return true if pSetting has its default value assuming its | |
| 4438 | +** current value is zVal. | |
| 4439 | +*/ | |
| 4440 | +int setting_has_default_value(const Setting *pSetting, const char *zVal){ | |
| 4441 | + if( zVal==0 ) return 1; | |
| 4442 | + if( pSetting->def==0 ) return 0; | |
| 4443 | + if( pSetting->width==0 ){ | |
| 4444 | + return is_false(pSetting->def)==is_false(zVal); | |
| 4445 | + } | |
| 4446 | + if( fossil_strcmp(pSetting->def, zVal)==0 ) return 1; | |
| 4447 | + if( is_false(zVal) && is_false(pSetting->def) ) return 1; | |
| 4448 | + if( is_truth(zVal) && is_truth(pSetting->def) ) return 1; | |
| 4449 | + return 0; | |
| 4450 | +} | |
| 4375 | 4451 | |
| 4376 | 4452 | /* |
| 4377 | 4453 | ** Print the current value of a setting identified by the pSetting |
| 4378 | 4454 | ** pointer. |
| 4455 | +** | |
| 4456 | +** Only show the value, not the setting name, if valueOnly is true. | |
| 4457 | +** | |
| 4458 | +** Show nothing if bIfChng is true and the setting is not currently set | |
| 4459 | +** or is set to its default value. | |
| 4379 | 4460 | */ |
| 4380 | -void print_setting(const Setting *pSetting, int valueOnly){ | |
| 4461 | +void print_setting(const Setting *pSetting, int valueOnly, int bIfChng){ | |
| 4381 | 4462 | Stmt q; |
| 4382 | 4463 | int versioned = 0; |
| 4383 | 4464 | if( pSetting->versionable && g.localOpen ){ |
| 4384 | 4465 | /* Check to see if this is overridden by a versionable settings file */ |
| 4385 | 4466 | Blob versionedPathname; |
| @@ -4390,11 +4471,16 @@ | ||
| 4390 | 4471 | versioned = 1; |
| 4391 | 4472 | } |
| 4392 | 4473 | blob_reset(&versionedPathname); |
| 4393 | 4474 | } |
| 4394 | 4475 | if( valueOnly && versioned ){ |
| 4395 | - fossil_print("%s\n", db_get_versioned(pSetting->name, NULL)); | |
| 4476 | + const char *zVal = db_get_versioned(pSetting->name, NULL, NULL); | |
| 4477 | + if( !bIfChng || (zVal!=0 && fossil_strcmp(zVal, pSetting->def)!=0) ){ | |
| 4478 | + fossil_print("%s\n", db_get_versioned(pSetting->name, NULL, NULL)); | |
| 4479 | + }else{ | |
| 4480 | + versioned = 0; | |
| 4481 | + } | |
| 4396 | 4482 | return; |
| 4397 | 4483 | } |
| 4398 | 4484 | if( g.repositoryOpen ){ |
| 4399 | 4485 | db_prepare(&q, |
| 4400 | 4486 | "SELECT '(local)', value FROM config WHERE name=%Q" |
| @@ -4407,20 +4493,47 @@ | ||
| 4407 | 4493 | "SELECT '(global)', value FROM global_config WHERE name=%Q", |
| 4408 | 4494 | pSetting->name |
| 4409 | 4495 | ); |
| 4410 | 4496 | } |
| 4411 | 4497 | if( db_step(&q)==SQLITE_ROW ){ |
| 4412 | - if( valueOnly ){ | |
| 4498 | + const char *zVal = db_column_text(&q,1); | |
| 4499 | + if( bIfChng && setting_has_default_value(pSetting,zVal) ){ | |
| 4500 | + if( versioned ){ | |
| 4501 | + fossil_print("%-24s (versioned)\n", pSetting->name); | |
| 4502 | + versioned = 0; | |
| 4503 | + } | |
| 4504 | + }else if( valueOnly ){ | |
| 4413 | 4505 | fossil_print("%s\n", db_column_text(&q, 1)); |
| 4414 | 4506 | }else{ |
| 4415 | - fossil_print("%-20s %-8s %s\n", pSetting->name, db_column_text(&q, 0), | |
| 4416 | - db_column_text(&q, 1)); | |
| 4507 | + const char *zVal = (const char*)db_column_text(&q,1); | |
| 4508 | + const char *zName = (const char*)db_column_text(&q,0); | |
| 4509 | + if( zVal==0 ) zVal = "NULL"; | |
| 4510 | + if( strchr(zVal,'\n')==0 ){ | |
| 4511 | + fossil_print("%-24s %-11s %s\n", pSetting->name, zName, zVal); | |
| 4512 | + }else{ | |
| 4513 | + fossil_print("%-24s %-11s\n", pSetting->name, zName); | |
| 4514 | + while( zVal[0] ){ | |
| 4515 | + char *zNL = strchr(zVal, '\n'); | |
| 4516 | + if( zNL==0 ){ | |
| 4517 | + fossil_print(" %s\n", zVal); | |
| 4518 | + break; | |
| 4519 | + }else{ | |
| 4520 | + int n = (int)(zNL - zVal); | |
| 4521 | + while( n>0 && fossil_isspace(zVal[n-1]) ){ n--; } | |
| 4522 | + fossil_print(" %.*s\n", n, zVal); | |
| 4523 | + zVal = zNL+1; | |
| 4524 | + } | |
| 4525 | + } | |
| 4526 | + } | |
| 4417 | 4527 | } |
| 4528 | + }else if( bIfChng ){ | |
| 4529 | + /* Display nothing */ | |
| 4530 | + versioned = 0; | |
| 4418 | 4531 | }else if( valueOnly ){ |
| 4419 | 4532 | fossil_print("\n"); |
| 4420 | 4533 | }else{ |
| 4421 | - fossil_print("%-20s\n", pSetting->name); | |
| 4534 | + fossil_print("%-24s\n", pSetting->name); | |
| 4422 | 4535 | } |
| 4423 | 4536 | if( versioned ){ |
| 4424 | 4537 | fossil_print(" (overridden by contents of file .fossil-settings/%s)\n", |
| 4425 | 4538 | pSetting->name); |
| 4426 | 4539 | } |
| @@ -4453,21 +4566,22 @@ | ||
| 4453 | 4566 | char versionable; /* Is this setting versionable? */ |
| 4454 | 4567 | char forceTextArea; /* Force using a text area for display? */ |
| 4455 | 4568 | char sensitive; /* True if this a security-sensitive setting */ |
| 4456 | 4569 | const char *def; /* Default value */ |
| 4457 | 4570 | }; |
| 4571 | + | |
| 4458 | 4572 | #endif /* INTERFACE */ |
| 4459 | 4573 | |
| 4460 | 4574 | /* |
| 4461 | -** SETTING: access-log boolean default=off | |
| 4575 | +** SETTING: access-log boolean default=on | |
| 4462 | 4576 | ** |
| 4463 | 4577 | ** When the access-log setting is enabled, all login attempts (successful |
| 4464 | 4578 | ** and unsuccessful) on the web interface are recorded in the "access" table |
| 4465 | 4579 | ** of the repository. |
| 4466 | 4580 | */ |
| 4467 | 4581 | /* |
| 4468 | -** SETTING: admin-log boolean default=off | |
| 4582 | +** SETTING: admin-log boolean default=on | |
| 4469 | 4583 | ** |
| 4470 | 4584 | ** When the admin-log setting is enabled, configuration changes are recorded |
| 4471 | 4585 | ** in the "admin_log" table of the repository. |
| 4472 | 4586 | */ |
| 4473 | 4587 | /* |
| @@ -4640,30 +4754,27 @@ | ||
| 4640 | 4754 | ** When enabled, fossil will attempt to sign all commits |
| 4641 | 4755 | ** with gpg or ssh. When disabled, commits will be unsigned. |
| 4642 | 4756 | */ |
| 4643 | 4757 | /* |
| 4644 | 4758 | ** SETTING: comment-format width=16 default=1 |
| 4645 | -** Set the default options for printing timeline comments to the console. | |
| 4646 | -** | |
| 4647 | -** The global --comfmtflags command-line option (or alias --comment-format) | |
| 4648 | -** overrides this setting. | |
| 4759 | +** Set the algorithm for printing timeline comments to the console. | |
| 4649 | 4760 | ** |
| 4650 | 4761 | ** Possible values are: |
| 4651 | -** 1 Activate the legacy comment printing format (default). | |
| 4762 | +** 1 Use the original comment printing algorithm: | |
| 4763 | +** * Leading and trialing whitespace is removed | |
| 4764 | +** * Internal whitespace is converted into a single space (0x20) | |
| 4765 | +** * Line breaks occurs at whitespace or hyphens if possible | |
| 4766 | +** This is the recommended value and the default. | |
| 4652 | 4767 | ** |
| 4653 | 4768 | ** Or a bitwise combination of the following flags: |
| 4654 | -** 0 Activate the newer (non-legacy) comment printing format. | |
| 4655 | 4769 | ** 2 Trim leading and trailing CR and LF characters. |
| 4656 | 4770 | ** 4 Trim leading and trailing white space characters. |
| 4657 | 4771 | ** 8 Attempt to break lines on word boundaries. |
| 4658 | 4772 | ** 16 Break lines before the original comment embedded in other text. |
| 4659 | 4773 | ** |
| 4660 | -** Note: To preserve line breaks, activate the newer (non-legacy) comment | |
| 4661 | -** printing format (i.e. set to "0", or a combination not including "1"). | |
| 4662 | -** | |
| 4663 | -** Note: The options for timeline comments displayed on the web UI can be | |
| 4664 | -** configured through the /setup_timeline web page. | |
| 4774 | +** Note: To preserve line breaks and/or other whitespace within comment text, | |
| 4775 | +** make this setting some integer value that omits the "1" bit. | |
| 4665 | 4776 | */ |
| 4666 | 4777 | /* |
| 4667 | 4778 | ** SETTING: crlf-glob width=40 versionable block-text |
| 4668 | 4779 | ** The VALUE of this setting is a list of GLOB patterns matching files |
| 4669 | 4780 | ** in which it is allowed to have CR, CR+LF or mixed line endings, |
| @@ -4709,10 +4820,18 @@ | ||
| 4709 | 4820 | */ |
| 4710 | 4821 | /* |
| 4711 | 4822 | ** SETTING: editor width=32 sensitive |
| 4712 | 4823 | ** The value is an external command that will launch the |
| 4713 | 4824 | ** text editor command used for check-in comments. |
| 4825 | +** | |
| 4826 | +** If this value is not set, then environment variables VISUAL and | |
| 4827 | +** EDITOR are consulted, in that order. If neither of those are set, | |
| 4828 | +** then a search is made for common text editors, including | |
| 4829 | +** "notepad", "nano", "pico", "jove", "edit", "vi", "vim", and "ed". | |
| 4830 | +** | |
| 4831 | +** If this setting is false ("off", "no", "false", or "0") then no | |
| 4832 | +** text editor is used. | |
| 4714 | 4833 | */ |
| 4715 | 4834 | /* |
| 4716 | 4835 | ** SETTING: empty-dirs width=40 versionable block-text |
| 4717 | 4836 | ** The value is a list of pathnames parsed according to the same rules as |
| 4718 | 4837 | ** the *-glob settings. On update and checkout commands, if no directory |
| @@ -4759,13 +4878,14 @@ | ||
| 4759 | 4878 | ** send the "pragma avoid-delta-manifests" statement in its reply, |
| 4760 | 4879 | ** which will cause the client to avoid generating a delta |
| 4761 | 4880 | ** manifest. |
| 4762 | 4881 | */ |
| 4763 | 4882 | /* |
| 4764 | -** SETTING: gdiff-command width=40 default=gdiff sensitive | |
| 4883 | +** SETTING: gdiff-command width=40 sensitive | |
| 4765 | 4884 | ** The value is an external command to run when performing a graphical |
| 4766 | -** diff. If undefined, text diff will be used. | |
| 4885 | +** diff. If undefined, a --tk diff is done if commands "tclsh" and "wish" | |
| 4886 | +** are on PATH, or a --by diff is done if "tclsh" or "wish" are unavailable. | |
| 4767 | 4887 | */ |
| 4768 | 4888 | /* |
| 4769 | 4889 | ** SETTING: gmerge-command width=40 sensitive |
| 4770 | 4890 | ** The value is a graphical merge conflict resolver command operating |
| 4771 | 4891 | ** on four files. Examples: |
| @@ -4949,11 +5069,12 @@ | ||
| 4949 | 5069 | ** |
| 4950 | 5070 | ** All repositories are searched (in lexicographical order) and the first |
| 4951 | 5071 | ** repository with a non-zero "repolist-skin" value is used as the skin |
| 4952 | 5072 | ** for the repository list page. If none of the repositories on the list |
| 4953 | 5073 | ** have a non-zero "repolist-skin" setting then the repository list is |
| 4954 | -** displayed using unadorned HTML ("skinless"). | |
| 5074 | +** displayed using unadorned HTML ("skinless"), with the page title taken | |
| 5075 | +** from the FOSSIL_REPOLIST_TITLE environment variable. | |
| 4955 | 5076 | ** |
| 4956 | 5077 | ** If repolist-skin has a value of 2, then the repository is omitted from |
| 4957 | 5078 | ** the list in use cases 1 through 4, but not for 5 and 6. |
| 4958 | 5079 | */ |
| 4959 | 5080 | /* |
| @@ -5154,20 +5275,22 @@ | ||
| 5154 | 5275 | ** configuration database. If both a local and a global value exists for a |
| 5155 | 5276 | ** setting, the local value takes precedence. This command normally operates |
| 5156 | 5277 | ** on the local settings. Use the --global option to change global settings. |
| 5157 | 5278 | ** |
| 5158 | 5279 | ** Options: |
| 5280 | +** --changed Only show settings if the value differs from the default | |
| 5281 | +** --exact Only consider exact name matches | |
| 5159 | 5282 | ** --global Set or unset the given property globally instead of |
| 5160 | 5283 | ** setting or unsetting it for the open repository only |
| 5161 | -** --exact Only consider exact name matches | |
| 5162 | 5284 | ** --value Only show the value of a given property (implies --exact) |
| 5163 | 5285 | ** |
| 5164 | 5286 | ** See also: [[configuration]] |
| 5165 | 5287 | */ |
| 5166 | 5288 | void setting_cmd(void){ |
| 5167 | 5289 | int i; |
| 5168 | 5290 | int globalFlag = find_option("global","g",0)!=0; |
| 5291 | + int bIfChng = find_option("changed",0,0)!=0; | |
| 5169 | 5292 | int exactFlag = find_option("exact",0,0)!=0; |
| 5170 | 5293 | int valueFlag = find_option("value",0,0)!=0; |
| 5171 | 5294 | /* Undocumented "--test-for-subsystem SUBSYS" option used to test |
| 5172 | 5295 | ** the db_get_for_subsystem() interface: */ |
| 5173 | 5296 | const char *zSubsys = find_option("test-for-subsystem",0,1); |
| @@ -5188,16 +5311,15 @@ | ||
| 5188 | 5311 | } |
| 5189 | 5312 | if( valueFlag ){ |
| 5190 | 5313 | if( g.argc!=3 ){ |
| 5191 | 5314 | fossil_fatal("--value is only supported when qurying a given property"); |
| 5192 | 5315 | } |
| 5193 | - exactFlag = 1; | |
| 5194 | 5316 | } |
| 5195 | 5317 | |
| 5196 | 5318 | if( g.argc==2 ){ |
| 5197 | 5319 | for(i=0; i<nSetting; i++){ |
| 5198 | - print_setting(&aSetting[i], 0); | |
| 5320 | + print_setting(&aSetting[i], 0, bIfChng); | |
| 5199 | 5321 | } |
| 5200 | 5322 | }else if( g.argc==3 || g.argc==4 ){ |
| 5201 | 5323 | const char *zName = g.argv[2]; |
| 5202 | 5324 | int n = (int)strlen(zName); |
| 5203 | 5325 | const Setting *pSetting = db_find_setting(zName, !exactFlag); |
| @@ -5248,11 +5370,11 @@ | ||
| 5248 | 5370 | fossil_print(" [%s]", zValue); |
| 5249 | 5371 | fossil_free(zValue); |
| 5250 | 5372 | } |
| 5251 | 5373 | fossil_print("\n"); |
| 5252 | 5374 | }else{ |
| 5253 | - print_setting(pSetting, valueFlag); | |
| 5375 | + print_setting(pSetting, valueFlag, bIfChng); | |
| 5254 | 5376 | } |
| 5255 | 5377 | pSetting++; |
| 5256 | 5378 | } |
| 5257 | 5379 | } |
| 5258 | 5380 | }else{ |
| 5259 | 5381 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -1629,10 +1629,12 @@ | |
| 1629 | sqlite3_create_function(db, "chat_msg_from_event", 4, |
| 1630 | SQLITE_UTF8 | SQLITE_INNOCUOUS, 0, |
| 1631 | chat_msg_from_event, 0, 0); |
| 1632 | sqlite3_create_function(db, "inode", 1, SQLITE_UTF8, 0, |
| 1633 | file_inode_sql_func,0,0); |
| 1634 | |
| 1635 | } |
| 1636 | |
| 1637 | #if USE_SEE |
| 1638 | /* |
| @@ -3204,23 +3206,16 @@ | |
| 3204 | |
| 3205 | db_unprotect(PROTECT_ALL); |
| 3206 | db_set("content-schema", CONTENT_SCHEMA, 0); |
| 3207 | db_set("aux-schema", AUX_SCHEMA_MAX, 0); |
| 3208 | db_set("rebuilt", get_version(), 0); |
| 3209 | db_set("admin-log", "1", 0); |
| 3210 | db_set("access-log", "1", 0); |
| 3211 | db_multi_exec( |
| 3212 | "INSERT INTO config(name,value,mtime)" |
| 3213 | " VALUES('server-code', lower(hex(randomblob(20))),now());" |
| 3214 | "INSERT INTO config(name,value,mtime)" |
| 3215 | " VALUES('project-code', lower(hex(randomblob(20))),now());" |
| 3216 | ); |
| 3217 | if( !db_is_global("autosync") ) db_set_int("autosync", 1, 0); |
| 3218 | if( !db_is_global("localauth") ) db_set_int("localauth", 0, 0); |
| 3219 | if( !db_is_global("timeline-plaintext") ){ |
| 3220 | db_set_int("timeline-plaintext", 1, 0); |
| 3221 | } |
| 3222 | db_create_default_users(0, zDefaultUser); |
| 3223 | if( zDefaultUser ) g.zLogin = zDefaultUser; |
| 3224 | user_select(); |
| 3225 | |
| 3226 | if( zTemplate ){ |
| @@ -3647,66 +3642,79 @@ | |
| 3647 | ** |
| 3648 | ** If the zNonVersionedSetting parameter is not NULL then it holds the |
| 3649 | ** non-versioned value for this setting. If both a versioned and a |
| 3650 | ** non-versioned value exist and are not equal, then a warning message |
| 3651 | ** might be generated. |
| 3652 | */ |
| 3653 | char *db_get_versioned(const char *zName, char *zNonVersionedSetting){ |
| 3654 | char *zVersionedSetting = 0; |
| 3655 | int noWarn = 0; |
| 3656 | int found = 0; |
| 3657 | struct _cacheEntry { |
| 3658 | struct _cacheEntry *next; |
| 3659 | const char *zName, *zValue; |
| 3660 | } *cacheEntry = 0; |
| 3661 | static struct _cacheEntry *cache = 0; |
| 3662 | |
| 3663 | if( !g.localOpen && g.zOpenRevision==0 ) return zNonVersionedSetting; |
| 3664 | /* Look up name in cache */ |
| 3665 | cacheEntry = cache; |
| 3666 | while( cacheEntry!=0 ){ |
| 3667 | if( fossil_strcmp(cacheEntry->zName, zName)==0 ){ |
| 3668 | zVersionedSetting = fossil_strdup(cacheEntry->zValue); |
| 3669 | break; |
| 3670 | } |
| 3671 | cacheEntry = cacheEntry->next; |
| 3672 | } |
| 3673 | /* Attempt to read value from file in check-out if there wasn't a cache hit.*/ |
| 3674 | if( cacheEntry==0 ){ |
| 3675 | Blob versionedPathname; |
| 3676 | Blob setting; |
| 3677 | blob_zero(&versionedPathname); |
| 3678 | blob_zero(&setting); |
| 3679 | blob_appendf(&versionedPathname, "%s.fossil-settings/%s", |
| 3680 | g.zLocalRoot, zName); |
| 3681 | if( !g.localOpen ){ |
| 3682 | /* Repository is in the process of being opened, but files have not been |
| 3683 | * written to disk. Load from the database. */ |
| 3684 | Blob noWarnFile; |
| 3685 | if( historical_blob(g.zOpenRevision, blob_str(&versionedPathname), |
| 3686 | &setting, 0) ){ |
| 3687 | found = 1; |
| 3688 | } |
| 3689 | /* See if there's a no-warn flag */ |
| 3690 | blob_append(&versionedPathname, ".no-warn", -1); |
| 3691 | blob_zero(&noWarnFile); |
| 3692 | if( historical_blob(g.zOpenRevision, blob_str(&versionedPathname), |
| 3693 | &noWarnFile, 0) ){ |
| 3694 | noWarn = 1; |
| 3695 | } |
| 3696 | blob_reset(&noWarnFile); |
| 3697 | }else if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){ |
| 3698 | /* File exists, and contains the value for this setting. Load from |
| 3699 | ** the file. */ |
| 3700 | const char *zFile = blob_str(&versionedPathname); |
| 3701 | if( blob_read_from_file(&setting, zFile, ExtFILE)>=0 ){ |
| 3702 | found = 1; |
| 3703 | } |
| 3704 | /* See if there's a no-warn flag */ |
| 3705 | blob_append(&versionedPathname, ".no-warn", -1); |
| 3706 | if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){ |
| 3707 | noWarn = 1; |
| 3708 | } |
| 3709 | } |
| 3710 | blob_reset(&versionedPathname); |
| 3711 | if( found ){ |
| 3712 | blob_strip_comment_lines(&setting, &setting); |
| @@ -3713,20 +3721,27 @@ | |
| 3713 | blob_trim(&setting); /* Avoid non-obvious problems with line endings |
| 3714 | ** on boolean properties */ |
| 3715 | zVersionedSetting = fossil_strdup(blob_str(&setting)); |
| 3716 | } |
| 3717 | blob_reset(&setting); |
| 3718 | /* Store result in cache, which can be the value or 0 if not found */ |
| 3719 | cacheEntry = (struct _cacheEntry*)fossil_malloc(sizeof(struct _cacheEntry)); |
| 3720 | cacheEntry->next = cache; |
| 3721 | cacheEntry->zName = zName; |
| 3722 | cacheEntry->zValue = fossil_strdup(zVersionedSetting); |
| 3723 | cache = cacheEntry; |
| 3724 | } |
| 3725 | /* Display a warning? */ |
| 3726 | if( zVersionedSetting!=0 && zNonVersionedSetting!=0 |
| 3727 | && zNonVersionedSetting[0]!='\0' && !noWarn |
| 3728 | ){ |
| 3729 | /* There's a versioned setting, and a non-versioned setting. Tell |
| 3730 | ** the user about the conflict */ |
| 3731 | fossil_warning( |
| 3732 | "setting %s has both versioned and non-versioned values: using " |
| @@ -3735,10 +3750,11 @@ | |
| 3735 | "\"%/.fossil-settings/%s.no-warn\" in the check-out root, or delete " |
| 3736 | "the non-versioned setting with \"fossil unset %s\")", zName, |
| 3737 | g.zLocalRoot, zName, g.zLocalRoot, zName, zName |
| 3738 | ); |
| 3739 | } |
| 3740 | /* Prefer the versioned setting */ |
| 3741 | return ( zVersionedSetting!=0 ) ? zVersionedSetting : zNonVersionedSetting; |
| 3742 | } |
| 3743 | |
| 3744 | |
| @@ -3778,11 +3794,11 @@ | |
| 3778 | } |
| 3779 | if( pSetting!=0 && pSetting->versionable ){ |
| 3780 | /* This is a versionable setting, try and get the info from a |
| 3781 | ** checked-out file */ |
| 3782 | char * zZ = z; |
| 3783 | z = db_get_versioned(zName, z); |
| 3784 | if(zZ != z){ |
| 3785 | fossil_free(zZ); |
| 3786 | } |
| 3787 | } |
| 3788 | if( z==0 ){ |
| @@ -3919,11 +3935,11 @@ | |
| 3919 | } |
| 3920 | fossil_free(zVal); |
| 3921 | return dflt; |
| 3922 | } |
| 3923 | int db_get_versioned_boolean(const char *zName, int dflt){ |
| 3924 | char *zVal = db_get_versioned(zName, 0); |
| 3925 | if( zVal==0 ) return dflt; |
| 3926 | if( is_truth(zVal) ) return 1; |
| 3927 | if( is_false(zVal) ) return 0; |
| 3928 | return dflt; |
| 3929 | } |
| @@ -4048,14 +4064,30 @@ | |
| 4048 | ** Get the manifest setting. For backwards compatibility first check if the |
| 4049 | ** value is a boolean. If it's not a boolean, treat each character as a flag |
| 4050 | ** to enable a manifest type. This system puts certain boundary conditions on |
| 4051 | ** which letters can be used to represent flags (any permutation of flags must |
| 4052 | ** not be able to fully form one of the boolean values). |
| 4053 | */ |
| 4054 | int db_get_manifest_setting(void){ |
| 4055 | int flg; |
| 4056 | char *zVal = db_get("manifest", 0); |
| 4057 | if( zVal==0 || is_false(zVal) ){ |
| 4058 | return 0; |
| 4059 | }else if( is_truth(zVal) ){ |
| 4060 | return MFESTFLG_RAW|MFESTFLG_UUID; |
| 4061 | } |
| @@ -4068,10 +4100,37 @@ | |
| 4068 | } |
| 4069 | zVal++; |
| 4070 | } |
| 4071 | return flg; |
| 4072 | } |
| 4073 | |
| 4074 | |
| 4075 | /* |
| 4076 | ** Record the name of a local repository in the global_config() database. |
| 4077 | ** The repository filename %s is recorded as an entry with a "name" field |
| @@ -4181,10 +4240,11 @@ | |
| 4181 | ** --force-missing Force opening a repository with missing content |
| 4182 | ** -k|--keep Only modify the manifest file(s) |
| 4183 | ** --nested Allow opening a repository inside an opened check-out |
| 4184 | ** --nosync Do not auto-sync the repository prior to opening even |
| 4185 | ** if the autosync setting is on. |
| 4186 | ** --repodir DIR If REPOSITORY is a URI that will be cloned, store |
| 4187 | ** the clone in DIR rather than in "." |
| 4188 | ** --setmtime Set timestamps of all files to match their SCM-side |
| 4189 | ** times (the timestamp of the last check-in which modified |
| 4190 | ** them). |
| @@ -4370,16 +4430,37 @@ | |
| 4370 | } |
| 4371 | } |
| 4372 | g.argc = 2; |
| 4373 | info_cmd(); |
| 4374 | } |
| 4375 | |
| 4376 | /* |
| 4377 | ** Print the current value of a setting identified by the pSetting |
| 4378 | ** pointer. |
| 4379 | */ |
| 4380 | void print_setting(const Setting *pSetting, int valueOnly){ |
| 4381 | Stmt q; |
| 4382 | int versioned = 0; |
| 4383 | if( pSetting->versionable && g.localOpen ){ |
| 4384 | /* Check to see if this is overridden by a versionable settings file */ |
| 4385 | Blob versionedPathname; |
| @@ -4390,11 +4471,16 @@ | |
| 4390 | versioned = 1; |
| 4391 | } |
| 4392 | blob_reset(&versionedPathname); |
| 4393 | } |
| 4394 | if( valueOnly && versioned ){ |
| 4395 | fossil_print("%s\n", db_get_versioned(pSetting->name, NULL)); |
| 4396 | return; |
| 4397 | } |
| 4398 | if( g.repositoryOpen ){ |
| 4399 | db_prepare(&q, |
| 4400 | "SELECT '(local)', value FROM config WHERE name=%Q" |
| @@ -4407,20 +4493,47 @@ | |
| 4407 | "SELECT '(global)', value FROM global_config WHERE name=%Q", |
| 4408 | pSetting->name |
| 4409 | ); |
| 4410 | } |
| 4411 | if( db_step(&q)==SQLITE_ROW ){ |
| 4412 | if( valueOnly ){ |
| 4413 | fossil_print("%s\n", db_column_text(&q, 1)); |
| 4414 | }else{ |
| 4415 | fossil_print("%-20s %-8s %s\n", pSetting->name, db_column_text(&q, 0), |
| 4416 | db_column_text(&q, 1)); |
| 4417 | } |
| 4418 | }else if( valueOnly ){ |
| 4419 | fossil_print("\n"); |
| 4420 | }else{ |
| 4421 | fossil_print("%-20s\n", pSetting->name); |
| 4422 | } |
| 4423 | if( versioned ){ |
| 4424 | fossil_print(" (overridden by contents of file .fossil-settings/%s)\n", |
| 4425 | pSetting->name); |
| 4426 | } |
| @@ -4453,21 +4566,22 @@ | |
| 4453 | char versionable; /* Is this setting versionable? */ |
| 4454 | char forceTextArea; /* Force using a text area for display? */ |
| 4455 | char sensitive; /* True if this a security-sensitive setting */ |
| 4456 | const char *def; /* Default value */ |
| 4457 | }; |
| 4458 | #endif /* INTERFACE */ |
| 4459 | |
| 4460 | /* |
| 4461 | ** SETTING: access-log boolean default=off |
| 4462 | ** |
| 4463 | ** When the access-log setting is enabled, all login attempts (successful |
| 4464 | ** and unsuccessful) on the web interface are recorded in the "access" table |
| 4465 | ** of the repository. |
| 4466 | */ |
| 4467 | /* |
| 4468 | ** SETTING: admin-log boolean default=off |
| 4469 | ** |
| 4470 | ** When the admin-log setting is enabled, configuration changes are recorded |
| 4471 | ** in the "admin_log" table of the repository. |
| 4472 | */ |
| 4473 | /* |
| @@ -4640,30 +4754,27 @@ | |
| 4640 | ** When enabled, fossil will attempt to sign all commits |
| 4641 | ** with gpg or ssh. When disabled, commits will be unsigned. |
| 4642 | */ |
| 4643 | /* |
| 4644 | ** SETTING: comment-format width=16 default=1 |
| 4645 | ** Set the default options for printing timeline comments to the console. |
| 4646 | ** |
| 4647 | ** The global --comfmtflags command-line option (or alias --comment-format) |
| 4648 | ** overrides this setting. |
| 4649 | ** |
| 4650 | ** Possible values are: |
| 4651 | ** 1 Activate the legacy comment printing format (default). |
| 4652 | ** |
| 4653 | ** Or a bitwise combination of the following flags: |
| 4654 | ** 0 Activate the newer (non-legacy) comment printing format. |
| 4655 | ** 2 Trim leading and trailing CR and LF characters. |
| 4656 | ** 4 Trim leading and trailing white space characters. |
| 4657 | ** 8 Attempt to break lines on word boundaries. |
| 4658 | ** 16 Break lines before the original comment embedded in other text. |
| 4659 | ** |
| 4660 | ** Note: To preserve line breaks, activate the newer (non-legacy) comment |
| 4661 | ** printing format (i.e. set to "0", or a combination not including "1"). |
| 4662 | ** |
| 4663 | ** Note: The options for timeline comments displayed on the web UI can be |
| 4664 | ** configured through the /setup_timeline web page. |
| 4665 | */ |
| 4666 | /* |
| 4667 | ** SETTING: crlf-glob width=40 versionable block-text |
| 4668 | ** The VALUE of this setting is a list of GLOB patterns matching files |
| 4669 | ** in which it is allowed to have CR, CR+LF or mixed line endings, |
| @@ -4709,10 +4820,18 @@ | |
| 4709 | */ |
| 4710 | /* |
| 4711 | ** SETTING: editor width=32 sensitive |
| 4712 | ** The value is an external command that will launch the |
| 4713 | ** text editor command used for check-in comments. |
| 4714 | */ |
| 4715 | /* |
| 4716 | ** SETTING: empty-dirs width=40 versionable block-text |
| 4717 | ** The value is a list of pathnames parsed according to the same rules as |
| 4718 | ** the *-glob settings. On update and checkout commands, if no directory |
| @@ -4759,13 +4878,14 @@ | |
| 4759 | ** send the "pragma avoid-delta-manifests" statement in its reply, |
| 4760 | ** which will cause the client to avoid generating a delta |
| 4761 | ** manifest. |
| 4762 | */ |
| 4763 | /* |
| 4764 | ** SETTING: gdiff-command width=40 default=gdiff sensitive |
| 4765 | ** The value is an external command to run when performing a graphical |
| 4766 | ** diff. If undefined, text diff will be used. |
| 4767 | */ |
| 4768 | /* |
| 4769 | ** SETTING: gmerge-command width=40 sensitive |
| 4770 | ** The value is a graphical merge conflict resolver command operating |
| 4771 | ** on four files. Examples: |
| @@ -4949,11 +5069,12 @@ | |
| 4949 | ** |
| 4950 | ** All repositories are searched (in lexicographical order) and the first |
| 4951 | ** repository with a non-zero "repolist-skin" value is used as the skin |
| 4952 | ** for the repository list page. If none of the repositories on the list |
| 4953 | ** have a non-zero "repolist-skin" setting then the repository list is |
| 4954 | ** displayed using unadorned HTML ("skinless"). |
| 4955 | ** |
| 4956 | ** If repolist-skin has a value of 2, then the repository is omitted from |
| 4957 | ** the list in use cases 1 through 4, but not for 5 and 6. |
| 4958 | */ |
| 4959 | /* |
| @@ -5154,20 +5275,22 @@ | |
| 5154 | ** configuration database. If both a local and a global value exists for a |
| 5155 | ** setting, the local value takes precedence. This command normally operates |
| 5156 | ** on the local settings. Use the --global option to change global settings. |
| 5157 | ** |
| 5158 | ** Options: |
| 5159 | ** --global Set or unset the given property globally instead of |
| 5160 | ** setting or unsetting it for the open repository only |
| 5161 | ** --exact Only consider exact name matches |
| 5162 | ** --value Only show the value of a given property (implies --exact) |
| 5163 | ** |
| 5164 | ** See also: [[configuration]] |
| 5165 | */ |
| 5166 | void setting_cmd(void){ |
| 5167 | int i; |
| 5168 | int globalFlag = find_option("global","g",0)!=0; |
| 5169 | int exactFlag = find_option("exact",0,0)!=0; |
| 5170 | int valueFlag = find_option("value",0,0)!=0; |
| 5171 | /* Undocumented "--test-for-subsystem SUBSYS" option used to test |
| 5172 | ** the db_get_for_subsystem() interface: */ |
| 5173 | const char *zSubsys = find_option("test-for-subsystem",0,1); |
| @@ -5188,16 +5311,15 @@ | |
| 5188 | } |
| 5189 | if( valueFlag ){ |
| 5190 | if( g.argc!=3 ){ |
| 5191 | fossil_fatal("--value is only supported when qurying a given property"); |
| 5192 | } |
| 5193 | exactFlag = 1; |
| 5194 | } |
| 5195 | |
| 5196 | if( g.argc==2 ){ |
| 5197 | for(i=0; i<nSetting; i++){ |
| 5198 | print_setting(&aSetting[i], 0); |
| 5199 | } |
| 5200 | }else if( g.argc==3 || g.argc==4 ){ |
| 5201 | const char *zName = g.argv[2]; |
| 5202 | int n = (int)strlen(zName); |
| 5203 | const Setting *pSetting = db_find_setting(zName, !exactFlag); |
| @@ -5248,11 +5370,11 @@ | |
| 5248 | fossil_print(" [%s]", zValue); |
| 5249 | fossil_free(zValue); |
| 5250 | } |
| 5251 | fossil_print("\n"); |
| 5252 | }else{ |
| 5253 | print_setting(pSetting, valueFlag); |
| 5254 | } |
| 5255 | pSetting++; |
| 5256 | } |
| 5257 | } |
| 5258 | }else{ |
| 5259 |
| --- src/db.c | |
| +++ src/db.c | |
| @@ -1629,10 +1629,12 @@ | |
| 1629 | sqlite3_create_function(db, "chat_msg_from_event", 4, |
| 1630 | SQLITE_UTF8 | SQLITE_INNOCUOUS, 0, |
| 1631 | chat_msg_from_event, 0, 0); |
| 1632 | sqlite3_create_function(db, "inode", 1, SQLITE_UTF8, 0, |
| 1633 | file_inode_sql_func,0,0); |
| 1634 | sqlite3_create_function(db, "artifact_to_json", 1, SQLITE_UTF8, 0, |
| 1635 | artifact_to_json_sql_func,0,0); |
| 1636 | |
| 1637 | } |
| 1638 | |
| 1639 | #if USE_SEE |
| 1640 | /* |
| @@ -3204,23 +3206,16 @@ | |
| 3206 | |
| 3207 | db_unprotect(PROTECT_ALL); |
| 3208 | db_set("content-schema", CONTENT_SCHEMA, 0); |
| 3209 | db_set("aux-schema", AUX_SCHEMA_MAX, 0); |
| 3210 | db_set("rebuilt", get_version(), 0); |
| 3211 | db_multi_exec( |
| 3212 | "INSERT INTO config(name,value,mtime)" |
| 3213 | " VALUES('server-code', lower(hex(randomblob(20))),now());" |
| 3214 | "INSERT INTO config(name,value,mtime)" |
| 3215 | " VALUES('project-code', lower(hex(randomblob(20))),now());" |
| 3216 | ); |
| 3217 | db_create_default_users(0, zDefaultUser); |
| 3218 | if( zDefaultUser ) g.zLogin = zDefaultUser; |
| 3219 | user_select(); |
| 3220 | |
| 3221 | if( zTemplate ){ |
| @@ -3647,66 +3642,79 @@ | |
| 3642 | ** |
| 3643 | ** If the zNonVersionedSetting parameter is not NULL then it holds the |
| 3644 | ** non-versioned value for this setting. If both a versioned and a |
| 3645 | ** non-versioned value exist and are not equal, then a warning message |
| 3646 | ** might be generated. |
| 3647 | ** |
| 3648 | ** zCkin is normally NULL. In that case, the versioned setting is |
| 3649 | ** take from the local check-out, if a local checkout exists, or from |
| 3650 | ** checkin named by the g.zOpenRevision global variable. If zCkin is |
| 3651 | ** not NULL, then zCkin is the name of the specific checkin from which |
| 3652 | ** versioned setting value is taken. When zCkin is not NULL, the cache |
| 3653 | ** is bypassed. |
| 3654 | */ |
| 3655 | char *db_get_versioned( |
| 3656 | const char *zName, |
| 3657 | char *zNonVersionedSetting, |
| 3658 | const char *zCkin |
| 3659 | ){ |
| 3660 | char *zVersionedSetting = 0; |
| 3661 | int noWarn = 0; |
| 3662 | int found = 0; |
| 3663 | struct _cacheEntry { |
| 3664 | struct _cacheEntry *next; |
| 3665 | const char *zName, *zValue; |
| 3666 | } *cacheEntry = 0; |
| 3667 | static struct _cacheEntry *cache = 0; |
| 3668 | |
| 3669 | if( !g.localOpen && g.zOpenRevision==0 && zCkin==0 ){ |
| 3670 | return zNonVersionedSetting; |
| 3671 | } |
| 3672 | |
| 3673 | /* Look up name in cache */ |
| 3674 | if( zCkin==0 ){ |
| 3675 | cacheEntry = cache; |
| 3676 | while( cacheEntry!=0 ){ |
| 3677 | if( fossil_strcmp(cacheEntry->zName, zName)==0 ){ |
| 3678 | zVersionedSetting = fossil_strdup(cacheEntry->zValue); |
| 3679 | break; |
| 3680 | } |
| 3681 | cacheEntry = cacheEntry->next; |
| 3682 | } |
| 3683 | } |
| 3684 | |
| 3685 | /* Attempt to read value from file in check-out if there wasn't a cache hit.*/ |
| 3686 | if( cacheEntry==0 ){ |
| 3687 | Blob versionedPathname; |
| 3688 | Blob setting; |
| 3689 | blob_init(&versionedPathname, 0, 0); |
| 3690 | blob_init(&setting, 0, 0); |
| 3691 | if( !g.localOpen || zCkin!=0 ){ |
| 3692 | /* Repository is in the process of being opened, but files have not been |
| 3693 | * written to disk. Load from the database. */ |
| 3694 | blob_appendf(&versionedPathname, ".fossil-settings/%s", zName); |
| 3695 | if( historical_blob(zCkin ? zCkin : g.zOpenRevision, |
| 3696 | blob_str(&versionedPathname), |
| 3697 | &setting, 0) |
| 3698 | ){ |
| 3699 | found = 1; |
| 3700 | } |
| 3701 | }else{ |
| 3702 | blob_appendf(&versionedPathname, "%s.fossil-settings/%s", |
| 3703 | g.zLocalRoot, zName); |
| 3704 | if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){ |
| 3705 | /* File exists, and contains the value for this setting. Load from |
| 3706 | ** the file. */ |
| 3707 | const char *zFile = blob_str(&versionedPathname); |
| 3708 | if( blob_read_from_file(&setting, zFile, ExtFILE)>=0 ){ |
| 3709 | found = 1; |
| 3710 | } |
| 3711 | /* See if there's a no-warn flag */ |
| 3712 | blob_append(&versionedPathname, ".no-warn", -1); |
| 3713 | if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){ |
| 3714 | noWarn = 1; |
| 3715 | } |
| 3716 | } |
| 3717 | } |
| 3718 | blob_reset(&versionedPathname); |
| 3719 | if( found ){ |
| 3720 | blob_strip_comment_lines(&setting, &setting); |
| @@ -3713,20 +3721,27 @@ | |
| 3721 | blob_trim(&setting); /* Avoid non-obvious problems with line endings |
| 3722 | ** on boolean properties */ |
| 3723 | zVersionedSetting = fossil_strdup(blob_str(&setting)); |
| 3724 | } |
| 3725 | blob_reset(&setting); |
| 3726 | |
| 3727 | /* Store result in cache, which can be the value or 0 if not found */ |
| 3728 | if( zCkin==0 ){ |
| 3729 | cacheEntry = (struct _cacheEntry*)fossil_malloc(sizeof(*cacheEntry)); |
| 3730 | cacheEntry->next = cache; |
| 3731 | cacheEntry->zName = zName; |
| 3732 | cacheEntry->zValue = fossil_strdup(zVersionedSetting); |
| 3733 | cache = cacheEntry; |
| 3734 | } |
| 3735 | } |
| 3736 | |
| 3737 | /* Display a warning? */ |
| 3738 | if( zVersionedSetting!=0 |
| 3739 | && zNonVersionedSetting!=0 |
| 3740 | && zNonVersionedSetting[0]!='\0' |
| 3741 | && zCkin==0 |
| 3742 | && !noWarn |
| 3743 | ){ |
| 3744 | /* There's a versioned setting, and a non-versioned setting. Tell |
| 3745 | ** the user about the conflict */ |
| 3746 | fossil_warning( |
| 3747 | "setting %s has both versioned and non-versioned values: using " |
| @@ -3735,10 +3750,11 @@ | |
| 3750 | "\"%/.fossil-settings/%s.no-warn\" in the check-out root, or delete " |
| 3751 | "the non-versioned setting with \"fossil unset %s\")", zName, |
| 3752 | g.zLocalRoot, zName, g.zLocalRoot, zName, zName |
| 3753 | ); |
| 3754 | } |
| 3755 | |
| 3756 | /* Prefer the versioned setting */ |
| 3757 | return ( zVersionedSetting!=0 ) ? zVersionedSetting : zNonVersionedSetting; |
| 3758 | } |
| 3759 | |
| 3760 | |
| @@ -3778,11 +3794,11 @@ | |
| 3794 | } |
| 3795 | if( pSetting!=0 && pSetting->versionable ){ |
| 3796 | /* This is a versionable setting, try and get the info from a |
| 3797 | ** checked-out file */ |
| 3798 | char * zZ = z; |
| 3799 | z = db_get_versioned(zName, z, 0); |
| 3800 | if(zZ != z){ |
| 3801 | fossil_free(zZ); |
| 3802 | } |
| 3803 | } |
| 3804 | if( z==0 ){ |
| @@ -3919,11 +3935,11 @@ | |
| 3935 | } |
| 3936 | fossil_free(zVal); |
| 3937 | return dflt; |
| 3938 | } |
| 3939 | int db_get_versioned_boolean(const char *zName, int dflt){ |
| 3940 | char *zVal = db_get_versioned(zName, 0, 0); |
| 3941 | if( zVal==0 ) return dflt; |
| 3942 | if( is_truth(zVal) ) return 1; |
| 3943 | if( is_false(zVal) ) return 0; |
| 3944 | return dflt; |
| 3945 | } |
| @@ -4048,14 +4064,30 @@ | |
| 4064 | ** Get the manifest setting. For backwards compatibility first check if the |
| 4065 | ** value is a boolean. If it's not a boolean, treat each character as a flag |
| 4066 | ** to enable a manifest type. This system puts certain boundary conditions on |
| 4067 | ** which letters can be used to represent flags (any permutation of flags must |
| 4068 | ** not be able to fully form one of the boolean values). |
| 4069 | ** |
| 4070 | ** "manifest" is a versionable setting. But we do not issue a warning |
| 4071 | ** if there is a conflict. Instead, the value returned is the value for |
| 4072 | ** the versioned setting if the versioned setting exists, or the ordinary |
| 4073 | ** setting otherwise. |
| 4074 | ** |
| 4075 | ** The argument zCkin is the specific check-in for which we want the |
| 4076 | ** manifest setting. |
| 4077 | */ |
| 4078 | int db_get_manifest_setting(const char *zCkin){ |
| 4079 | int flg; |
| 4080 | char *zVal; |
| 4081 | |
| 4082 | /* Look for the versioned setting first */ |
| 4083 | zVal = db_get_versioned("manifest", 0, zCkin); |
| 4084 | |
| 4085 | if( zVal==0 && g.repositoryOpen ){ |
| 4086 | /* No versioned setting, look for the repository setting second */ |
| 4087 | zVal = db_text(0, "SELECT value FROM config WHERE name='manifest'"); |
| 4088 | } |
| 4089 | if( zVal==0 || is_false(zVal) ){ |
| 4090 | return 0; |
| 4091 | }else if( is_truth(zVal) ){ |
| 4092 | return MFESTFLG_RAW|MFESTFLG_UUID; |
| 4093 | } |
| @@ -4068,10 +4100,37 @@ | |
| 4100 | } |
| 4101 | zVal++; |
| 4102 | } |
| 4103 | return flg; |
| 4104 | } |
| 4105 | |
| 4106 | /* |
| 4107 | ** COMMAND: test-manifest-setting |
| 4108 | ** |
| 4109 | ** Usage: %fossil test-manifest-setting VERSION VERSION ... |
| 4110 | ** |
| 4111 | ** Display the value for the "manifest" setting for various versions |
| 4112 | ** of the repository. |
| 4113 | */ |
| 4114 | void test_manfest_setting_cmd(void){ |
| 4115 | int i; |
| 4116 | db_find_and_open_repository(0, 0); |
| 4117 | for(i=2; i<g.argc; i++){ |
| 4118 | int m = db_get_manifest_setting(g.argv[i]); |
| 4119 | fossil_print("%s:\n", g.argv[i]); |
| 4120 | fossil_print(" flags = 0x%02x\n", m); |
| 4121 | if( m & MFESTFLG_RAW ){ |
| 4122 | fossil_print(" manifest\n"); |
| 4123 | } |
| 4124 | if( m & MFESTFLG_UUID ){ |
| 4125 | fossil_print(" manifest.uuid\n"); |
| 4126 | } |
| 4127 | if( m & MFESTFLG_TAGS ){ |
| 4128 | fossil_print(" manifest.tags\n"); |
| 4129 | } |
| 4130 | } |
| 4131 | } |
| 4132 | |
| 4133 | |
| 4134 | /* |
| 4135 | ** Record the name of a local repository in the global_config() database. |
| 4136 | ** The repository filename %s is recorded as an entry with a "name" field |
| @@ -4181,10 +4240,11 @@ | |
| 4240 | ** --force-missing Force opening a repository with missing content |
| 4241 | ** -k|--keep Only modify the manifest file(s) |
| 4242 | ** --nested Allow opening a repository inside an opened check-out |
| 4243 | ** --nosync Do not auto-sync the repository prior to opening even |
| 4244 | ** if the autosync setting is on. |
| 4245 | ** --proxy PROXY Use PROXY as http proxy during sync operation |
| 4246 | ** --repodir DIR If REPOSITORY is a URI that will be cloned, store |
| 4247 | ** the clone in DIR rather than in "." |
| 4248 | ** --setmtime Set timestamps of all files to match their SCM-side |
| 4249 | ** times (the timestamp of the last check-in which modified |
| 4250 | ** them). |
| @@ -4370,16 +4430,37 @@ | |
| 4430 | } |
| 4431 | } |
| 4432 | g.argc = 2; |
| 4433 | info_cmd(); |
| 4434 | } |
| 4435 | |
| 4436 | /* |
| 4437 | ** Return true if pSetting has its default value assuming its |
| 4438 | ** current value is zVal. |
| 4439 | */ |
| 4440 | int setting_has_default_value(const Setting *pSetting, const char *zVal){ |
| 4441 | if( zVal==0 ) return 1; |
| 4442 | if( pSetting->def==0 ) return 0; |
| 4443 | if( pSetting->width==0 ){ |
| 4444 | return is_false(pSetting->def)==is_false(zVal); |
| 4445 | } |
| 4446 | if( fossil_strcmp(pSetting->def, zVal)==0 ) return 1; |
| 4447 | if( is_false(zVal) && is_false(pSetting->def) ) return 1; |
| 4448 | if( is_truth(zVal) && is_truth(pSetting->def) ) return 1; |
| 4449 | return 0; |
| 4450 | } |
| 4451 | |
| 4452 | /* |
| 4453 | ** Print the current value of a setting identified by the pSetting |
| 4454 | ** pointer. |
| 4455 | ** |
| 4456 | ** Only show the value, not the setting name, if valueOnly is true. |
| 4457 | ** |
| 4458 | ** Show nothing if bIfChng is true and the setting is not currently set |
| 4459 | ** or is set to its default value. |
| 4460 | */ |
| 4461 | void print_setting(const Setting *pSetting, int valueOnly, int bIfChng){ |
| 4462 | Stmt q; |
| 4463 | int versioned = 0; |
| 4464 | if( pSetting->versionable && g.localOpen ){ |
| 4465 | /* Check to see if this is overridden by a versionable settings file */ |
| 4466 | Blob versionedPathname; |
| @@ -4390,11 +4471,16 @@ | |
| 4471 | versioned = 1; |
| 4472 | } |
| 4473 | blob_reset(&versionedPathname); |
| 4474 | } |
| 4475 | if( valueOnly && versioned ){ |
| 4476 | const char *zVal = db_get_versioned(pSetting->name, NULL, NULL); |
| 4477 | if( !bIfChng || (zVal!=0 && fossil_strcmp(zVal, pSetting->def)!=0) ){ |
| 4478 | fossil_print("%s\n", db_get_versioned(pSetting->name, NULL, NULL)); |
| 4479 | }else{ |
| 4480 | versioned = 0; |
| 4481 | } |
| 4482 | return; |
| 4483 | } |
| 4484 | if( g.repositoryOpen ){ |
| 4485 | db_prepare(&q, |
| 4486 | "SELECT '(local)', value FROM config WHERE name=%Q" |
| @@ -4407,20 +4493,47 @@ | |
| 4493 | "SELECT '(global)', value FROM global_config WHERE name=%Q", |
| 4494 | pSetting->name |
| 4495 | ); |
| 4496 | } |
| 4497 | if( db_step(&q)==SQLITE_ROW ){ |
| 4498 | const char *zVal = db_column_text(&q,1); |
| 4499 | if( bIfChng && setting_has_default_value(pSetting,zVal) ){ |
| 4500 | if( versioned ){ |
| 4501 | fossil_print("%-24s (versioned)\n", pSetting->name); |
| 4502 | versioned = 0; |
| 4503 | } |
| 4504 | }else if( valueOnly ){ |
| 4505 | fossil_print("%s\n", db_column_text(&q, 1)); |
| 4506 | }else{ |
| 4507 | const char *zVal = (const char*)db_column_text(&q,1); |
| 4508 | const char *zName = (const char*)db_column_text(&q,0); |
| 4509 | if( zVal==0 ) zVal = "NULL"; |
| 4510 | if( strchr(zVal,'\n')==0 ){ |
| 4511 | fossil_print("%-24s %-11s %s\n", pSetting->name, zName, zVal); |
| 4512 | }else{ |
| 4513 | fossil_print("%-24s %-11s\n", pSetting->name, zName); |
| 4514 | while( zVal[0] ){ |
| 4515 | char *zNL = strchr(zVal, '\n'); |
| 4516 | if( zNL==0 ){ |
| 4517 | fossil_print(" %s\n", zVal); |
| 4518 | break; |
| 4519 | }else{ |
| 4520 | int n = (int)(zNL - zVal); |
| 4521 | while( n>0 && fossil_isspace(zVal[n-1]) ){ n--; } |
| 4522 | fossil_print(" %.*s\n", n, zVal); |
| 4523 | zVal = zNL+1; |
| 4524 | } |
| 4525 | } |
| 4526 | } |
| 4527 | } |
| 4528 | }else if( bIfChng ){ |
| 4529 | /* Display nothing */ |
| 4530 | versioned = 0; |
| 4531 | }else if( valueOnly ){ |
| 4532 | fossil_print("\n"); |
| 4533 | }else{ |
| 4534 | fossil_print("%-24s\n", pSetting->name); |
| 4535 | } |
| 4536 | if( versioned ){ |
| 4537 | fossil_print(" (overridden by contents of file .fossil-settings/%s)\n", |
| 4538 | pSetting->name); |
| 4539 | } |
| @@ -4453,21 +4566,22 @@ | |
| 4566 | char versionable; /* Is this setting versionable? */ |
| 4567 | char forceTextArea; /* Force using a text area for display? */ |
| 4568 | char sensitive; /* True if this a security-sensitive setting */ |
| 4569 | const char *def; /* Default value */ |
| 4570 | }; |
| 4571 | |
| 4572 | #endif /* INTERFACE */ |
| 4573 | |
| 4574 | /* |
| 4575 | ** SETTING: access-log boolean default=on |
| 4576 | ** |
| 4577 | ** When the access-log setting is enabled, all login attempts (successful |
| 4578 | ** and unsuccessful) on the web interface are recorded in the "access" table |
| 4579 | ** of the repository. |
| 4580 | */ |
| 4581 | /* |
| 4582 | ** SETTING: admin-log boolean default=on |
| 4583 | ** |
| 4584 | ** When the admin-log setting is enabled, configuration changes are recorded |
| 4585 | ** in the "admin_log" table of the repository. |
| 4586 | */ |
| 4587 | /* |
| @@ -4640,30 +4754,27 @@ | |
| 4754 | ** When enabled, fossil will attempt to sign all commits |
| 4755 | ** with gpg or ssh. When disabled, commits will be unsigned. |
| 4756 | */ |
| 4757 | /* |
| 4758 | ** SETTING: comment-format width=16 default=1 |
| 4759 | ** Set the algorithm for printing timeline comments to the console. |
| 4760 | ** |
| 4761 | ** Possible values are: |
| 4762 | ** 1 Use the original comment printing algorithm: |
| 4763 | ** * Leading and trialing whitespace is removed |
| 4764 | ** * Internal whitespace is converted into a single space (0x20) |
| 4765 | ** * Line breaks occurs at whitespace or hyphens if possible |
| 4766 | ** This is the recommended value and the default. |
| 4767 | ** |
| 4768 | ** Or a bitwise combination of the following flags: |
| 4769 | ** 2 Trim leading and trailing CR and LF characters. |
| 4770 | ** 4 Trim leading and trailing white space characters. |
| 4771 | ** 8 Attempt to break lines on word boundaries. |
| 4772 | ** 16 Break lines before the original comment embedded in other text. |
| 4773 | ** |
| 4774 | ** Note: To preserve line breaks and/or other whitespace within comment text, |
| 4775 | ** make this setting some integer value that omits the "1" bit. |
| 4776 | */ |
| 4777 | /* |
| 4778 | ** SETTING: crlf-glob width=40 versionable block-text |
| 4779 | ** The VALUE of this setting is a list of GLOB patterns matching files |
| 4780 | ** in which it is allowed to have CR, CR+LF or mixed line endings, |
| @@ -4709,10 +4820,18 @@ | |
| 4820 | */ |
| 4821 | /* |
| 4822 | ** SETTING: editor width=32 sensitive |
| 4823 | ** The value is an external command that will launch the |
| 4824 | ** text editor command used for check-in comments. |
| 4825 | ** |
| 4826 | ** If this value is not set, then environment variables VISUAL and |
| 4827 | ** EDITOR are consulted, in that order. If neither of those are set, |
| 4828 | ** then a search is made for common text editors, including |
| 4829 | ** "notepad", "nano", "pico", "jove", "edit", "vi", "vim", and "ed". |
| 4830 | ** |
| 4831 | ** If this setting is false ("off", "no", "false", or "0") then no |
| 4832 | ** text editor is used. |
| 4833 | */ |
| 4834 | /* |
| 4835 | ** SETTING: empty-dirs width=40 versionable block-text |
| 4836 | ** The value is a list of pathnames parsed according to the same rules as |
| 4837 | ** the *-glob settings. On update and checkout commands, if no directory |
| @@ -4759,13 +4878,14 @@ | |
| 4878 | ** send the "pragma avoid-delta-manifests" statement in its reply, |
| 4879 | ** which will cause the client to avoid generating a delta |
| 4880 | ** manifest. |
| 4881 | */ |
| 4882 | /* |
| 4883 | ** SETTING: gdiff-command width=40 sensitive |
| 4884 | ** The value is an external command to run when performing a graphical |
| 4885 | ** diff. If undefined, a --tk diff is done if commands "tclsh" and "wish" |
| 4886 | ** are on PATH, or a --by diff is done if "tclsh" or "wish" are unavailable. |
| 4887 | */ |
| 4888 | /* |
| 4889 | ** SETTING: gmerge-command width=40 sensitive |
| 4890 | ** The value is a graphical merge conflict resolver command operating |
| 4891 | ** on four files. Examples: |
| @@ -4949,11 +5069,12 @@ | |
| 5069 | ** |
| 5070 | ** All repositories are searched (in lexicographical order) and the first |
| 5071 | ** repository with a non-zero "repolist-skin" value is used as the skin |
| 5072 | ** for the repository list page. If none of the repositories on the list |
| 5073 | ** have a non-zero "repolist-skin" setting then the repository list is |
| 5074 | ** displayed using unadorned HTML ("skinless"), with the page title taken |
| 5075 | ** from the FOSSIL_REPOLIST_TITLE environment variable. |
| 5076 | ** |
| 5077 | ** If repolist-skin has a value of 2, then the repository is omitted from |
| 5078 | ** the list in use cases 1 through 4, but not for 5 and 6. |
| 5079 | */ |
| 5080 | /* |
| @@ -5154,20 +5275,22 @@ | |
| 5275 | ** configuration database. If both a local and a global value exists for a |
| 5276 | ** setting, the local value takes precedence. This command normally operates |
| 5277 | ** on the local settings. Use the --global option to change global settings. |
| 5278 | ** |
| 5279 | ** Options: |
| 5280 | ** --changed Only show settings if the value differs from the default |
| 5281 | ** --exact Only consider exact name matches |
| 5282 | ** --global Set or unset the given property globally instead of |
| 5283 | ** setting or unsetting it for the open repository only |
| 5284 | ** --value Only show the value of a given property (implies --exact) |
| 5285 | ** |
| 5286 | ** See also: [[configuration]] |
| 5287 | */ |
| 5288 | void setting_cmd(void){ |
| 5289 | int i; |
| 5290 | int globalFlag = find_option("global","g",0)!=0; |
| 5291 | int bIfChng = find_option("changed",0,0)!=0; |
| 5292 | int exactFlag = find_option("exact",0,0)!=0; |
| 5293 | int valueFlag = find_option("value",0,0)!=0; |
| 5294 | /* Undocumented "--test-for-subsystem SUBSYS" option used to test |
| 5295 | ** the db_get_for_subsystem() interface: */ |
| 5296 | const char *zSubsys = find_option("test-for-subsystem",0,1); |
| @@ -5188,16 +5311,15 @@ | |
| 5311 | } |
| 5312 | if( valueFlag ){ |
| 5313 | if( g.argc!=3 ){ |
| 5314 | fossil_fatal("--value is only supported when qurying a given property"); |
| 5315 | } |
| 5316 | } |
| 5317 | |
| 5318 | if( g.argc==2 ){ |
| 5319 | for(i=0; i<nSetting; i++){ |
| 5320 | print_setting(&aSetting[i], 0, bIfChng); |
| 5321 | } |
| 5322 | }else if( g.argc==3 || g.argc==4 ){ |
| 5323 | const char *zName = g.argv[2]; |
| 5324 | int n = (int)strlen(zName); |
| 5325 | const Setting *pSetting = db_find_setting(zName, !exactFlag); |
| @@ -5248,11 +5370,11 @@ | |
| 5370 | fossil_print(" [%s]", zValue); |
| 5371 | fossil_free(zValue); |
| 5372 | } |
| 5373 | fossil_print("\n"); |
| 5374 | }else{ |
| 5375 | print_setting(pSetting, valueFlag, bIfChng); |
| 5376 | } |
| 5377 | pSetting++; |
| 5378 | } |
| 5379 | } |
| 5380 | }else{ |
| 5381 |
+1
| --- src/default.css | ||
| +++ src/default.css | ||
| @@ -750,10 +750,11 @@ | ||
| 750 | 750 | body.tkt div.content ol.tkt-changes > li:target > ol { |
| 751 | 751 | border-left: 1px solid gold; |
| 752 | 752 | } |
| 753 | 753 | body.cpage-ckout .file-change-line, |
| 754 | 754 | body.cpage-info .file-change-line, |
| 755 | +body.cpage-vinfo .file-change-line, | |
| 755 | 756 | body.cpage-vdiff .file-change-line { |
| 756 | 757 | margin-top: 16px; |
| 757 | 758 | margin-bottom: 16px; |
| 758 | 759 | margin-right: 1em /* keep it from nudging right up against the scrollbar-reveal zone */; |
| 759 | 760 | display: flex; |
| 760 | 761 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -750,10 +750,11 @@ | |
| 750 | body.tkt div.content ol.tkt-changes > li:target > ol { |
| 751 | border-left: 1px solid gold; |
| 752 | } |
| 753 | body.cpage-ckout .file-change-line, |
| 754 | body.cpage-info .file-change-line, |
| 755 | body.cpage-vdiff .file-change-line { |
| 756 | margin-top: 16px; |
| 757 | margin-bottom: 16px; |
| 758 | margin-right: 1em /* keep it from nudging right up against the scrollbar-reveal zone */; |
| 759 | display: flex; |
| 760 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -750,10 +750,11 @@ | |
| 750 | body.tkt div.content ol.tkt-changes > li:target > ol { |
| 751 | border-left: 1px solid gold; |
| 752 | } |
| 753 | body.cpage-ckout .file-change-line, |
| 754 | body.cpage-info .file-change-line, |
| 755 | body.cpage-vinfo .file-change-line, |
| 756 | body.cpage-vdiff .file-change-line { |
| 757 | margin-top: 16px; |
| 758 | margin-bottom: 16px; |
| 759 | margin-right: 1em /* keep it from nudging right up against the scrollbar-reveal zone */; |
| 760 | display: flex; |
| 761 |
+1
| --- src/default.css | ||
| +++ src/default.css | ||
| @@ -750,10 +750,11 @@ | ||
| 750 | 750 | body.tkt div.content ol.tkt-changes > li:target > ol { |
| 751 | 751 | border-left: 1px solid gold; |
| 752 | 752 | } |
| 753 | 753 | body.cpage-ckout .file-change-line, |
| 754 | 754 | body.cpage-info .file-change-line, |
| 755 | +body.cpage-vinfo .file-change-line, | |
| 755 | 756 | body.cpage-vdiff .file-change-line { |
| 756 | 757 | margin-top: 16px; |
| 757 | 758 | margin-bottom: 16px; |
| 758 | 759 | margin-right: 1em /* keep it from nudging right up against the scrollbar-reveal zone */; |
| 759 | 760 | display: flex; |
| 760 | 761 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -750,10 +750,11 @@ | |
| 750 | body.tkt div.content ol.tkt-changes > li:target > ol { |
| 751 | border-left: 1px solid gold; |
| 752 | } |
| 753 | body.cpage-ckout .file-change-line, |
| 754 | body.cpage-info .file-change-line, |
| 755 | body.cpage-vdiff .file-change-line { |
| 756 | margin-top: 16px; |
| 757 | margin-bottom: 16px; |
| 758 | margin-right: 1em /* keep it from nudging right up against the scrollbar-reveal zone */; |
| 759 | display: flex; |
| 760 |
| --- src/default.css | |
| +++ src/default.css | |
| @@ -750,10 +750,11 @@ | |
| 750 | body.tkt div.content ol.tkt-changes > li:target > ol { |
| 751 | border-left: 1px solid gold; |
| 752 | } |
| 753 | body.cpage-ckout .file-change-line, |
| 754 | body.cpage-info .file-change-line, |
| 755 | body.cpage-vinfo .file-change-line, |
| 756 | body.cpage-vdiff .file-change-line { |
| 757 | margin-top: 16px; |
| 758 | margin-bottom: 16px; |
| 759 | margin-right: 1em /* keep it from nudging right up against the scrollbar-reveal zone */; |
| 760 | display: flex; |
| 761 |
+18
-3
| --- src/descendants.c | ||
| +++ src/descendants.c | ||
| @@ -156,10 +156,27 @@ | ||
| 156 | 156 | " AND tagxref.tagtype>0)", |
| 157 | 157 | TAG_CLOSED |
| 158 | 158 | ); |
| 159 | 159 | } |
| 160 | 160 | } |
| 161 | + | |
| 162 | +/* | |
| 163 | +** If RID refers to a check-in, return the mtime of that check-in - the | |
| 164 | +** julian day number of when the check-in occurred. | |
| 165 | +*/ | |
| 166 | +double mtime_of_rid(int rid, double mtime){ | |
| 167 | + static Stmt q; | |
| 168 | + db_static_prepare(&q,"SELECT mtime FROM event WHERE objid=:rid"); | |
| 169 | + db_bind_int(&q, ":rid", rid); | |
| 170 | + if( db_step(&q)==SQLITE_ROW ){ | |
| 171 | + mtime = db_column_double(&q,0); | |
| 172 | + } | |
| 173 | + db_reset(&q); | |
| 174 | + return mtime; | |
| 175 | +} | |
| 176 | + | |
| 177 | + | |
| 161 | 178 | |
| 162 | 179 | /* |
| 163 | 180 | ** Load the record ID rid and up to |N|-1 closest ancestors into |
| 164 | 181 | ** the "ok" table. If N is zero, no limit. If ridBackTo is not zero |
| 165 | 182 | ** then stop the search upon reaching the ancestor with rid==ridBackTo. |
| @@ -197,13 +214,11 @@ | ||
| 197 | 214 | ** (3) Cherrypick merge parents. |
| 198 | 215 | ** (4) All ancestores of 1 and 2 but not of 3. |
| 199 | 216 | */ |
| 200 | 217 | double rLimitMtime = 0.0; |
| 201 | 218 | if( ridBackTo ){ |
| 202 | - rLimitMtime = db_double(0.0, | |
| 203 | - "SELECT mtime FROM event WHERE objid=%d", | |
| 204 | - ridBackTo); | |
| 219 | + rLimitMtime = mtime_of_rid(ridBackTo, 0.0); | |
| 205 | 220 | } |
| 206 | 221 | db_multi_exec( |
| 207 | 222 | "WITH RECURSIVE\n" |
| 208 | 223 | " parent(pid,cid,isCP) AS (\n" |
| 209 | 224 | " SELECT plink.pid, plink.cid, 0 AS xisCP FROM plink\n" |
| 210 | 225 |
| --- src/descendants.c | |
| +++ src/descendants.c | |
| @@ -156,10 +156,27 @@ | |
| 156 | " AND tagxref.tagtype>0)", |
| 157 | TAG_CLOSED |
| 158 | ); |
| 159 | } |
| 160 | } |
| 161 | |
| 162 | /* |
| 163 | ** Load the record ID rid and up to |N|-1 closest ancestors into |
| 164 | ** the "ok" table. If N is zero, no limit. If ridBackTo is not zero |
| 165 | ** then stop the search upon reaching the ancestor with rid==ridBackTo. |
| @@ -197,13 +214,11 @@ | |
| 197 | ** (3) Cherrypick merge parents. |
| 198 | ** (4) All ancestores of 1 and 2 but not of 3. |
| 199 | */ |
| 200 | double rLimitMtime = 0.0; |
| 201 | if( ridBackTo ){ |
| 202 | rLimitMtime = db_double(0.0, |
| 203 | "SELECT mtime FROM event WHERE objid=%d", |
| 204 | ridBackTo); |
| 205 | } |
| 206 | db_multi_exec( |
| 207 | "WITH RECURSIVE\n" |
| 208 | " parent(pid,cid,isCP) AS (\n" |
| 209 | " SELECT plink.pid, plink.cid, 0 AS xisCP FROM plink\n" |
| 210 |
| --- src/descendants.c | |
| +++ src/descendants.c | |
| @@ -156,10 +156,27 @@ | |
| 156 | " AND tagxref.tagtype>0)", |
| 157 | TAG_CLOSED |
| 158 | ); |
| 159 | } |
| 160 | } |
| 161 | |
| 162 | /* |
| 163 | ** If RID refers to a check-in, return the mtime of that check-in - the |
| 164 | ** julian day number of when the check-in occurred. |
| 165 | */ |
| 166 | double mtime_of_rid(int rid, double mtime){ |
| 167 | static Stmt q; |
| 168 | db_static_prepare(&q,"SELECT mtime FROM event WHERE objid=:rid"); |
| 169 | db_bind_int(&q, ":rid", rid); |
| 170 | if( db_step(&q)==SQLITE_ROW ){ |
| 171 | mtime = db_column_double(&q,0); |
| 172 | } |
| 173 | db_reset(&q); |
| 174 | return mtime; |
| 175 | } |
| 176 | |
| 177 | |
| 178 | |
| 179 | /* |
| 180 | ** Load the record ID rid and up to |N|-1 closest ancestors into |
| 181 | ** the "ok" table. If N is zero, no limit. If ridBackTo is not zero |
| 182 | ** then stop the search upon reaching the ancestor with rid==ridBackTo. |
| @@ -197,13 +214,11 @@ | |
| 214 | ** (3) Cherrypick merge parents. |
| 215 | ** (4) All ancestores of 1 and 2 but not of 3. |
| 216 | */ |
| 217 | double rLimitMtime = 0.0; |
| 218 | if( ridBackTo ){ |
| 219 | rLimitMtime = mtime_of_rid(ridBackTo, 0.0); |
| 220 | } |
| 221 | db_multi_exec( |
| 222 | "WITH RECURSIVE\n" |
| 223 | " parent(pid,cid,isCP) AS (\n" |
| 224 | " SELECT plink.pid, plink.cid, 0 AS xisCP FROM plink\n" |
| 225 |
+7
-1
| --- src/diff.c | ||
| +++ src/diff.c | ||
| @@ -3175,11 +3175,13 @@ | ||
| 3175 | 3175 | } |
| 3176 | 3176 | g.diffCnt[1] += nIns; |
| 3177 | 3177 | g.diffCnt[2] += nDel; |
| 3178 | 3178 | if( nIns+nDel ){ |
| 3179 | 3179 | g.diffCnt[0]++; |
| 3180 | - blob_appendf(pOut, "%10d %10d", nIns, nDel); | |
| 3180 | + if( !(pCfg->diffFlags & DIFF_BRIEF) ){ | |
| 3181 | + blob_appendf(pOut, "%10d %10d", nIns, nDel); | |
| 3182 | + } | |
| 3181 | 3183 | } |
| 3182 | 3184 | }else if( pCfg->diffFlags & (DIFF_RAW|DIFF_BY_TOKEN) ){ |
| 3183 | 3185 | const int *R = c.aEdit; |
| 3184 | 3186 | unsigned int r; |
| 3185 | 3187 | for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){ |
| @@ -3335,10 +3337,14 @@ | ||
| 3335 | 3337 | if( zDiffBinary ){ |
| 3336 | 3338 | if( is_truth(zDiffBinary) ) diffFlags |= DIFF_INCBINARY; |
| 3337 | 3339 | }else if( db_get_boolean("diff-binary", 1) ){ |
| 3338 | 3340 | diffFlags |= DIFF_INCBINARY; |
| 3339 | 3341 | } |
| 3342 | + }else if( isGDiff) { | |
| 3343 | + /* No external gdiff command found, using --by */ | |
| 3344 | + diffFlags |= DIFF_HTML|DIFF_WEBPAGE|DIFF_LINENO|DIFF_BROWSER | |
| 3345 | + |DIFF_SIDEBYSIDE; | |
| 3340 | 3346 | } |
| 3341 | 3347 | } |
| 3342 | 3348 | if( find_option("verbose","v",0)!=0 ) diffFlags |= DIFF_VERBOSE; |
| 3343 | 3349 | /* Deprecated, but retained for script compatibility. */ |
| 3344 | 3350 | else if( find_option("new-file","N",0)!=0 ) diffFlags |= DIFF_VERBOSE; |
| 3345 | 3351 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -3175,11 +3175,13 @@ | |
| 3175 | } |
| 3176 | g.diffCnt[1] += nIns; |
| 3177 | g.diffCnt[2] += nDel; |
| 3178 | if( nIns+nDel ){ |
| 3179 | g.diffCnt[0]++; |
| 3180 | blob_appendf(pOut, "%10d %10d", nIns, nDel); |
| 3181 | } |
| 3182 | }else if( pCfg->diffFlags & (DIFF_RAW|DIFF_BY_TOKEN) ){ |
| 3183 | const int *R = c.aEdit; |
| 3184 | unsigned int r; |
| 3185 | for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){ |
| @@ -3335,10 +3337,14 @@ | |
| 3335 | if( zDiffBinary ){ |
| 3336 | if( is_truth(zDiffBinary) ) diffFlags |= DIFF_INCBINARY; |
| 3337 | }else if( db_get_boolean("diff-binary", 1) ){ |
| 3338 | diffFlags |= DIFF_INCBINARY; |
| 3339 | } |
| 3340 | } |
| 3341 | } |
| 3342 | if( find_option("verbose","v",0)!=0 ) diffFlags |= DIFF_VERBOSE; |
| 3343 | /* Deprecated, but retained for script compatibility. */ |
| 3344 | else if( find_option("new-file","N",0)!=0 ) diffFlags |= DIFF_VERBOSE; |
| 3345 |
| --- src/diff.c | |
| +++ src/diff.c | |
| @@ -3175,11 +3175,13 @@ | |
| 3175 | } |
| 3176 | g.diffCnt[1] += nIns; |
| 3177 | g.diffCnt[2] += nDel; |
| 3178 | if( nIns+nDel ){ |
| 3179 | g.diffCnt[0]++; |
| 3180 | if( !(pCfg->diffFlags & DIFF_BRIEF) ){ |
| 3181 | blob_appendf(pOut, "%10d %10d", nIns, nDel); |
| 3182 | } |
| 3183 | } |
| 3184 | }else if( pCfg->diffFlags & (DIFF_RAW|DIFF_BY_TOKEN) ){ |
| 3185 | const int *R = c.aEdit; |
| 3186 | unsigned int r; |
| 3187 | for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){ |
| @@ -3335,10 +3337,14 @@ | |
| 3337 | if( zDiffBinary ){ |
| 3338 | if( is_truth(zDiffBinary) ) diffFlags |= DIFF_INCBINARY; |
| 3339 | }else if( db_get_boolean("diff-binary", 1) ){ |
| 3340 | diffFlags |= DIFF_INCBINARY; |
| 3341 | } |
| 3342 | }else if( isGDiff) { |
| 3343 | /* No external gdiff command found, using --by */ |
| 3344 | diffFlags |= DIFF_HTML|DIFF_WEBPAGE|DIFF_LINENO|DIFF_BROWSER |
| 3345 | |DIFF_SIDEBYSIDE; |
| 3346 | } |
| 3347 | } |
| 3348 | if( find_option("verbose","v",0)!=0 ) diffFlags |= DIFF_VERBOSE; |
| 3349 | /* Deprecated, but retained for script compatibility. */ |
| 3350 | else if( find_option("new-file","N",0)!=0 ) diffFlags |= DIFF_VERBOSE; |
| 3351 |
+36
-7
| --- src/diff.tcl | ||
| +++ src/diff.tcl | ||
| @@ -90,28 +90,56 @@ | ||
| 90 | 90 | set x [lindex $difftxt $ii] |
| 91 | 91 | incr ii |
| 92 | 92 | return $x |
| 93 | 93 | } |
| 94 | 94 | |
| 95 | -proc readDiffs {fossilcmd} { | |
| 95 | +proc reloadDiff {} { | |
| 96 | + global fossilcmd difftxt | |
| 97 | + unset -nocomplain difftxt | |
| 98 | + set idx [.txtA index @0,0] | |
| 99 | + readDiffs $fossilcmd 1 | |
| 100 | + update | |
| 101 | + viewDiff $idx | |
| 102 | +} | |
| 103 | + | |
| 104 | +proc readDiffs {fossilcmd redo} { | |
| 96 | 105 | global difftxt debug |
| 97 | 106 | if {![info exists difftxt]} { |
| 98 | 107 | if {$debug} { |
| 99 | 108 | puts "# [list open $fossilcmd r]" |
| 100 | 109 | flush stdout |
| 101 | 110 | } |
| 102 | - set in [open $fossilcmd r] | |
| 103 | - fconfigure $in -encoding utf-8 | |
| 104 | - set difftxt [split [read $in] \n] | |
| 105 | - close $in | |
| 111 | + if {[catch { | |
| 112 | + set in [open $fossilcmd r] | |
| 113 | + fconfigure $in -encoding utf-8 | |
| 114 | + set difftxt [split [read $in] \n] | |
| 115 | + close $in | |
| 116 | + } msg]} { | |
| 117 | + if {$redo} { | |
| 118 | + tk_messageBox -type ok -title Error -message "Unable to refresh:\n$msg" | |
| 119 | + return 0 | |
| 120 | + } else { | |
| 121 | + puts $msg | |
| 122 | + exit 1 | |
| 123 | + } | |
| 124 | + } | |
| 106 | 125 | } |
| 107 | 126 | set N [llength $difftxt] |
| 108 | 127 | set ii 0 |
| 109 | 128 | set nDiffs 0 |
| 110 | 129 | set n1 0 |
| 111 | 130 | set n2 0 |
| 112 | 131 | array set widths {txt 3 ln 3 mkr 1} |
| 132 | + if {$redo} { | |
| 133 | + foreach c [cols] {$c config -state normal} | |
| 134 | + .lnA delete 1.0 end | |
| 135 | + .txtA delete 1.0 end | |
| 136 | + .lnB delete 1.0 end | |
| 137 | + .txtB delete 1.0 end | |
| 138 | + .mkr delete 1.0 end | |
| 139 | + .wfiles.lb delete 0 end | |
| 140 | + } | |
| 113 | 141 | |
| 114 | 142 | |
| 115 | 143 | set fromIndex [lsearch -glob $fossilcmd *-from] |
| 116 | 144 | set toIndex [lsearch -glob $fossilcmd *-to] |
| 117 | 145 | set branchIndex [lsearch -glob $fossilcmd *-branch] |
| @@ -459,11 +487,11 @@ | ||
| 459 | 487 | ::ttk::scrollbar .sby -command {.txtA yview} -orient vertical |
| 460 | 488 | ::ttk::scrollbar .sbxA -command {.txtA xview} -orient horizontal |
| 461 | 489 | ::ttk::scrollbar .sbxB -command {.txtB xview} -orient horizontal |
| 462 | 490 | frame .spacer |
| 463 | 491 | |
| 464 | -if {[readDiffs $fossilcmd] == 0} { | |
| 492 | +if {[readDiffs $fossilcmd 0] == 0} { | |
| 465 | 493 | tk_messageBox -type ok -title $CFG(TITLE) -message "No changes" |
| 466 | 494 | exit |
| 467 | 495 | } |
| 468 | 496 | update idletasks |
| 469 | 497 | |
| @@ -574,14 +602,15 @@ | ||
| 574 | 602 | $w tag config search -background {#fcc000} |
| 575 | 603 | } |
| 576 | 604 | set ::search $w |
| 577 | 605 | } |
| 578 | 606 | ::ttk::button .bb.quit -text {Quit} -command exit |
| 607 | +::ttk::button .bb.reload -text {Reload} -command reloadDiff | |
| 579 | 608 | ::ttk::button .bb.invert -text {Invert} -command invertDiff |
| 580 | 609 | ::ttk::button .bb.save -text {Save As...} -command saveDiff |
| 581 | 610 | ::ttk::button .bb.search -text {Search} -command searchOnOff |
| 582 | -pack .bb.quit .bb.invert -side left | |
| 611 | +pack .bb.quit .bb.reload .bb.invert -side left | |
| 583 | 612 | if {$fossilcmd!=""} {pack .bb.save -side left} |
| 584 | 613 | pack .bb.files .bb.search -side left |
| 585 | 614 | grid rowconfigure . 1 -weight 1 |
| 586 | 615 | grid columnconfigure . 1 -weight 1 |
| 587 | 616 | grid columnconfigure . 4 -weight 1 |
| 588 | 617 |
| --- src/diff.tcl | |
| +++ src/diff.tcl | |
| @@ -90,28 +90,56 @@ | |
| 90 | set x [lindex $difftxt $ii] |
| 91 | incr ii |
| 92 | return $x |
| 93 | } |
| 94 | |
| 95 | proc readDiffs {fossilcmd} { |
| 96 | global difftxt debug |
| 97 | if {![info exists difftxt]} { |
| 98 | if {$debug} { |
| 99 | puts "# [list open $fossilcmd r]" |
| 100 | flush stdout |
| 101 | } |
| 102 | set in [open $fossilcmd r] |
| 103 | fconfigure $in -encoding utf-8 |
| 104 | set difftxt [split [read $in] \n] |
| 105 | close $in |
| 106 | } |
| 107 | set N [llength $difftxt] |
| 108 | set ii 0 |
| 109 | set nDiffs 0 |
| 110 | set n1 0 |
| 111 | set n2 0 |
| 112 | array set widths {txt 3 ln 3 mkr 1} |
| 113 | |
| 114 | |
| 115 | set fromIndex [lsearch -glob $fossilcmd *-from] |
| 116 | set toIndex [lsearch -glob $fossilcmd *-to] |
| 117 | set branchIndex [lsearch -glob $fossilcmd *-branch] |
| @@ -459,11 +487,11 @@ | |
| 459 | ::ttk::scrollbar .sby -command {.txtA yview} -orient vertical |
| 460 | ::ttk::scrollbar .sbxA -command {.txtA xview} -orient horizontal |
| 461 | ::ttk::scrollbar .sbxB -command {.txtB xview} -orient horizontal |
| 462 | frame .spacer |
| 463 | |
| 464 | if {[readDiffs $fossilcmd] == 0} { |
| 465 | tk_messageBox -type ok -title $CFG(TITLE) -message "No changes" |
| 466 | exit |
| 467 | } |
| 468 | update idletasks |
| 469 | |
| @@ -574,14 +602,15 @@ | |
| 574 | $w tag config search -background {#fcc000} |
| 575 | } |
| 576 | set ::search $w |
| 577 | } |
| 578 | ::ttk::button .bb.quit -text {Quit} -command exit |
| 579 | ::ttk::button .bb.invert -text {Invert} -command invertDiff |
| 580 | ::ttk::button .bb.save -text {Save As...} -command saveDiff |
| 581 | ::ttk::button .bb.search -text {Search} -command searchOnOff |
| 582 | pack .bb.quit .bb.invert -side left |
| 583 | if {$fossilcmd!=""} {pack .bb.save -side left} |
| 584 | pack .bb.files .bb.search -side left |
| 585 | grid rowconfigure . 1 -weight 1 |
| 586 | grid columnconfigure . 1 -weight 1 |
| 587 | grid columnconfigure . 4 -weight 1 |
| 588 |
| --- src/diff.tcl | |
| +++ src/diff.tcl | |
| @@ -90,28 +90,56 @@ | |
| 90 | set x [lindex $difftxt $ii] |
| 91 | incr ii |
| 92 | return $x |
| 93 | } |
| 94 | |
| 95 | proc reloadDiff {} { |
| 96 | global fossilcmd difftxt |
| 97 | unset -nocomplain difftxt |
| 98 | set idx [.txtA index @0,0] |
| 99 | readDiffs $fossilcmd 1 |
| 100 | update |
| 101 | viewDiff $idx |
| 102 | } |
| 103 | |
| 104 | proc readDiffs {fossilcmd redo} { |
| 105 | global difftxt debug |
| 106 | if {![info exists difftxt]} { |
| 107 | if {$debug} { |
| 108 | puts "# [list open $fossilcmd r]" |
| 109 | flush stdout |
| 110 | } |
| 111 | if {[catch { |
| 112 | set in [open $fossilcmd r] |
| 113 | fconfigure $in -encoding utf-8 |
| 114 | set difftxt [split [read $in] \n] |
| 115 | close $in |
| 116 | } msg]} { |
| 117 | if {$redo} { |
| 118 | tk_messageBox -type ok -title Error -message "Unable to refresh:\n$msg" |
| 119 | return 0 |
| 120 | } else { |
| 121 | puts $msg |
| 122 | exit 1 |
| 123 | } |
| 124 | } |
| 125 | } |
| 126 | set N [llength $difftxt] |
| 127 | set ii 0 |
| 128 | set nDiffs 0 |
| 129 | set n1 0 |
| 130 | set n2 0 |
| 131 | array set widths {txt 3 ln 3 mkr 1} |
| 132 | if {$redo} { |
| 133 | foreach c [cols] {$c config -state normal} |
| 134 | .lnA delete 1.0 end |
| 135 | .txtA delete 1.0 end |
| 136 | .lnB delete 1.0 end |
| 137 | .txtB delete 1.0 end |
| 138 | .mkr delete 1.0 end |
| 139 | .wfiles.lb delete 0 end |
| 140 | } |
| 141 | |
| 142 | |
| 143 | set fromIndex [lsearch -glob $fossilcmd *-from] |
| 144 | set toIndex [lsearch -glob $fossilcmd *-to] |
| 145 | set branchIndex [lsearch -glob $fossilcmd *-branch] |
| @@ -459,11 +487,11 @@ | |
| 487 | ::ttk::scrollbar .sby -command {.txtA yview} -orient vertical |
| 488 | ::ttk::scrollbar .sbxA -command {.txtA xview} -orient horizontal |
| 489 | ::ttk::scrollbar .sbxB -command {.txtB xview} -orient horizontal |
| 490 | frame .spacer |
| 491 | |
| 492 | if {[readDiffs $fossilcmd 0] == 0} { |
| 493 | tk_messageBox -type ok -title $CFG(TITLE) -message "No changes" |
| 494 | exit |
| 495 | } |
| 496 | update idletasks |
| 497 | |
| @@ -574,14 +602,15 @@ | |
| 602 | $w tag config search -background {#fcc000} |
| 603 | } |
| 604 | set ::search $w |
| 605 | } |
| 606 | ::ttk::button .bb.quit -text {Quit} -command exit |
| 607 | ::ttk::button .bb.reload -text {Reload} -command reloadDiff |
| 608 | ::ttk::button .bb.invert -text {Invert} -command invertDiff |
| 609 | ::ttk::button .bb.save -text {Save As...} -command saveDiff |
| 610 | ::ttk::button .bb.search -text {Search} -command searchOnOff |
| 611 | pack .bb.quit .bb.reload .bb.invert -side left |
| 612 | if {$fossilcmd!=""} {pack .bb.save -side left} |
| 613 | pack .bb.files .bb.search -side left |
| 614 | grid rowconfigure . 1 -weight 1 |
| 615 | grid columnconfigure . 1 -weight 1 |
| 616 | grid columnconfigure . 4 -weight 1 |
| 617 |
+69
-30
| --- src/diffcmd.c | ||
| +++ src/diffcmd.c | ||
| @@ -127,16 +127,16 @@ | ||
| 127 | 127 | DIFF_HTML|DIFF_WEBPAGE|DIFF_BROWSER|DIFF_JSON|DIFF_TCL))==0 ){ |
| 128 | 128 | fossil_print("Fossil-Diff-From: %s\n", |
| 129 | 129 | zFrom[0]=='(' ? zFrom : mprintf("%S %s", |
| 130 | 130 | rid_to_uuid(symbolic_name_to_rid(zFrom, "ci")), |
| 131 | 131 | db_text("","SELECT datetime(%f)||' UTC'", |
| 132 | - symbolic_name_to_mtime(zFrom, 0)))); | |
| 132 | + symbolic_name_to_mtime(zFrom, 0, 0)))); | |
| 133 | 133 | fossil_print("Fossil-Diff-To: %s\n", |
| 134 | 134 | zTo[0]=='(' ? zTo : mprintf("%S %s", |
| 135 | 135 | rid_to_uuid(symbolic_name_to_rid(zTo, "ci")), |
| 136 | 136 | db_text("","SELECT datetime(%f)||' UTC'", |
| 137 | - symbolic_name_to_mtime(zTo, 0)))); | |
| 137 | + symbolic_name_to_mtime(zTo, 0, 1)))); | |
| 138 | 138 | fossil_print("%.66c\n", '-'); |
| 139 | 139 | } |
| 140 | 140 | } |
| 141 | 141 | |
| 142 | 142 | /* |
| @@ -606,20 +606,22 @@ | ||
| 606 | 606 | blob_read_from_file(&file2, zFile2, ExtFILE); |
| 607 | 607 | zName2 = zName; |
| 608 | 608 | } |
| 609 | 609 | |
| 610 | 610 | /* Compute and output the differences */ |
| 611 | - if( pCfg->diffFlags & DIFF_BRIEF ){ | |
| 611 | + if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){ | |
| 612 | 612 | if( blob_compare(pFile1, &file2) ){ |
| 613 | 613 | fossil_print("CHANGED %s\n", zName); |
| 614 | 614 | } |
| 615 | 615 | }else{ |
| 616 | 616 | blob_zero(&out); |
| 617 | 617 | text_diff(pFile1, &file2, &out, pCfg); |
| 618 | 618 | if( blob_size(&out) ){ |
| 619 | 619 | if( pCfg->diffFlags & DIFF_NUMSTAT ){ |
| 620 | - blob_appendf(pOut, "%s %s\n", blob_str(&out), zName); | |
| 620 | + if( !(pCfg->diffFlags & DIFF_BRIEF) ){ | |
| 621 | + blob_appendf(pOut, "%s %s\n", blob_str(&out), zName); | |
| 622 | + } | |
| 621 | 623 | }else{ |
| 622 | 624 | diff_print_filenames(zName, zName2, pCfg, pOut); |
| 623 | 625 | blob_appendf(pOut, "%s\n", blob_str(&out)); |
| 624 | 626 | } |
| 625 | 627 | } |
| @@ -684,11 +686,16 @@ | ||
| 684 | 686 | blob_append_escaped_arg(&cmd, blob_str(&nameFile1), 1); |
| 685 | 687 | blob_append_escaped_arg(&cmd, zFile2, 1); |
| 686 | 688 | } |
| 687 | 689 | |
| 688 | 690 | /* Run the external diff command */ |
| 689 | - fossil_system(blob_str(&cmd)); | |
| 691 | + if( fossil_system(blob_str(&cmd)) ){ | |
| 692 | +#if !defined(_WIN32) | |
| 693 | + /* On Windows, exit codes are unreliable. */ | |
| 694 | + fossil_warning("External diff command failed: %b\n", &cmd); | |
| 695 | +#endif | |
| 696 | + } | |
| 690 | 697 | |
| 691 | 698 | /* Delete the temporary file and clean up memory used */ |
| 692 | 699 | if( useTempfile ) file_delete(blob_str(&nameFile1)); |
| 693 | 700 | blob_reset(&nameFile1); |
| 694 | 701 | blob_reset(&cmd); |
| @@ -712,18 +719,22 @@ | ||
| 712 | 719 | Blob *pFile1, /* In memory content to compare from */ |
| 713 | 720 | Blob *pFile2, /* In memory content to compare to */ |
| 714 | 721 | const char *zName, /* Display name of the file */ |
| 715 | 722 | DiffConfig *pCfg /* Diff flags */ |
| 716 | 723 | ){ |
| 717 | - if( pCfg->diffFlags & DIFF_BRIEF ) return; | |
| 724 | + if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){ | |
| 725 | + return; | |
| 726 | + } | |
| 718 | 727 | if( pCfg->zDiffCmd==0 ){ |
| 719 | 728 | Blob out; /* Diff output text */ |
| 720 | 729 | |
| 721 | 730 | blob_zero(&out); |
| 722 | 731 | text_diff(pFile1, pFile2, &out, pCfg); |
| 723 | 732 | if( pCfg->diffFlags & DIFF_NUMSTAT ){ |
| 724 | - fossil_print("%s %s\n", blob_str(&out), zName); | |
| 733 | + if( !(pCfg->diffFlags & DIFF_BRIEF) ){ | |
| 734 | + fossil_print("%s %s\n", blob_str(&out), zName); | |
| 735 | + } | |
| 725 | 736 | }else{ |
| 726 | 737 | diff_print_filenames(zName, zName, pCfg, 0); |
| 727 | 738 | fossil_print("%s\n", blob_str(&out)); |
| 728 | 739 | } |
| 729 | 740 | |
| @@ -1009,11 +1020,13 @@ | ||
| 1009 | 1020 | }else if( pTo ){ |
| 1010 | 1021 | zName = pTo->zName; |
| 1011 | 1022 | }else{ |
| 1012 | 1023 | zName = DIFF_NO_NAME; |
| 1013 | 1024 | } |
| 1014 | - if( pCfg->diffFlags & DIFF_BRIEF ) return; | |
| 1025 | + if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){ | |
| 1026 | + return; | |
| 1027 | + } | |
| 1015 | 1028 | diff_print_index(zName, pCfg, 0); |
| 1016 | 1029 | if( pFrom ){ |
| 1017 | 1030 | rid = uuid_to_rid(pFrom->zUuid, 0); |
| 1018 | 1031 | content_get(rid, &f1); |
| 1019 | 1032 | }else{ |
| @@ -1097,11 +1110,11 @@ | ||
| 1097 | 1110 | (void)file_dir_match(pFileDir, pFromFile->zName); /* Record name usage */ |
| 1098 | 1111 | pFromFile = manifest_file_next(pFrom,0); |
| 1099 | 1112 | pToFile = manifest_file_next(pTo,0); |
| 1100 | 1113 | }else{ |
| 1101 | 1114 | if( file_dir_match(pFileDir, pToFile->zName) ){ |
| 1102 | - if( pCfg->diffFlags & DIFF_BRIEF ){ | |
| 1115 | + if((pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT)){ | |
| 1103 | 1116 | fossil_print("CHANGED %s\n", pFromFile->zName); |
| 1104 | 1117 | }else{ |
| 1105 | 1118 | diff_manifest_entry(pFromFile, pToFile, pCfg); |
| 1106 | 1119 | } |
| 1107 | 1120 | } |
| @@ -1173,25 +1186,34 @@ | ||
| 1173 | 1186 | /* |
| 1174 | 1187 | ** Return the name of the external diff command, or return NULL if |
| 1175 | 1188 | ** no external diff command is defined. |
| 1176 | 1189 | */ |
| 1177 | 1190 | const char *diff_command_external(int guiDiff){ |
| 1178 | - const char *zDefault; | |
| 1179 | 1191 | const char *zName; |
| 1180 | - | |
| 1181 | - if( guiDiff ){ | |
| 1182 | -#if defined(_WIN32) | |
| 1183 | - zDefault = "WinDiff.exe"; | |
| 1184 | -#else | |
| 1185 | - zDefault = 0; | |
| 1186 | -#endif | |
| 1187 | - zName = "gdiff-command"; | |
| 1188 | - }else{ | |
| 1189 | - zDefault = 0; | |
| 1190 | - zName = "diff-command"; | |
| 1191 | - } | |
| 1192 | - return db_get(zName, zDefault); | |
| 1192 | + zName = guiDiff ? "gdiff-command" : "diff-command"; | |
| 1193 | + return db_get(zName, 0); | |
| 1194 | +} | |
| 1195 | + | |
| 1196 | +/* | |
| 1197 | +** Return true if it reasonable to run "diff -tk" for "gdiff". | |
| 1198 | +** | |
| 1199 | +** Details: Return true if all of the following are true: | |
| 1200 | +** | |
| 1201 | +** (1) The isGDiff flags is true | |
| 1202 | +** (2) The "gdiff-command" setting is undefined | |
| 1203 | +** (3) There is a "tclsh" on PATH | |
| 1204 | +** (4) There is a "wish" on PATH | |
| 1205 | +*/ | |
| 1206 | +int gdiff_using_tk(int isGdiff){ | |
| 1207 | + if( isGdiff | |
| 1208 | + && db_get("gdiff-command","")[0]==0 | |
| 1209 | + && fossil_app_on_path("tclsh",0) | |
| 1210 | + && fossil_app_on_path("wish",0) | |
| 1211 | + ){ | |
| 1212 | + return 1; | |
| 1213 | + } | |
| 1214 | + return 0; | |
| 1193 | 1215 | } |
| 1194 | 1216 | |
| 1195 | 1217 | /* |
| 1196 | 1218 | ** Show diff output in a Tcl/Tk window, in response to the --tk option |
| 1197 | 1219 | ** to the diff command. |
| @@ -1209,10 +1231,11 @@ | ||
| 1209 | 1231 | const char *zTempFile = 0; |
| 1210 | 1232 | char *zCmd; |
| 1211 | 1233 | const char *zTclsh; |
| 1212 | 1234 | int bDebug = find_option("tkdebug",0,0)!=0; |
| 1213 | 1235 | int bDarkMode = find_option("dark",0,0)!=0; |
| 1236 | + (void)find_option("debug",0,0); | |
| 1214 | 1237 | blob_zero(&script); |
| 1215 | 1238 | /* Caution: When this routine is called from the merge-info command, |
| 1216 | 1239 | ** the --tcl argument requires an argument. But merge-info does not |
| 1217 | 1240 | ** use -i, so we can take -i as that argument. This routine needs to |
| 1218 | 1241 | ** always have -i after --tcl. |
| @@ -1290,11 +1313,12 @@ | ||
| 1290 | 1313 | ** Usage: %fossil diff|gdiff ?OPTIONS? ?FILE1? ?FILE2 ...? |
| 1291 | 1314 | ** |
| 1292 | 1315 | ** Show the difference between the current version of each of the FILEs |
| 1293 | 1316 | ** specified (as they exist on disk) and that same file as it was checked- |
| 1294 | 1317 | ** out. Or if the FILE arguments are omitted, show all unsaved changes |
| 1295 | -** currently in the working check-out. | |
| 1318 | +** currently in the working check-out. The "gdiff" variant means to | |
| 1319 | +** to use a GUI diff. | |
| 1296 | 1320 | ** |
| 1297 | 1321 | ** The default output format is a "unified patch" (the same as the |
| 1298 | 1322 | ** output of "diff -u" on most unix systems). Many alternative formats |
| 1299 | 1323 | ** are available. A few of the more useful alternatives: |
| 1300 | 1324 | ** |
| @@ -1356,11 +1380,13 @@ | ||
| 1356 | 1380 | ** -i|--internal Use internal diff logic |
| 1357 | 1381 | ** --invert Invert the diff |
| 1358 | 1382 | ** --json Output formatted as JSON |
| 1359 | 1383 | ** -n|--linenum Show line numbers |
| 1360 | 1384 | ** -N|--new-file Alias for --verbose |
| 1361 | -** --numstat Show only the number of added and deleted lines | |
| 1385 | +** --numstat Show the number of added and deleted lines per | |
| 1386 | +** file, omitting the diff. When combined with | |
| 1387 | +** --brief, show only the total row. | |
| 1362 | 1388 | ** -y|--side-by-side Side-by-side diff |
| 1363 | 1389 | ** --strip-trailing-cr Strip trailing CR |
| 1364 | 1390 | ** --tcl Tcl-formatted output used internally by --tk |
| 1365 | 1391 | ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh") |
| 1366 | 1392 | ** --tk Launch a Tcl/Tk GUI for display |
| @@ -1382,15 +1408,15 @@ | ||
| 1382 | 1408 | int againstUndo = 0; /* Diff against files in the undo buffer */ |
| 1383 | 1409 | FileDirList *pFileDir = 0; /* Restrict the diff to these files */ |
| 1384 | 1410 | DiffConfig DCfg; /* Diff configuration object */ |
| 1385 | 1411 | int bFromIsDir = 0; /* True if zFrom is a directory name */ |
| 1386 | 1412 | |
| 1387 | - if( find_option("tk",0,0)!=0 || has_option("tclsh") ){ | |
| 1413 | + isGDiff = g.argv[1][0]=='g'; | |
| 1414 | + if( find_option("tk",0,0)!=0|| has_option("tclsh") ){ | |
| 1388 | 1415 | diff_tk("diff", 2); |
| 1389 | 1416 | return; |
| 1390 | 1417 | } |
| 1391 | - isGDiff = g.argv[1][0]=='g'; | |
| 1392 | 1418 | zFrom = find_option("from", "r", 1); |
| 1393 | 1419 | zTo = find_option("to", 0, 1); |
| 1394 | 1420 | zCheckin = find_option("checkin", "ci", 1); |
| 1395 | 1421 | zBranch = find_option("branch", 0, 1); |
| 1396 | 1422 | againstUndo = find_option("undo",0,0)!=0; |
| @@ -1402,10 +1428,11 @@ | ||
| 1402 | 1428 | if( zTo || zFrom || zCheckin ){ |
| 1403 | 1429 | fossil_fatal("cannot use --from, --to, or --checkin with --branch"); |
| 1404 | 1430 | } |
| 1405 | 1431 | zTo = zBranch; |
| 1406 | 1432 | zFrom = mprintf("root:%s", zBranch); |
| 1433 | + zBranch = 0; | |
| 1407 | 1434 | } |
| 1408 | 1435 | if( zCheckin!=0 && (zFrom!=0 || zTo!=0) ){ |
| 1409 | 1436 | fossil_fatal("cannot use --checkin together with --from or --to"); |
| 1410 | 1437 | } |
| 1411 | 1438 | if( 0==zCheckin ){ |
| @@ -1416,10 +1443,19 @@ | ||
| 1416 | 1443 | }else{ |
| 1417 | 1444 | db_find_and_open_repository(0, 0); |
| 1418 | 1445 | } |
| 1419 | 1446 | }else{ |
| 1420 | 1447 | db_find_and_open_repository(0, 0); |
| 1448 | + } | |
| 1449 | + if( gdiff_using_tk(isGDiff) ){ | |
| 1450 | + restore_option("--from", zFrom, 1); | |
| 1451 | + restore_option("--to", zTo, 1); | |
| 1452 | + restore_option("--checkin", zCheckin, 1); | |
| 1453 | + restore_option("--branch", zBranch, 1); | |
| 1454 | + if( againstUndo ) restore_option("--undo", 0, 0); | |
| 1455 | + diff_tk("diff", 2); | |
| 1456 | + return; | |
| 1421 | 1457 | } |
| 1422 | 1458 | determine_exec_relative_option(1); |
| 1423 | 1459 | if( zFrom!=file_tail(zFrom) |
| 1424 | 1460 | && file_isdir(zFrom, ExtFILE)==1 |
| 1425 | 1461 | && !db_exists("SELECT 1 FROM tag WHERE tagname='sym-%q'", zFrom) |
| @@ -1448,11 +1484,14 @@ | ||
| 1448 | 1484 | pFileDir[i-2].nName = blob_size(&fname); |
| 1449 | 1485 | pFileDir[i-2].nUsed = 0; |
| 1450 | 1486 | blob_reset(&fname); |
| 1451 | 1487 | } |
| 1452 | 1488 | } |
| 1453 | - if ( zCheckin!=0 ){ | |
| 1489 | + if( DCfg.diffFlags & DIFF_NUMSTAT ){ | |
| 1490 | + fossil_print("%10s %10s\n", "INSERTED", "DELETED"); | |
| 1491 | + } | |
| 1492 | + if( zCheckin!=0 ){ | |
| 1454 | 1493 | int ridTo = name_to_typed_rid(zCheckin, "ci"); |
| 1455 | 1494 | zTo = zCheckin; |
| 1456 | 1495 | zFrom = db_text(0, |
| 1457 | 1496 | "SELECT uuid FROM blob, plink" |
| 1458 | 1497 | " WHERE plink.cid=%d AND plink.isprim AND plink.pid=blob.rid", |
| @@ -1488,12 +1527,12 @@ | ||
| 1488 | 1527 | } |
| 1489 | 1528 | fossil_free(pFileDir); |
| 1490 | 1529 | } |
| 1491 | 1530 | diff_end(&DCfg, 0); |
| 1492 | 1531 | if ( DCfg.diffFlags & DIFF_NUMSTAT ){ |
| 1493 | - fossil_print("%10d %10d TOTAL over %d changed files\n", | |
| 1494 | - g.diffCnt[1], g.diffCnt[2], g.diffCnt[0]); | |
| 1532 | + fossil_print("%10d %10d TOTAL over %d changed file%s\n", | |
| 1533 | + g.diffCnt[1], g.diffCnt[2], g.diffCnt[0], g.diffCnt[0]!=1 ? "s": ""); | |
| 1495 | 1534 | } |
| 1496 | 1535 | } |
| 1497 | 1536 | |
| 1498 | 1537 | /* |
| 1499 | 1538 | ** WEBPAGE: vpatch |
| 1500 | 1539 |
| --- src/diffcmd.c | |
| +++ src/diffcmd.c | |
| @@ -127,16 +127,16 @@ | |
| 127 | DIFF_HTML|DIFF_WEBPAGE|DIFF_BROWSER|DIFF_JSON|DIFF_TCL))==0 ){ |
| 128 | fossil_print("Fossil-Diff-From: %s\n", |
| 129 | zFrom[0]=='(' ? zFrom : mprintf("%S %s", |
| 130 | rid_to_uuid(symbolic_name_to_rid(zFrom, "ci")), |
| 131 | db_text("","SELECT datetime(%f)||' UTC'", |
| 132 | symbolic_name_to_mtime(zFrom, 0)))); |
| 133 | fossil_print("Fossil-Diff-To: %s\n", |
| 134 | zTo[0]=='(' ? zTo : mprintf("%S %s", |
| 135 | rid_to_uuid(symbolic_name_to_rid(zTo, "ci")), |
| 136 | db_text("","SELECT datetime(%f)||' UTC'", |
| 137 | symbolic_name_to_mtime(zTo, 0)))); |
| 138 | fossil_print("%.66c\n", '-'); |
| 139 | } |
| 140 | } |
| 141 | |
| 142 | /* |
| @@ -606,20 +606,22 @@ | |
| 606 | blob_read_from_file(&file2, zFile2, ExtFILE); |
| 607 | zName2 = zName; |
| 608 | } |
| 609 | |
| 610 | /* Compute and output the differences */ |
| 611 | if( pCfg->diffFlags & DIFF_BRIEF ){ |
| 612 | if( blob_compare(pFile1, &file2) ){ |
| 613 | fossil_print("CHANGED %s\n", zName); |
| 614 | } |
| 615 | }else{ |
| 616 | blob_zero(&out); |
| 617 | text_diff(pFile1, &file2, &out, pCfg); |
| 618 | if( blob_size(&out) ){ |
| 619 | if( pCfg->diffFlags & DIFF_NUMSTAT ){ |
| 620 | blob_appendf(pOut, "%s %s\n", blob_str(&out), zName); |
| 621 | }else{ |
| 622 | diff_print_filenames(zName, zName2, pCfg, pOut); |
| 623 | blob_appendf(pOut, "%s\n", blob_str(&out)); |
| 624 | } |
| 625 | } |
| @@ -684,11 +686,16 @@ | |
| 684 | blob_append_escaped_arg(&cmd, blob_str(&nameFile1), 1); |
| 685 | blob_append_escaped_arg(&cmd, zFile2, 1); |
| 686 | } |
| 687 | |
| 688 | /* Run the external diff command */ |
| 689 | fossil_system(blob_str(&cmd)); |
| 690 | |
| 691 | /* Delete the temporary file and clean up memory used */ |
| 692 | if( useTempfile ) file_delete(blob_str(&nameFile1)); |
| 693 | blob_reset(&nameFile1); |
| 694 | blob_reset(&cmd); |
| @@ -712,18 +719,22 @@ | |
| 712 | Blob *pFile1, /* In memory content to compare from */ |
| 713 | Blob *pFile2, /* In memory content to compare to */ |
| 714 | const char *zName, /* Display name of the file */ |
| 715 | DiffConfig *pCfg /* Diff flags */ |
| 716 | ){ |
| 717 | if( pCfg->diffFlags & DIFF_BRIEF ) return; |
| 718 | if( pCfg->zDiffCmd==0 ){ |
| 719 | Blob out; /* Diff output text */ |
| 720 | |
| 721 | blob_zero(&out); |
| 722 | text_diff(pFile1, pFile2, &out, pCfg); |
| 723 | if( pCfg->diffFlags & DIFF_NUMSTAT ){ |
| 724 | fossil_print("%s %s\n", blob_str(&out), zName); |
| 725 | }else{ |
| 726 | diff_print_filenames(zName, zName, pCfg, 0); |
| 727 | fossil_print("%s\n", blob_str(&out)); |
| 728 | } |
| 729 | |
| @@ -1009,11 +1020,13 @@ | |
| 1009 | }else if( pTo ){ |
| 1010 | zName = pTo->zName; |
| 1011 | }else{ |
| 1012 | zName = DIFF_NO_NAME; |
| 1013 | } |
| 1014 | if( pCfg->diffFlags & DIFF_BRIEF ) return; |
| 1015 | diff_print_index(zName, pCfg, 0); |
| 1016 | if( pFrom ){ |
| 1017 | rid = uuid_to_rid(pFrom->zUuid, 0); |
| 1018 | content_get(rid, &f1); |
| 1019 | }else{ |
| @@ -1097,11 +1110,11 @@ | |
| 1097 | (void)file_dir_match(pFileDir, pFromFile->zName); /* Record name usage */ |
| 1098 | pFromFile = manifest_file_next(pFrom,0); |
| 1099 | pToFile = manifest_file_next(pTo,0); |
| 1100 | }else{ |
| 1101 | if( file_dir_match(pFileDir, pToFile->zName) ){ |
| 1102 | if( pCfg->diffFlags & DIFF_BRIEF ){ |
| 1103 | fossil_print("CHANGED %s\n", pFromFile->zName); |
| 1104 | }else{ |
| 1105 | diff_manifest_entry(pFromFile, pToFile, pCfg); |
| 1106 | } |
| 1107 | } |
| @@ -1173,25 +1186,34 @@ | |
| 1173 | /* |
| 1174 | ** Return the name of the external diff command, or return NULL if |
| 1175 | ** no external diff command is defined. |
| 1176 | */ |
| 1177 | const char *diff_command_external(int guiDiff){ |
| 1178 | const char *zDefault; |
| 1179 | const char *zName; |
| 1180 | |
| 1181 | if( guiDiff ){ |
| 1182 | #if defined(_WIN32) |
| 1183 | zDefault = "WinDiff.exe"; |
| 1184 | #else |
| 1185 | zDefault = 0; |
| 1186 | #endif |
| 1187 | zName = "gdiff-command"; |
| 1188 | }else{ |
| 1189 | zDefault = 0; |
| 1190 | zName = "diff-command"; |
| 1191 | } |
| 1192 | return db_get(zName, zDefault); |
| 1193 | } |
| 1194 | |
| 1195 | /* |
| 1196 | ** Show diff output in a Tcl/Tk window, in response to the --tk option |
| 1197 | ** to the diff command. |
| @@ -1209,10 +1231,11 @@ | |
| 1209 | const char *zTempFile = 0; |
| 1210 | char *zCmd; |
| 1211 | const char *zTclsh; |
| 1212 | int bDebug = find_option("tkdebug",0,0)!=0; |
| 1213 | int bDarkMode = find_option("dark",0,0)!=0; |
| 1214 | blob_zero(&script); |
| 1215 | /* Caution: When this routine is called from the merge-info command, |
| 1216 | ** the --tcl argument requires an argument. But merge-info does not |
| 1217 | ** use -i, so we can take -i as that argument. This routine needs to |
| 1218 | ** always have -i after --tcl. |
| @@ -1290,11 +1313,12 @@ | |
| 1290 | ** Usage: %fossil diff|gdiff ?OPTIONS? ?FILE1? ?FILE2 ...? |
| 1291 | ** |
| 1292 | ** Show the difference between the current version of each of the FILEs |
| 1293 | ** specified (as they exist on disk) and that same file as it was checked- |
| 1294 | ** out. Or if the FILE arguments are omitted, show all unsaved changes |
| 1295 | ** currently in the working check-out. |
| 1296 | ** |
| 1297 | ** The default output format is a "unified patch" (the same as the |
| 1298 | ** output of "diff -u" on most unix systems). Many alternative formats |
| 1299 | ** are available. A few of the more useful alternatives: |
| 1300 | ** |
| @@ -1356,11 +1380,13 @@ | |
| 1356 | ** -i|--internal Use internal diff logic |
| 1357 | ** --invert Invert the diff |
| 1358 | ** --json Output formatted as JSON |
| 1359 | ** -n|--linenum Show line numbers |
| 1360 | ** -N|--new-file Alias for --verbose |
| 1361 | ** --numstat Show only the number of added and deleted lines |
| 1362 | ** -y|--side-by-side Side-by-side diff |
| 1363 | ** --strip-trailing-cr Strip trailing CR |
| 1364 | ** --tcl Tcl-formatted output used internally by --tk |
| 1365 | ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh") |
| 1366 | ** --tk Launch a Tcl/Tk GUI for display |
| @@ -1382,15 +1408,15 @@ | |
| 1382 | int againstUndo = 0; /* Diff against files in the undo buffer */ |
| 1383 | FileDirList *pFileDir = 0; /* Restrict the diff to these files */ |
| 1384 | DiffConfig DCfg; /* Diff configuration object */ |
| 1385 | int bFromIsDir = 0; /* True if zFrom is a directory name */ |
| 1386 | |
| 1387 | if( find_option("tk",0,0)!=0 || has_option("tclsh") ){ |
| 1388 | diff_tk("diff", 2); |
| 1389 | return; |
| 1390 | } |
| 1391 | isGDiff = g.argv[1][0]=='g'; |
| 1392 | zFrom = find_option("from", "r", 1); |
| 1393 | zTo = find_option("to", 0, 1); |
| 1394 | zCheckin = find_option("checkin", "ci", 1); |
| 1395 | zBranch = find_option("branch", 0, 1); |
| 1396 | againstUndo = find_option("undo",0,0)!=0; |
| @@ -1402,10 +1428,11 @@ | |
| 1402 | if( zTo || zFrom || zCheckin ){ |
| 1403 | fossil_fatal("cannot use --from, --to, or --checkin with --branch"); |
| 1404 | } |
| 1405 | zTo = zBranch; |
| 1406 | zFrom = mprintf("root:%s", zBranch); |
| 1407 | } |
| 1408 | if( zCheckin!=0 && (zFrom!=0 || zTo!=0) ){ |
| 1409 | fossil_fatal("cannot use --checkin together with --from or --to"); |
| 1410 | } |
| 1411 | if( 0==zCheckin ){ |
| @@ -1416,10 +1443,19 @@ | |
| 1416 | }else{ |
| 1417 | db_find_and_open_repository(0, 0); |
| 1418 | } |
| 1419 | }else{ |
| 1420 | db_find_and_open_repository(0, 0); |
| 1421 | } |
| 1422 | determine_exec_relative_option(1); |
| 1423 | if( zFrom!=file_tail(zFrom) |
| 1424 | && file_isdir(zFrom, ExtFILE)==1 |
| 1425 | && !db_exists("SELECT 1 FROM tag WHERE tagname='sym-%q'", zFrom) |
| @@ -1448,11 +1484,14 @@ | |
| 1448 | pFileDir[i-2].nName = blob_size(&fname); |
| 1449 | pFileDir[i-2].nUsed = 0; |
| 1450 | blob_reset(&fname); |
| 1451 | } |
| 1452 | } |
| 1453 | if ( zCheckin!=0 ){ |
| 1454 | int ridTo = name_to_typed_rid(zCheckin, "ci"); |
| 1455 | zTo = zCheckin; |
| 1456 | zFrom = db_text(0, |
| 1457 | "SELECT uuid FROM blob, plink" |
| 1458 | " WHERE plink.cid=%d AND plink.isprim AND plink.pid=blob.rid", |
| @@ -1488,12 +1527,12 @@ | |
| 1488 | } |
| 1489 | fossil_free(pFileDir); |
| 1490 | } |
| 1491 | diff_end(&DCfg, 0); |
| 1492 | if ( DCfg.diffFlags & DIFF_NUMSTAT ){ |
| 1493 | fossil_print("%10d %10d TOTAL over %d changed files\n", |
| 1494 | g.diffCnt[1], g.diffCnt[2], g.diffCnt[0]); |
| 1495 | } |
| 1496 | } |
| 1497 | |
| 1498 | /* |
| 1499 | ** WEBPAGE: vpatch |
| 1500 |
| --- src/diffcmd.c | |
| +++ src/diffcmd.c | |
| @@ -127,16 +127,16 @@ | |
| 127 | DIFF_HTML|DIFF_WEBPAGE|DIFF_BROWSER|DIFF_JSON|DIFF_TCL))==0 ){ |
| 128 | fossil_print("Fossil-Diff-From: %s\n", |
| 129 | zFrom[0]=='(' ? zFrom : mprintf("%S %s", |
| 130 | rid_to_uuid(symbolic_name_to_rid(zFrom, "ci")), |
| 131 | db_text("","SELECT datetime(%f)||' UTC'", |
| 132 | symbolic_name_to_mtime(zFrom, 0, 0)))); |
| 133 | fossil_print("Fossil-Diff-To: %s\n", |
| 134 | zTo[0]=='(' ? zTo : mprintf("%S %s", |
| 135 | rid_to_uuid(symbolic_name_to_rid(zTo, "ci")), |
| 136 | db_text("","SELECT datetime(%f)||' UTC'", |
| 137 | symbolic_name_to_mtime(zTo, 0, 1)))); |
| 138 | fossil_print("%.66c\n", '-'); |
| 139 | } |
| 140 | } |
| 141 | |
| 142 | /* |
| @@ -606,20 +606,22 @@ | |
| 606 | blob_read_from_file(&file2, zFile2, ExtFILE); |
| 607 | zName2 = zName; |
| 608 | } |
| 609 | |
| 610 | /* Compute and output the differences */ |
| 611 | if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){ |
| 612 | if( blob_compare(pFile1, &file2) ){ |
| 613 | fossil_print("CHANGED %s\n", zName); |
| 614 | } |
| 615 | }else{ |
| 616 | blob_zero(&out); |
| 617 | text_diff(pFile1, &file2, &out, pCfg); |
| 618 | if( blob_size(&out) ){ |
| 619 | if( pCfg->diffFlags & DIFF_NUMSTAT ){ |
| 620 | if( !(pCfg->diffFlags & DIFF_BRIEF) ){ |
| 621 | blob_appendf(pOut, "%s %s\n", blob_str(&out), zName); |
| 622 | } |
| 623 | }else{ |
| 624 | diff_print_filenames(zName, zName2, pCfg, pOut); |
| 625 | blob_appendf(pOut, "%s\n", blob_str(&out)); |
| 626 | } |
| 627 | } |
| @@ -684,11 +686,16 @@ | |
| 686 | blob_append_escaped_arg(&cmd, blob_str(&nameFile1), 1); |
| 687 | blob_append_escaped_arg(&cmd, zFile2, 1); |
| 688 | } |
| 689 | |
| 690 | /* Run the external diff command */ |
| 691 | if( fossil_system(blob_str(&cmd)) ){ |
| 692 | #if !defined(_WIN32) |
| 693 | /* On Windows, exit codes are unreliable. */ |
| 694 | fossil_warning("External diff command failed: %b\n", &cmd); |
| 695 | #endif |
| 696 | } |
| 697 | |
| 698 | /* Delete the temporary file and clean up memory used */ |
| 699 | if( useTempfile ) file_delete(blob_str(&nameFile1)); |
| 700 | blob_reset(&nameFile1); |
| 701 | blob_reset(&cmd); |
| @@ -712,18 +719,22 @@ | |
| 719 | Blob *pFile1, /* In memory content to compare from */ |
| 720 | Blob *pFile2, /* In memory content to compare to */ |
| 721 | const char *zName, /* Display name of the file */ |
| 722 | DiffConfig *pCfg /* Diff flags */ |
| 723 | ){ |
| 724 | if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){ |
| 725 | return; |
| 726 | } |
| 727 | if( pCfg->zDiffCmd==0 ){ |
| 728 | Blob out; /* Diff output text */ |
| 729 | |
| 730 | blob_zero(&out); |
| 731 | text_diff(pFile1, pFile2, &out, pCfg); |
| 732 | if( pCfg->diffFlags & DIFF_NUMSTAT ){ |
| 733 | if( !(pCfg->diffFlags & DIFF_BRIEF) ){ |
| 734 | fossil_print("%s %s\n", blob_str(&out), zName); |
| 735 | } |
| 736 | }else{ |
| 737 | diff_print_filenames(zName, zName, pCfg, 0); |
| 738 | fossil_print("%s\n", blob_str(&out)); |
| 739 | } |
| 740 | |
| @@ -1009,11 +1020,13 @@ | |
| 1020 | }else if( pTo ){ |
| 1021 | zName = pTo->zName; |
| 1022 | }else{ |
| 1023 | zName = DIFF_NO_NAME; |
| 1024 | } |
| 1025 | if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){ |
| 1026 | return; |
| 1027 | } |
| 1028 | diff_print_index(zName, pCfg, 0); |
| 1029 | if( pFrom ){ |
| 1030 | rid = uuid_to_rid(pFrom->zUuid, 0); |
| 1031 | content_get(rid, &f1); |
| 1032 | }else{ |
| @@ -1097,11 +1110,11 @@ | |
| 1110 | (void)file_dir_match(pFileDir, pFromFile->zName); /* Record name usage */ |
| 1111 | pFromFile = manifest_file_next(pFrom,0); |
| 1112 | pToFile = manifest_file_next(pTo,0); |
| 1113 | }else{ |
| 1114 | if( file_dir_match(pFileDir, pToFile->zName) ){ |
| 1115 | if((pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT)){ |
| 1116 | fossil_print("CHANGED %s\n", pFromFile->zName); |
| 1117 | }else{ |
| 1118 | diff_manifest_entry(pFromFile, pToFile, pCfg); |
| 1119 | } |
| 1120 | } |
| @@ -1173,25 +1186,34 @@ | |
| 1186 | /* |
| 1187 | ** Return the name of the external diff command, or return NULL if |
| 1188 | ** no external diff command is defined. |
| 1189 | */ |
| 1190 | const char *diff_command_external(int guiDiff){ |
| 1191 | const char *zName; |
| 1192 | zName = guiDiff ? "gdiff-command" : "diff-command"; |
| 1193 | return db_get(zName, 0); |
| 1194 | } |
| 1195 | |
| 1196 | /* |
| 1197 | ** Return true if it reasonable to run "diff -tk" for "gdiff". |
| 1198 | ** |
| 1199 | ** Details: Return true if all of the following are true: |
| 1200 | ** |
| 1201 | ** (1) The isGDiff flags is true |
| 1202 | ** (2) The "gdiff-command" setting is undefined |
| 1203 | ** (3) There is a "tclsh" on PATH |
| 1204 | ** (4) There is a "wish" on PATH |
| 1205 | */ |
| 1206 | int gdiff_using_tk(int isGdiff){ |
| 1207 | if( isGdiff |
| 1208 | && db_get("gdiff-command","")[0]==0 |
| 1209 | && fossil_app_on_path("tclsh",0) |
| 1210 | && fossil_app_on_path("wish",0) |
| 1211 | ){ |
| 1212 | return 1; |
| 1213 | } |
| 1214 | return 0; |
| 1215 | } |
| 1216 | |
| 1217 | /* |
| 1218 | ** Show diff output in a Tcl/Tk window, in response to the --tk option |
| 1219 | ** to the diff command. |
| @@ -1209,10 +1231,11 @@ | |
| 1231 | const char *zTempFile = 0; |
| 1232 | char *zCmd; |
| 1233 | const char *zTclsh; |
| 1234 | int bDebug = find_option("tkdebug",0,0)!=0; |
| 1235 | int bDarkMode = find_option("dark",0,0)!=0; |
| 1236 | (void)find_option("debug",0,0); |
| 1237 | blob_zero(&script); |
| 1238 | /* Caution: When this routine is called from the merge-info command, |
| 1239 | ** the --tcl argument requires an argument. But merge-info does not |
| 1240 | ** use -i, so we can take -i as that argument. This routine needs to |
| 1241 | ** always have -i after --tcl. |
| @@ -1290,11 +1313,12 @@ | |
| 1313 | ** Usage: %fossil diff|gdiff ?OPTIONS? ?FILE1? ?FILE2 ...? |
| 1314 | ** |
| 1315 | ** Show the difference between the current version of each of the FILEs |
| 1316 | ** specified (as they exist on disk) and that same file as it was checked- |
| 1317 | ** out. Or if the FILE arguments are omitted, show all unsaved changes |
| 1318 | ** currently in the working check-out. The "gdiff" variant means to |
| 1319 | ** to use a GUI diff. |
| 1320 | ** |
| 1321 | ** The default output format is a "unified patch" (the same as the |
| 1322 | ** output of "diff -u" on most unix systems). Many alternative formats |
| 1323 | ** are available. A few of the more useful alternatives: |
| 1324 | ** |
| @@ -1356,11 +1380,13 @@ | |
| 1380 | ** -i|--internal Use internal diff logic |
| 1381 | ** --invert Invert the diff |
| 1382 | ** --json Output formatted as JSON |
| 1383 | ** -n|--linenum Show line numbers |
| 1384 | ** -N|--new-file Alias for --verbose |
| 1385 | ** --numstat Show the number of added and deleted lines per |
| 1386 | ** file, omitting the diff. When combined with |
| 1387 | ** --brief, show only the total row. |
| 1388 | ** -y|--side-by-side Side-by-side diff |
| 1389 | ** --strip-trailing-cr Strip trailing CR |
| 1390 | ** --tcl Tcl-formatted output used internally by --tk |
| 1391 | ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh") |
| 1392 | ** --tk Launch a Tcl/Tk GUI for display |
| @@ -1382,15 +1408,15 @@ | |
| 1408 | int againstUndo = 0; /* Diff against files in the undo buffer */ |
| 1409 | FileDirList *pFileDir = 0; /* Restrict the diff to these files */ |
| 1410 | DiffConfig DCfg; /* Diff configuration object */ |
| 1411 | int bFromIsDir = 0; /* True if zFrom is a directory name */ |
| 1412 | |
| 1413 | isGDiff = g.argv[1][0]=='g'; |
| 1414 | if( find_option("tk",0,0)!=0|| has_option("tclsh") ){ |
| 1415 | diff_tk("diff", 2); |
| 1416 | return; |
| 1417 | } |
| 1418 | zFrom = find_option("from", "r", 1); |
| 1419 | zTo = find_option("to", 0, 1); |
| 1420 | zCheckin = find_option("checkin", "ci", 1); |
| 1421 | zBranch = find_option("branch", 0, 1); |
| 1422 | againstUndo = find_option("undo",0,0)!=0; |
| @@ -1402,10 +1428,11 @@ | |
| 1428 | if( zTo || zFrom || zCheckin ){ |
| 1429 | fossil_fatal("cannot use --from, --to, or --checkin with --branch"); |
| 1430 | } |
| 1431 | zTo = zBranch; |
| 1432 | zFrom = mprintf("root:%s", zBranch); |
| 1433 | zBranch = 0; |
| 1434 | } |
| 1435 | if( zCheckin!=0 && (zFrom!=0 || zTo!=0) ){ |
| 1436 | fossil_fatal("cannot use --checkin together with --from or --to"); |
| 1437 | } |
| 1438 | if( 0==zCheckin ){ |
| @@ -1416,10 +1443,19 @@ | |
| 1443 | }else{ |
| 1444 | db_find_and_open_repository(0, 0); |
| 1445 | } |
| 1446 | }else{ |
| 1447 | db_find_and_open_repository(0, 0); |
| 1448 | } |
| 1449 | if( gdiff_using_tk(isGDiff) ){ |
| 1450 | restore_option("--from", zFrom, 1); |
| 1451 | restore_option("--to", zTo, 1); |
| 1452 | restore_option("--checkin", zCheckin, 1); |
| 1453 | restore_option("--branch", zBranch, 1); |
| 1454 | if( againstUndo ) restore_option("--undo", 0, 0); |
| 1455 | diff_tk("diff", 2); |
| 1456 | return; |
| 1457 | } |
| 1458 | determine_exec_relative_option(1); |
| 1459 | if( zFrom!=file_tail(zFrom) |
| 1460 | && file_isdir(zFrom, ExtFILE)==1 |
| 1461 | && !db_exists("SELECT 1 FROM tag WHERE tagname='sym-%q'", zFrom) |
| @@ -1448,11 +1484,14 @@ | |
| 1484 | pFileDir[i-2].nName = blob_size(&fname); |
| 1485 | pFileDir[i-2].nUsed = 0; |
| 1486 | blob_reset(&fname); |
| 1487 | } |
| 1488 | } |
| 1489 | if( DCfg.diffFlags & DIFF_NUMSTAT ){ |
| 1490 | fossil_print("%10s %10s\n", "INSERTED", "DELETED"); |
| 1491 | } |
| 1492 | if( zCheckin!=0 ){ |
| 1493 | int ridTo = name_to_typed_rid(zCheckin, "ci"); |
| 1494 | zTo = zCheckin; |
| 1495 | zFrom = db_text(0, |
| 1496 | "SELECT uuid FROM blob, plink" |
| 1497 | " WHERE plink.cid=%d AND plink.isprim AND plink.pid=blob.rid", |
| @@ -1488,12 +1527,12 @@ | |
| 1527 | } |
| 1528 | fossil_free(pFileDir); |
| 1529 | } |
| 1530 | diff_end(&DCfg, 0); |
| 1531 | if ( DCfg.diffFlags & DIFF_NUMSTAT ){ |
| 1532 | fossil_print("%10d %10d TOTAL over %d changed file%s\n", |
| 1533 | g.diffCnt[1], g.diffCnt[2], g.diffCnt[0], g.diffCnt[0]!=1 ? "s": ""); |
| 1534 | } |
| 1535 | } |
| 1536 | |
| 1537 | /* |
| 1538 | ** WEBPAGE: vpatch |
| 1539 |
+69
-30
| --- src/diffcmd.c | ||
| +++ src/diffcmd.c | ||
| @@ -127,16 +127,16 @@ | ||
| 127 | 127 | DIFF_HTML|DIFF_WEBPAGE|DIFF_BROWSER|DIFF_JSON|DIFF_TCL))==0 ){ |
| 128 | 128 | fossil_print("Fossil-Diff-From: %s\n", |
| 129 | 129 | zFrom[0]=='(' ? zFrom : mprintf("%S %s", |
| 130 | 130 | rid_to_uuid(symbolic_name_to_rid(zFrom, "ci")), |
| 131 | 131 | db_text("","SELECT datetime(%f)||' UTC'", |
| 132 | - symbolic_name_to_mtime(zFrom, 0)))); | |
| 132 | + symbolic_name_to_mtime(zFrom, 0, 0)))); | |
| 133 | 133 | fossil_print("Fossil-Diff-To: %s\n", |
| 134 | 134 | zTo[0]=='(' ? zTo : mprintf("%S %s", |
| 135 | 135 | rid_to_uuid(symbolic_name_to_rid(zTo, "ci")), |
| 136 | 136 | db_text("","SELECT datetime(%f)||' UTC'", |
| 137 | - symbolic_name_to_mtime(zTo, 0)))); | |
| 137 | + symbolic_name_to_mtime(zTo, 0, 1)))); | |
| 138 | 138 | fossil_print("%.66c\n", '-'); |
| 139 | 139 | } |
| 140 | 140 | } |
| 141 | 141 | |
| 142 | 142 | /* |
| @@ -606,20 +606,22 @@ | ||
| 606 | 606 | blob_read_from_file(&file2, zFile2, ExtFILE); |
| 607 | 607 | zName2 = zName; |
| 608 | 608 | } |
| 609 | 609 | |
| 610 | 610 | /* Compute and output the differences */ |
| 611 | - if( pCfg->diffFlags & DIFF_BRIEF ){ | |
| 611 | + if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){ | |
| 612 | 612 | if( blob_compare(pFile1, &file2) ){ |
| 613 | 613 | fossil_print("CHANGED %s\n", zName); |
| 614 | 614 | } |
| 615 | 615 | }else{ |
| 616 | 616 | blob_zero(&out); |
| 617 | 617 | text_diff(pFile1, &file2, &out, pCfg); |
| 618 | 618 | if( blob_size(&out) ){ |
| 619 | 619 | if( pCfg->diffFlags & DIFF_NUMSTAT ){ |
| 620 | - blob_appendf(pOut, "%s %s\n", blob_str(&out), zName); | |
| 620 | + if( !(pCfg->diffFlags & DIFF_BRIEF) ){ | |
| 621 | + blob_appendf(pOut, "%s %s\n", blob_str(&out), zName); | |
| 622 | + } | |
| 621 | 623 | }else{ |
| 622 | 624 | diff_print_filenames(zName, zName2, pCfg, pOut); |
| 623 | 625 | blob_appendf(pOut, "%s\n", blob_str(&out)); |
| 624 | 626 | } |
| 625 | 627 | } |
| @@ -684,11 +686,16 @@ | ||
| 684 | 686 | blob_append_escaped_arg(&cmd, blob_str(&nameFile1), 1); |
| 685 | 687 | blob_append_escaped_arg(&cmd, zFile2, 1); |
| 686 | 688 | } |
| 687 | 689 | |
| 688 | 690 | /* Run the external diff command */ |
| 689 | - fossil_system(blob_str(&cmd)); | |
| 691 | + if( fossil_system(blob_str(&cmd)) ){ | |
| 692 | +#if !defined(_WIN32) | |
| 693 | + /* On Windows, exit codes are unreliable. */ | |
| 694 | + fossil_warning("External diff command failed: %b\n", &cmd); | |
| 695 | +#endif | |
| 696 | + } | |
| 690 | 697 | |
| 691 | 698 | /* Delete the temporary file and clean up memory used */ |
| 692 | 699 | if( useTempfile ) file_delete(blob_str(&nameFile1)); |
| 693 | 700 | blob_reset(&nameFile1); |
| 694 | 701 | blob_reset(&cmd); |
| @@ -712,18 +719,22 @@ | ||
| 712 | 719 | Blob *pFile1, /* In memory content to compare from */ |
| 713 | 720 | Blob *pFile2, /* In memory content to compare to */ |
| 714 | 721 | const char *zName, /* Display name of the file */ |
| 715 | 722 | DiffConfig *pCfg /* Diff flags */ |
| 716 | 723 | ){ |
| 717 | - if( pCfg->diffFlags & DIFF_BRIEF ) return; | |
| 724 | + if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){ | |
| 725 | + return; | |
| 726 | + } | |
| 718 | 727 | if( pCfg->zDiffCmd==0 ){ |
| 719 | 728 | Blob out; /* Diff output text */ |
| 720 | 729 | |
| 721 | 730 | blob_zero(&out); |
| 722 | 731 | text_diff(pFile1, pFile2, &out, pCfg); |
| 723 | 732 | if( pCfg->diffFlags & DIFF_NUMSTAT ){ |
| 724 | - fossil_print("%s %s\n", blob_str(&out), zName); | |
| 733 | + if( !(pCfg->diffFlags & DIFF_BRIEF) ){ | |
| 734 | + fossil_print("%s %s\n", blob_str(&out), zName); | |
| 735 | + } | |
| 725 | 736 | }else{ |
| 726 | 737 | diff_print_filenames(zName, zName, pCfg, 0); |
| 727 | 738 | fossil_print("%s\n", blob_str(&out)); |
| 728 | 739 | } |
| 729 | 740 | |
| @@ -1009,11 +1020,13 @@ | ||
| 1009 | 1020 | }else if( pTo ){ |
| 1010 | 1021 | zName = pTo->zName; |
| 1011 | 1022 | }else{ |
| 1012 | 1023 | zName = DIFF_NO_NAME; |
| 1013 | 1024 | } |
| 1014 | - if( pCfg->diffFlags & DIFF_BRIEF ) return; | |
| 1025 | + if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){ | |
| 1026 | + return; | |
| 1027 | + } | |
| 1015 | 1028 | diff_print_index(zName, pCfg, 0); |
| 1016 | 1029 | if( pFrom ){ |
| 1017 | 1030 | rid = uuid_to_rid(pFrom->zUuid, 0); |
| 1018 | 1031 | content_get(rid, &f1); |
| 1019 | 1032 | }else{ |
| @@ -1097,11 +1110,11 @@ | ||
| 1097 | 1110 | (void)file_dir_match(pFileDir, pFromFile->zName); /* Record name usage */ |
| 1098 | 1111 | pFromFile = manifest_file_next(pFrom,0); |
| 1099 | 1112 | pToFile = manifest_file_next(pTo,0); |
| 1100 | 1113 | }else{ |
| 1101 | 1114 | if( file_dir_match(pFileDir, pToFile->zName) ){ |
| 1102 | - if( pCfg->diffFlags & DIFF_BRIEF ){ | |
| 1115 | + if((pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT)){ | |
| 1103 | 1116 | fossil_print("CHANGED %s\n", pFromFile->zName); |
| 1104 | 1117 | }else{ |
| 1105 | 1118 | diff_manifest_entry(pFromFile, pToFile, pCfg); |
| 1106 | 1119 | } |
| 1107 | 1120 | } |
| @@ -1173,25 +1186,34 @@ | ||
| 1173 | 1186 | /* |
| 1174 | 1187 | ** Return the name of the external diff command, or return NULL if |
| 1175 | 1188 | ** no external diff command is defined. |
| 1176 | 1189 | */ |
| 1177 | 1190 | const char *diff_command_external(int guiDiff){ |
| 1178 | - const char *zDefault; | |
| 1179 | 1191 | const char *zName; |
| 1180 | - | |
| 1181 | - if( guiDiff ){ | |
| 1182 | -#if defined(_WIN32) | |
| 1183 | - zDefault = "WinDiff.exe"; | |
| 1184 | -#else | |
| 1185 | - zDefault = 0; | |
| 1186 | -#endif | |
| 1187 | - zName = "gdiff-command"; | |
| 1188 | - }else{ | |
| 1189 | - zDefault = 0; | |
| 1190 | - zName = "diff-command"; | |
| 1191 | - } | |
| 1192 | - return db_get(zName, zDefault); | |
| 1192 | + zName = guiDiff ? "gdiff-command" : "diff-command"; | |
| 1193 | + return db_get(zName, 0); | |
| 1194 | +} | |
| 1195 | + | |
| 1196 | +/* | |
| 1197 | +** Return true if it reasonable to run "diff -tk" for "gdiff". | |
| 1198 | +** | |
| 1199 | +** Details: Return true if all of the following are true: | |
| 1200 | +** | |
| 1201 | +** (1) The isGDiff flags is true | |
| 1202 | +** (2) The "gdiff-command" setting is undefined | |
| 1203 | +** (3) There is a "tclsh" on PATH | |
| 1204 | +** (4) There is a "wish" on PATH | |
| 1205 | +*/ | |
| 1206 | +int gdiff_using_tk(int isGdiff){ | |
| 1207 | + if( isGdiff | |
| 1208 | + && db_get("gdiff-command","")[0]==0 | |
| 1209 | + && fossil_app_on_path("tclsh",0) | |
| 1210 | + && fossil_app_on_path("wish",0) | |
| 1211 | + ){ | |
| 1212 | + return 1; | |
| 1213 | + } | |
| 1214 | + return 0; | |
| 1193 | 1215 | } |
| 1194 | 1216 | |
| 1195 | 1217 | /* |
| 1196 | 1218 | ** Show diff output in a Tcl/Tk window, in response to the --tk option |
| 1197 | 1219 | ** to the diff command. |
| @@ -1209,10 +1231,11 @@ | ||
| 1209 | 1231 | const char *zTempFile = 0; |
| 1210 | 1232 | char *zCmd; |
| 1211 | 1233 | const char *zTclsh; |
| 1212 | 1234 | int bDebug = find_option("tkdebug",0,0)!=0; |
| 1213 | 1235 | int bDarkMode = find_option("dark",0,0)!=0; |
| 1236 | + (void)find_option("debug",0,0); | |
| 1214 | 1237 | blob_zero(&script); |
| 1215 | 1238 | /* Caution: When this routine is called from the merge-info command, |
| 1216 | 1239 | ** the --tcl argument requires an argument. But merge-info does not |
| 1217 | 1240 | ** use -i, so we can take -i as that argument. This routine needs to |
| 1218 | 1241 | ** always have -i after --tcl. |
| @@ -1290,11 +1313,12 @@ | ||
| 1290 | 1313 | ** Usage: %fossil diff|gdiff ?OPTIONS? ?FILE1? ?FILE2 ...? |
| 1291 | 1314 | ** |
| 1292 | 1315 | ** Show the difference between the current version of each of the FILEs |
| 1293 | 1316 | ** specified (as they exist on disk) and that same file as it was checked- |
| 1294 | 1317 | ** out. Or if the FILE arguments are omitted, show all unsaved changes |
| 1295 | -** currently in the working check-out. | |
| 1318 | +** currently in the working check-out. The "gdiff" variant means to | |
| 1319 | +** to use a GUI diff. | |
| 1296 | 1320 | ** |
| 1297 | 1321 | ** The default output format is a "unified patch" (the same as the |
| 1298 | 1322 | ** output of "diff -u" on most unix systems). Many alternative formats |
| 1299 | 1323 | ** are available. A few of the more useful alternatives: |
| 1300 | 1324 | ** |
| @@ -1356,11 +1380,13 @@ | ||
| 1356 | 1380 | ** -i|--internal Use internal diff logic |
| 1357 | 1381 | ** --invert Invert the diff |
| 1358 | 1382 | ** --json Output formatted as JSON |
| 1359 | 1383 | ** -n|--linenum Show line numbers |
| 1360 | 1384 | ** -N|--new-file Alias for --verbose |
| 1361 | -** --numstat Show only the number of added and deleted lines | |
| 1385 | +** --numstat Show the number of added and deleted lines per | |
| 1386 | +** file, omitting the diff. When combined with | |
| 1387 | +** --brief, show only the total row. | |
| 1362 | 1388 | ** -y|--side-by-side Side-by-side diff |
| 1363 | 1389 | ** --strip-trailing-cr Strip trailing CR |
| 1364 | 1390 | ** --tcl Tcl-formatted output used internally by --tk |
| 1365 | 1391 | ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh") |
| 1366 | 1392 | ** --tk Launch a Tcl/Tk GUI for display |
| @@ -1382,15 +1408,15 @@ | ||
| 1382 | 1408 | int againstUndo = 0; /* Diff against files in the undo buffer */ |
| 1383 | 1409 | FileDirList *pFileDir = 0; /* Restrict the diff to these files */ |
| 1384 | 1410 | DiffConfig DCfg; /* Diff configuration object */ |
| 1385 | 1411 | int bFromIsDir = 0; /* True if zFrom is a directory name */ |
| 1386 | 1412 | |
| 1387 | - if( find_option("tk",0,0)!=0 || has_option("tclsh") ){ | |
| 1413 | + isGDiff = g.argv[1][0]=='g'; | |
| 1414 | + if( find_option("tk",0,0)!=0|| has_option("tclsh") ){ | |
| 1388 | 1415 | diff_tk("diff", 2); |
| 1389 | 1416 | return; |
| 1390 | 1417 | } |
| 1391 | - isGDiff = g.argv[1][0]=='g'; | |
| 1392 | 1418 | zFrom = find_option("from", "r", 1); |
| 1393 | 1419 | zTo = find_option("to", 0, 1); |
| 1394 | 1420 | zCheckin = find_option("checkin", "ci", 1); |
| 1395 | 1421 | zBranch = find_option("branch", 0, 1); |
| 1396 | 1422 | againstUndo = find_option("undo",0,0)!=0; |
| @@ -1402,10 +1428,11 @@ | ||
| 1402 | 1428 | if( zTo || zFrom || zCheckin ){ |
| 1403 | 1429 | fossil_fatal("cannot use --from, --to, or --checkin with --branch"); |
| 1404 | 1430 | } |
| 1405 | 1431 | zTo = zBranch; |
| 1406 | 1432 | zFrom = mprintf("root:%s", zBranch); |
| 1433 | + zBranch = 0; | |
| 1407 | 1434 | } |
| 1408 | 1435 | if( zCheckin!=0 && (zFrom!=0 || zTo!=0) ){ |
| 1409 | 1436 | fossil_fatal("cannot use --checkin together with --from or --to"); |
| 1410 | 1437 | } |
| 1411 | 1438 | if( 0==zCheckin ){ |
| @@ -1416,10 +1443,19 @@ | ||
| 1416 | 1443 | }else{ |
| 1417 | 1444 | db_find_and_open_repository(0, 0); |
| 1418 | 1445 | } |
| 1419 | 1446 | }else{ |
| 1420 | 1447 | db_find_and_open_repository(0, 0); |
| 1448 | + } | |
| 1449 | + if( gdiff_using_tk(isGDiff) ){ | |
| 1450 | + restore_option("--from", zFrom, 1); | |
| 1451 | + restore_option("--to", zTo, 1); | |
| 1452 | + restore_option("--checkin", zCheckin, 1); | |
| 1453 | + restore_option("--branch", zBranch, 1); | |
| 1454 | + if( againstUndo ) restore_option("--undo", 0, 0); | |
| 1455 | + diff_tk("diff", 2); | |
| 1456 | + return; | |
| 1421 | 1457 | } |
| 1422 | 1458 | determine_exec_relative_option(1); |
| 1423 | 1459 | if( zFrom!=file_tail(zFrom) |
| 1424 | 1460 | && file_isdir(zFrom, ExtFILE)==1 |
| 1425 | 1461 | && !db_exists("SELECT 1 FROM tag WHERE tagname='sym-%q'", zFrom) |
| @@ -1448,11 +1484,14 @@ | ||
| 1448 | 1484 | pFileDir[i-2].nName = blob_size(&fname); |
| 1449 | 1485 | pFileDir[i-2].nUsed = 0; |
| 1450 | 1486 | blob_reset(&fname); |
| 1451 | 1487 | } |
| 1452 | 1488 | } |
| 1453 | - if ( zCheckin!=0 ){ | |
| 1489 | + if( DCfg.diffFlags & DIFF_NUMSTAT ){ | |
| 1490 | + fossil_print("%10s %10s\n", "INSERTED", "DELETED"); | |
| 1491 | + } | |
| 1492 | + if( zCheckin!=0 ){ | |
| 1454 | 1493 | int ridTo = name_to_typed_rid(zCheckin, "ci"); |
| 1455 | 1494 | zTo = zCheckin; |
| 1456 | 1495 | zFrom = db_text(0, |
| 1457 | 1496 | "SELECT uuid FROM blob, plink" |
| 1458 | 1497 | " WHERE plink.cid=%d AND plink.isprim AND plink.pid=blob.rid", |
| @@ -1488,12 +1527,12 @@ | ||
| 1488 | 1527 | } |
| 1489 | 1528 | fossil_free(pFileDir); |
| 1490 | 1529 | } |
| 1491 | 1530 | diff_end(&DCfg, 0); |
| 1492 | 1531 | if ( DCfg.diffFlags & DIFF_NUMSTAT ){ |
| 1493 | - fossil_print("%10d %10d TOTAL over %d changed files\n", | |
| 1494 | - g.diffCnt[1], g.diffCnt[2], g.diffCnt[0]); | |
| 1532 | + fossil_print("%10d %10d TOTAL over %d changed file%s\n", | |
| 1533 | + g.diffCnt[1], g.diffCnt[2], g.diffCnt[0], g.diffCnt[0]!=1 ? "s": ""); | |
| 1495 | 1534 | } |
| 1496 | 1535 | } |
| 1497 | 1536 | |
| 1498 | 1537 | /* |
| 1499 | 1538 | ** WEBPAGE: vpatch |
| 1500 | 1539 |
| --- src/diffcmd.c | |
| +++ src/diffcmd.c | |
| @@ -127,16 +127,16 @@ | |
| 127 | DIFF_HTML|DIFF_WEBPAGE|DIFF_BROWSER|DIFF_JSON|DIFF_TCL))==0 ){ |
| 128 | fossil_print("Fossil-Diff-From: %s\n", |
| 129 | zFrom[0]=='(' ? zFrom : mprintf("%S %s", |
| 130 | rid_to_uuid(symbolic_name_to_rid(zFrom, "ci")), |
| 131 | db_text("","SELECT datetime(%f)||' UTC'", |
| 132 | symbolic_name_to_mtime(zFrom, 0)))); |
| 133 | fossil_print("Fossil-Diff-To: %s\n", |
| 134 | zTo[0]=='(' ? zTo : mprintf("%S %s", |
| 135 | rid_to_uuid(symbolic_name_to_rid(zTo, "ci")), |
| 136 | db_text("","SELECT datetime(%f)||' UTC'", |
| 137 | symbolic_name_to_mtime(zTo, 0)))); |
| 138 | fossil_print("%.66c\n", '-'); |
| 139 | } |
| 140 | } |
| 141 | |
| 142 | /* |
| @@ -606,20 +606,22 @@ | |
| 606 | blob_read_from_file(&file2, zFile2, ExtFILE); |
| 607 | zName2 = zName; |
| 608 | } |
| 609 | |
| 610 | /* Compute and output the differences */ |
| 611 | if( pCfg->diffFlags & DIFF_BRIEF ){ |
| 612 | if( blob_compare(pFile1, &file2) ){ |
| 613 | fossil_print("CHANGED %s\n", zName); |
| 614 | } |
| 615 | }else{ |
| 616 | blob_zero(&out); |
| 617 | text_diff(pFile1, &file2, &out, pCfg); |
| 618 | if( blob_size(&out) ){ |
| 619 | if( pCfg->diffFlags & DIFF_NUMSTAT ){ |
| 620 | blob_appendf(pOut, "%s %s\n", blob_str(&out), zName); |
| 621 | }else{ |
| 622 | diff_print_filenames(zName, zName2, pCfg, pOut); |
| 623 | blob_appendf(pOut, "%s\n", blob_str(&out)); |
| 624 | } |
| 625 | } |
| @@ -684,11 +686,16 @@ | |
| 684 | blob_append_escaped_arg(&cmd, blob_str(&nameFile1), 1); |
| 685 | blob_append_escaped_arg(&cmd, zFile2, 1); |
| 686 | } |
| 687 | |
| 688 | /* Run the external diff command */ |
| 689 | fossil_system(blob_str(&cmd)); |
| 690 | |
| 691 | /* Delete the temporary file and clean up memory used */ |
| 692 | if( useTempfile ) file_delete(blob_str(&nameFile1)); |
| 693 | blob_reset(&nameFile1); |
| 694 | blob_reset(&cmd); |
| @@ -712,18 +719,22 @@ | |
| 712 | Blob *pFile1, /* In memory content to compare from */ |
| 713 | Blob *pFile2, /* In memory content to compare to */ |
| 714 | const char *zName, /* Display name of the file */ |
| 715 | DiffConfig *pCfg /* Diff flags */ |
| 716 | ){ |
| 717 | if( pCfg->diffFlags & DIFF_BRIEF ) return; |
| 718 | if( pCfg->zDiffCmd==0 ){ |
| 719 | Blob out; /* Diff output text */ |
| 720 | |
| 721 | blob_zero(&out); |
| 722 | text_diff(pFile1, pFile2, &out, pCfg); |
| 723 | if( pCfg->diffFlags & DIFF_NUMSTAT ){ |
| 724 | fossil_print("%s %s\n", blob_str(&out), zName); |
| 725 | }else{ |
| 726 | diff_print_filenames(zName, zName, pCfg, 0); |
| 727 | fossil_print("%s\n", blob_str(&out)); |
| 728 | } |
| 729 | |
| @@ -1009,11 +1020,13 @@ | |
| 1009 | }else if( pTo ){ |
| 1010 | zName = pTo->zName; |
| 1011 | }else{ |
| 1012 | zName = DIFF_NO_NAME; |
| 1013 | } |
| 1014 | if( pCfg->diffFlags & DIFF_BRIEF ) return; |
| 1015 | diff_print_index(zName, pCfg, 0); |
| 1016 | if( pFrom ){ |
| 1017 | rid = uuid_to_rid(pFrom->zUuid, 0); |
| 1018 | content_get(rid, &f1); |
| 1019 | }else{ |
| @@ -1097,11 +1110,11 @@ | |
| 1097 | (void)file_dir_match(pFileDir, pFromFile->zName); /* Record name usage */ |
| 1098 | pFromFile = manifest_file_next(pFrom,0); |
| 1099 | pToFile = manifest_file_next(pTo,0); |
| 1100 | }else{ |
| 1101 | if( file_dir_match(pFileDir, pToFile->zName) ){ |
| 1102 | if( pCfg->diffFlags & DIFF_BRIEF ){ |
| 1103 | fossil_print("CHANGED %s\n", pFromFile->zName); |
| 1104 | }else{ |
| 1105 | diff_manifest_entry(pFromFile, pToFile, pCfg); |
| 1106 | } |
| 1107 | } |
| @@ -1173,25 +1186,34 @@ | |
| 1173 | /* |
| 1174 | ** Return the name of the external diff command, or return NULL if |
| 1175 | ** no external diff command is defined. |
| 1176 | */ |
| 1177 | const char *diff_command_external(int guiDiff){ |
| 1178 | const char *zDefault; |
| 1179 | const char *zName; |
| 1180 | |
| 1181 | if( guiDiff ){ |
| 1182 | #if defined(_WIN32) |
| 1183 | zDefault = "WinDiff.exe"; |
| 1184 | #else |
| 1185 | zDefault = 0; |
| 1186 | #endif |
| 1187 | zName = "gdiff-command"; |
| 1188 | }else{ |
| 1189 | zDefault = 0; |
| 1190 | zName = "diff-command"; |
| 1191 | } |
| 1192 | return db_get(zName, zDefault); |
| 1193 | } |
| 1194 | |
| 1195 | /* |
| 1196 | ** Show diff output in a Tcl/Tk window, in response to the --tk option |
| 1197 | ** to the diff command. |
| @@ -1209,10 +1231,11 @@ | |
| 1209 | const char *zTempFile = 0; |
| 1210 | char *zCmd; |
| 1211 | const char *zTclsh; |
| 1212 | int bDebug = find_option("tkdebug",0,0)!=0; |
| 1213 | int bDarkMode = find_option("dark",0,0)!=0; |
| 1214 | blob_zero(&script); |
| 1215 | /* Caution: When this routine is called from the merge-info command, |
| 1216 | ** the --tcl argument requires an argument. But merge-info does not |
| 1217 | ** use -i, so we can take -i as that argument. This routine needs to |
| 1218 | ** always have -i after --tcl. |
| @@ -1290,11 +1313,12 @@ | |
| 1290 | ** Usage: %fossil diff|gdiff ?OPTIONS? ?FILE1? ?FILE2 ...? |
| 1291 | ** |
| 1292 | ** Show the difference between the current version of each of the FILEs |
| 1293 | ** specified (as they exist on disk) and that same file as it was checked- |
| 1294 | ** out. Or if the FILE arguments are omitted, show all unsaved changes |
| 1295 | ** currently in the working check-out. |
| 1296 | ** |
| 1297 | ** The default output format is a "unified patch" (the same as the |
| 1298 | ** output of "diff -u" on most unix systems). Many alternative formats |
| 1299 | ** are available. A few of the more useful alternatives: |
| 1300 | ** |
| @@ -1356,11 +1380,13 @@ | |
| 1356 | ** -i|--internal Use internal diff logic |
| 1357 | ** --invert Invert the diff |
| 1358 | ** --json Output formatted as JSON |
| 1359 | ** -n|--linenum Show line numbers |
| 1360 | ** -N|--new-file Alias for --verbose |
| 1361 | ** --numstat Show only the number of added and deleted lines |
| 1362 | ** -y|--side-by-side Side-by-side diff |
| 1363 | ** --strip-trailing-cr Strip trailing CR |
| 1364 | ** --tcl Tcl-formatted output used internally by --tk |
| 1365 | ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh") |
| 1366 | ** --tk Launch a Tcl/Tk GUI for display |
| @@ -1382,15 +1408,15 @@ | |
| 1382 | int againstUndo = 0; /* Diff against files in the undo buffer */ |
| 1383 | FileDirList *pFileDir = 0; /* Restrict the diff to these files */ |
| 1384 | DiffConfig DCfg; /* Diff configuration object */ |
| 1385 | int bFromIsDir = 0; /* True if zFrom is a directory name */ |
| 1386 | |
| 1387 | if( find_option("tk",0,0)!=0 || has_option("tclsh") ){ |
| 1388 | diff_tk("diff", 2); |
| 1389 | return; |
| 1390 | } |
| 1391 | isGDiff = g.argv[1][0]=='g'; |
| 1392 | zFrom = find_option("from", "r", 1); |
| 1393 | zTo = find_option("to", 0, 1); |
| 1394 | zCheckin = find_option("checkin", "ci", 1); |
| 1395 | zBranch = find_option("branch", 0, 1); |
| 1396 | againstUndo = find_option("undo",0,0)!=0; |
| @@ -1402,10 +1428,11 @@ | |
| 1402 | if( zTo || zFrom || zCheckin ){ |
| 1403 | fossil_fatal("cannot use --from, --to, or --checkin with --branch"); |
| 1404 | } |
| 1405 | zTo = zBranch; |
| 1406 | zFrom = mprintf("root:%s", zBranch); |
| 1407 | } |
| 1408 | if( zCheckin!=0 && (zFrom!=0 || zTo!=0) ){ |
| 1409 | fossil_fatal("cannot use --checkin together with --from or --to"); |
| 1410 | } |
| 1411 | if( 0==zCheckin ){ |
| @@ -1416,10 +1443,19 @@ | |
| 1416 | }else{ |
| 1417 | db_find_and_open_repository(0, 0); |
| 1418 | } |
| 1419 | }else{ |
| 1420 | db_find_and_open_repository(0, 0); |
| 1421 | } |
| 1422 | determine_exec_relative_option(1); |
| 1423 | if( zFrom!=file_tail(zFrom) |
| 1424 | && file_isdir(zFrom, ExtFILE)==1 |
| 1425 | && !db_exists("SELECT 1 FROM tag WHERE tagname='sym-%q'", zFrom) |
| @@ -1448,11 +1484,14 @@ | |
| 1448 | pFileDir[i-2].nName = blob_size(&fname); |
| 1449 | pFileDir[i-2].nUsed = 0; |
| 1450 | blob_reset(&fname); |
| 1451 | } |
| 1452 | } |
| 1453 | if ( zCheckin!=0 ){ |
| 1454 | int ridTo = name_to_typed_rid(zCheckin, "ci"); |
| 1455 | zTo = zCheckin; |
| 1456 | zFrom = db_text(0, |
| 1457 | "SELECT uuid FROM blob, plink" |
| 1458 | " WHERE plink.cid=%d AND plink.isprim AND plink.pid=blob.rid", |
| @@ -1488,12 +1527,12 @@ | |
| 1488 | } |
| 1489 | fossil_free(pFileDir); |
| 1490 | } |
| 1491 | diff_end(&DCfg, 0); |
| 1492 | if ( DCfg.diffFlags & DIFF_NUMSTAT ){ |
| 1493 | fossil_print("%10d %10d TOTAL over %d changed files\n", |
| 1494 | g.diffCnt[1], g.diffCnt[2], g.diffCnt[0]); |
| 1495 | } |
| 1496 | } |
| 1497 | |
| 1498 | /* |
| 1499 | ** WEBPAGE: vpatch |
| 1500 |
| --- src/diffcmd.c | |
| +++ src/diffcmd.c | |
| @@ -127,16 +127,16 @@ | |
| 127 | DIFF_HTML|DIFF_WEBPAGE|DIFF_BROWSER|DIFF_JSON|DIFF_TCL))==0 ){ |
| 128 | fossil_print("Fossil-Diff-From: %s\n", |
| 129 | zFrom[0]=='(' ? zFrom : mprintf("%S %s", |
| 130 | rid_to_uuid(symbolic_name_to_rid(zFrom, "ci")), |
| 131 | db_text("","SELECT datetime(%f)||' UTC'", |
| 132 | symbolic_name_to_mtime(zFrom, 0, 0)))); |
| 133 | fossil_print("Fossil-Diff-To: %s\n", |
| 134 | zTo[0]=='(' ? zTo : mprintf("%S %s", |
| 135 | rid_to_uuid(symbolic_name_to_rid(zTo, "ci")), |
| 136 | db_text("","SELECT datetime(%f)||' UTC'", |
| 137 | symbolic_name_to_mtime(zTo, 0, 1)))); |
| 138 | fossil_print("%.66c\n", '-'); |
| 139 | } |
| 140 | } |
| 141 | |
| 142 | /* |
| @@ -606,20 +606,22 @@ | |
| 606 | blob_read_from_file(&file2, zFile2, ExtFILE); |
| 607 | zName2 = zName; |
| 608 | } |
| 609 | |
| 610 | /* Compute and output the differences */ |
| 611 | if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){ |
| 612 | if( blob_compare(pFile1, &file2) ){ |
| 613 | fossil_print("CHANGED %s\n", zName); |
| 614 | } |
| 615 | }else{ |
| 616 | blob_zero(&out); |
| 617 | text_diff(pFile1, &file2, &out, pCfg); |
| 618 | if( blob_size(&out) ){ |
| 619 | if( pCfg->diffFlags & DIFF_NUMSTAT ){ |
| 620 | if( !(pCfg->diffFlags & DIFF_BRIEF) ){ |
| 621 | blob_appendf(pOut, "%s %s\n", blob_str(&out), zName); |
| 622 | } |
| 623 | }else{ |
| 624 | diff_print_filenames(zName, zName2, pCfg, pOut); |
| 625 | blob_appendf(pOut, "%s\n", blob_str(&out)); |
| 626 | } |
| 627 | } |
| @@ -684,11 +686,16 @@ | |
| 686 | blob_append_escaped_arg(&cmd, blob_str(&nameFile1), 1); |
| 687 | blob_append_escaped_arg(&cmd, zFile2, 1); |
| 688 | } |
| 689 | |
| 690 | /* Run the external diff command */ |
| 691 | if( fossil_system(blob_str(&cmd)) ){ |
| 692 | #if !defined(_WIN32) |
| 693 | /* On Windows, exit codes are unreliable. */ |
| 694 | fossil_warning("External diff command failed: %b\n", &cmd); |
| 695 | #endif |
| 696 | } |
| 697 | |
| 698 | /* Delete the temporary file and clean up memory used */ |
| 699 | if( useTempfile ) file_delete(blob_str(&nameFile1)); |
| 700 | blob_reset(&nameFile1); |
| 701 | blob_reset(&cmd); |
| @@ -712,18 +719,22 @@ | |
| 719 | Blob *pFile1, /* In memory content to compare from */ |
| 720 | Blob *pFile2, /* In memory content to compare to */ |
| 721 | const char *zName, /* Display name of the file */ |
| 722 | DiffConfig *pCfg /* Diff flags */ |
| 723 | ){ |
| 724 | if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){ |
| 725 | return; |
| 726 | } |
| 727 | if( pCfg->zDiffCmd==0 ){ |
| 728 | Blob out; /* Diff output text */ |
| 729 | |
| 730 | blob_zero(&out); |
| 731 | text_diff(pFile1, pFile2, &out, pCfg); |
| 732 | if( pCfg->diffFlags & DIFF_NUMSTAT ){ |
| 733 | if( !(pCfg->diffFlags & DIFF_BRIEF) ){ |
| 734 | fossil_print("%s %s\n", blob_str(&out), zName); |
| 735 | } |
| 736 | }else{ |
| 737 | diff_print_filenames(zName, zName, pCfg, 0); |
| 738 | fossil_print("%s\n", blob_str(&out)); |
| 739 | } |
| 740 | |
| @@ -1009,11 +1020,13 @@ | |
| 1020 | }else if( pTo ){ |
| 1021 | zName = pTo->zName; |
| 1022 | }else{ |
| 1023 | zName = DIFF_NO_NAME; |
| 1024 | } |
| 1025 | if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){ |
| 1026 | return; |
| 1027 | } |
| 1028 | diff_print_index(zName, pCfg, 0); |
| 1029 | if( pFrom ){ |
| 1030 | rid = uuid_to_rid(pFrom->zUuid, 0); |
| 1031 | content_get(rid, &f1); |
| 1032 | }else{ |
| @@ -1097,11 +1110,11 @@ | |
| 1110 | (void)file_dir_match(pFileDir, pFromFile->zName); /* Record name usage */ |
| 1111 | pFromFile = manifest_file_next(pFrom,0); |
| 1112 | pToFile = manifest_file_next(pTo,0); |
| 1113 | }else{ |
| 1114 | if( file_dir_match(pFileDir, pToFile->zName) ){ |
| 1115 | if((pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT)){ |
| 1116 | fossil_print("CHANGED %s\n", pFromFile->zName); |
| 1117 | }else{ |
| 1118 | diff_manifest_entry(pFromFile, pToFile, pCfg); |
| 1119 | } |
| 1120 | } |
| @@ -1173,25 +1186,34 @@ | |
| 1186 | /* |
| 1187 | ** Return the name of the external diff command, or return NULL if |
| 1188 | ** no external diff command is defined. |
| 1189 | */ |
| 1190 | const char *diff_command_external(int guiDiff){ |
| 1191 | const char *zName; |
| 1192 | zName = guiDiff ? "gdiff-command" : "diff-command"; |
| 1193 | return db_get(zName, 0); |
| 1194 | } |
| 1195 | |
| 1196 | /* |
| 1197 | ** Return true if it reasonable to run "diff -tk" for "gdiff". |
| 1198 | ** |
| 1199 | ** Details: Return true if all of the following are true: |
| 1200 | ** |
| 1201 | ** (1) The isGDiff flags is true |
| 1202 | ** (2) The "gdiff-command" setting is undefined |
| 1203 | ** (3) There is a "tclsh" on PATH |
| 1204 | ** (4) There is a "wish" on PATH |
| 1205 | */ |
| 1206 | int gdiff_using_tk(int isGdiff){ |
| 1207 | if( isGdiff |
| 1208 | && db_get("gdiff-command","")[0]==0 |
| 1209 | && fossil_app_on_path("tclsh",0) |
| 1210 | && fossil_app_on_path("wish",0) |
| 1211 | ){ |
| 1212 | return 1; |
| 1213 | } |
| 1214 | return 0; |
| 1215 | } |
| 1216 | |
| 1217 | /* |
| 1218 | ** Show diff output in a Tcl/Tk window, in response to the --tk option |
| 1219 | ** to the diff command. |
| @@ -1209,10 +1231,11 @@ | |
| 1231 | const char *zTempFile = 0; |
| 1232 | char *zCmd; |
| 1233 | const char *zTclsh; |
| 1234 | int bDebug = find_option("tkdebug",0,0)!=0; |
| 1235 | int bDarkMode = find_option("dark",0,0)!=0; |
| 1236 | (void)find_option("debug",0,0); |
| 1237 | blob_zero(&script); |
| 1238 | /* Caution: When this routine is called from the merge-info command, |
| 1239 | ** the --tcl argument requires an argument. But merge-info does not |
| 1240 | ** use -i, so we can take -i as that argument. This routine needs to |
| 1241 | ** always have -i after --tcl. |
| @@ -1290,11 +1313,12 @@ | |
| 1313 | ** Usage: %fossil diff|gdiff ?OPTIONS? ?FILE1? ?FILE2 ...? |
| 1314 | ** |
| 1315 | ** Show the difference between the current version of each of the FILEs |
| 1316 | ** specified (as they exist on disk) and that same file as it was checked- |
| 1317 | ** out. Or if the FILE arguments are omitted, show all unsaved changes |
| 1318 | ** currently in the working check-out. The "gdiff" variant means to |
| 1319 | ** to use a GUI diff. |
| 1320 | ** |
| 1321 | ** The default output format is a "unified patch" (the same as the |
| 1322 | ** output of "diff -u" on most unix systems). Many alternative formats |
| 1323 | ** are available. A few of the more useful alternatives: |
| 1324 | ** |
| @@ -1356,11 +1380,13 @@ | |
| 1380 | ** -i|--internal Use internal diff logic |
| 1381 | ** --invert Invert the diff |
| 1382 | ** --json Output formatted as JSON |
| 1383 | ** -n|--linenum Show line numbers |
| 1384 | ** -N|--new-file Alias for --verbose |
| 1385 | ** --numstat Show the number of added and deleted lines per |
| 1386 | ** file, omitting the diff. When combined with |
| 1387 | ** --brief, show only the total row. |
| 1388 | ** -y|--side-by-side Side-by-side diff |
| 1389 | ** --strip-trailing-cr Strip trailing CR |
| 1390 | ** --tcl Tcl-formatted output used internally by --tk |
| 1391 | ** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh") |
| 1392 | ** --tk Launch a Tcl/Tk GUI for display |
| @@ -1382,15 +1408,15 @@ | |
| 1408 | int againstUndo = 0; /* Diff against files in the undo buffer */ |
| 1409 | FileDirList *pFileDir = 0; /* Restrict the diff to these files */ |
| 1410 | DiffConfig DCfg; /* Diff configuration object */ |
| 1411 | int bFromIsDir = 0; /* True if zFrom is a directory name */ |
| 1412 | |
| 1413 | isGDiff = g.argv[1][0]=='g'; |
| 1414 | if( find_option("tk",0,0)!=0|| has_option("tclsh") ){ |
| 1415 | diff_tk("diff", 2); |
| 1416 | return; |
| 1417 | } |
| 1418 | zFrom = find_option("from", "r", 1); |
| 1419 | zTo = find_option("to", 0, 1); |
| 1420 | zCheckin = find_option("checkin", "ci", 1); |
| 1421 | zBranch = find_option("branch", 0, 1); |
| 1422 | againstUndo = find_option("undo",0,0)!=0; |
| @@ -1402,10 +1428,11 @@ | |
| 1428 | if( zTo || zFrom || zCheckin ){ |
| 1429 | fossil_fatal("cannot use --from, --to, or --checkin with --branch"); |
| 1430 | } |
| 1431 | zTo = zBranch; |
| 1432 | zFrom = mprintf("root:%s", zBranch); |
| 1433 | zBranch = 0; |
| 1434 | } |
| 1435 | if( zCheckin!=0 && (zFrom!=0 || zTo!=0) ){ |
| 1436 | fossil_fatal("cannot use --checkin together with --from or --to"); |
| 1437 | } |
| 1438 | if( 0==zCheckin ){ |
| @@ -1416,10 +1443,19 @@ | |
| 1443 | }else{ |
| 1444 | db_find_and_open_repository(0, 0); |
| 1445 | } |
| 1446 | }else{ |
| 1447 | db_find_and_open_repository(0, 0); |
| 1448 | } |
| 1449 | if( gdiff_using_tk(isGDiff) ){ |
| 1450 | restore_option("--from", zFrom, 1); |
| 1451 | restore_option("--to", zTo, 1); |
| 1452 | restore_option("--checkin", zCheckin, 1); |
| 1453 | restore_option("--branch", zBranch, 1); |
| 1454 | if( againstUndo ) restore_option("--undo", 0, 0); |
| 1455 | diff_tk("diff", 2); |
| 1456 | return; |
| 1457 | } |
| 1458 | determine_exec_relative_option(1); |
| 1459 | if( zFrom!=file_tail(zFrom) |
| 1460 | && file_isdir(zFrom, ExtFILE)==1 |
| 1461 | && !db_exists("SELECT 1 FROM tag WHERE tagname='sym-%q'", zFrom) |
| @@ -1448,11 +1484,14 @@ | |
| 1484 | pFileDir[i-2].nName = blob_size(&fname); |
| 1485 | pFileDir[i-2].nUsed = 0; |
| 1486 | blob_reset(&fname); |
| 1487 | } |
| 1488 | } |
| 1489 | if( DCfg.diffFlags & DIFF_NUMSTAT ){ |
| 1490 | fossil_print("%10s %10s\n", "INSERTED", "DELETED"); |
| 1491 | } |
| 1492 | if( zCheckin!=0 ){ |
| 1493 | int ridTo = name_to_typed_rid(zCheckin, "ci"); |
| 1494 | zTo = zCheckin; |
| 1495 | zFrom = db_text(0, |
| 1496 | "SELECT uuid FROM blob, plink" |
| 1497 | " WHERE plink.cid=%d AND plink.isprim AND plink.pid=blob.rid", |
| @@ -1488,12 +1527,12 @@ | |
| 1527 | } |
| 1528 | fossil_free(pFileDir); |
| 1529 | } |
| 1530 | diff_end(&DCfg, 0); |
| 1531 | if ( DCfg.diffFlags & DIFF_NUMSTAT ){ |
| 1532 | fossil_print("%10d %10d TOTAL over %d changed file%s\n", |
| 1533 | g.diffCnt[1], g.diffCnt[2], g.diffCnt[0], g.diffCnt[0]!=1 ? "s": ""); |
| 1534 | } |
| 1535 | } |
| 1536 | |
| 1537 | /* |
| 1538 | ** WEBPAGE: vpatch |
| 1539 |
+372
-55
| --- src/dispatch.c | ||
| +++ src/dispatch.c | ||
| @@ -54,10 +54,11 @@ | ||
| 54 | 54 | /* NOTE: 0x0400 = CMDFLAG_SENSITIVE in mkindex.c! */ |
| 55 | 55 | #define CMDFLAG_HIDDEN 0x0800 /* Elide from most listings */ |
| 56 | 56 | #define CMDFLAG_LDAVG_EXEMPT 0x1000 /* Exempt from load_control() */ |
| 57 | 57 | #define CMDFLAG_ALIAS 0x2000 /* Command aliases */ |
| 58 | 58 | #define CMDFLAG_KEEPEMPTY 0x4000 /* Do not unset empty settings */ |
| 59 | +#define CMDFLAG_ABBREVSUBCMD 0x8000 /* Help text abbreviates subcommands */ | |
| 59 | 60 | /**************************************************************************/ |
| 60 | 61 | |
| 61 | 62 | /* Values for the 2nd parameter to dispatch_name_search() */ |
| 62 | 63 | #define CMDFLAG_ANY 0x0038 /* Match anything */ |
| 63 | 64 | #define CMDFLAG_PREFIX 0x0200 /* Prefix match is ok */ |
| @@ -522,13 +523,21 @@ | ||
| 522 | 523 | } |
| 523 | 524 | |
| 524 | 525 | /* |
| 525 | 526 | ** Format help text for TTY display. |
| 526 | 527 | */ |
| 527 | -static void help_to_text(const char *zHelp, Blob *pText){ | |
| 528 | +static void help_to_text(const char *zHelp, Blob *pText, int bUsage){ | |
| 528 | 529 | int i, x; |
| 529 | 530 | char c; |
| 531 | + if( zHelp[0]=='>' ){ | |
| 532 | + if( !bUsage ){ | |
| 533 | + blob_appendf(pText, "Usage:"); | |
| 534 | + }else{ | |
| 535 | + blob_append_char(pText, ' '); | |
| 536 | + } | |
| 537 | + zHelp++; | |
| 538 | + } | |
| 530 | 539 | for(i=0; (c = zHelp[i])!=0; i++){ |
| 531 | 540 | if( c=='%' && strncmp(zHelp+i,"%fossil",7)==0 ){ |
| 532 | 541 | if( i>0 ) blob_append(pText, zHelp, i); |
| 533 | 542 | blob_append(pText, "fossil", 6); |
| 534 | 543 | zHelp += i+7; |
| @@ -603,11 +612,11 @@ | ||
| 603 | 612 | fossil_print("# %s\n", aCommand[bktHelp[aCommand[i].iHelp][j]].zName); |
| 604 | 613 | fossil_print("%s\n\n", aCommand[i].zHelp); |
| 605 | 614 | }else{ |
| 606 | 615 | Blob txt; |
| 607 | 616 | blob_init(&txt, 0, 0); |
| 608 | - help_to_text(aCommand[i].zHelp, &txt); | |
| 617 | + help_to_text(aCommand[i].zHelp, &txt, 0); | |
| 609 | 618 | for(j=0; j<occHelp[aCommand[i].iHelp]; j++){ |
| 610 | 619 | fossil_print("# %s%s\n", |
| 611 | 620 | aCommand[bktHelp[aCommand[i].iHelp][j]].zName, |
| 612 | 621 | (aCommand[i].eCmdFlags & CMDFLAG_VERSIONABLE)!=0 ? |
| 613 | 622 | " (versionable)" : ""); |
| @@ -802,10 +811,32 @@ | ||
| 802 | 811 | fossil_print(" %s\n", az[j]); |
| 803 | 812 | } |
| 804 | 813 | } |
| 805 | 814 | } |
| 806 | 815 | |
| 816 | + | |
| 817 | +/* | |
| 818 | +** Returns 1 if the command or page name zName is known to be a | |
| 819 | +** command/page which is only available in certain builds/platforms, | |
| 820 | +** else returns 0. | |
| 821 | +*/ | |
| 822 | +static int help_is_platform_command(const char *zName){ | |
| 823 | + const char *aList[] = { | |
| 824 | + /* List of commands/pages which are known to only be available in | |
| 825 | + ** certain builds/platforms. */ | |
| 826 | + "winsrv", | |
| 827 | + "json", "/json", | |
| 828 | + NULL /* end-of-list sentinel */ | |
| 829 | + }; | |
| 830 | + int i = 0; | |
| 831 | + const char *z; | |
| 832 | + for( z = aList[0]; z ; z = aList[++i] ){ | |
| 833 | + if( 0==fossil_strcmp(zName, z) ) return 1; | |
| 834 | + } | |
| 835 | + return 0; | |
| 836 | +} | |
| 837 | + | |
| 807 | 838 | /* |
| 808 | 839 | ** WEBPAGE: help |
| 809 | 840 | ** URL: /help?name=CMD |
| 810 | 841 | ** |
| 811 | 842 | ** Show the built-in help text for CMD. CMD can be a command-line interface |
| @@ -829,34 +860,46 @@ | ||
| 829 | 860 | if( zCmd && *zCmd ){ |
| 830 | 861 | int rc; |
| 831 | 862 | const CmdOrPage *pCmd = 0; |
| 832 | 863 | |
| 833 | 864 | style_set_current_feature("tkt"); |
| 834 | - style_header("Help: %s", zCmd); | |
| 835 | - | |
| 836 | - style_submenu_element("Command-List", "%R/help"); | |
| 865 | + style_submenu_element("Topic-List", "%R/help"); | |
| 866 | + if( search_restrict(SRCH_HELP)!=0 ){ | |
| 867 | + style_submenu_element("Search","%R/search?y=h"); | |
| 868 | + } | |
| 837 | 869 | rc = dispatch_name_search(zCmd, CMDFLAG_ANY|CMDFLAG_PREFIX, &pCmd); |
| 838 | - if( *zCmd=='/' ){ | |
| 870 | + if( pCmd ){ | |
| 871 | + style_header("Help: %s", pCmd->zName); | |
| 872 | + }else{ | |
| 873 | + style_header("Help"); | |
| 874 | + } | |
| 875 | + if( pCmd==0 ){ | |
| 876 | + /* No <h1> line in this case */ | |
| 877 | + }else if( *zCmd=='/' ){ | |
| 839 | 878 | /* Some of the webpages require query parameters in order to work. |
| 840 | 879 | ** @ <h1>The "<a href='%R%s(zCmd)'>%s(zCmd)</a>" page:</h1> */ |
| 841 | - @ <h1>The "%h(zCmd)" page:</h1> | |
| 880 | + @ <h1>The "%h(pCmd->zName)" page:</h1> | |
| 842 | 881 | }else if( rc==0 && (pCmd->eCmdFlags & CMDFLAG_SETTING)!=0 ){ |
| 843 | 882 | @ <h1>The "%h(pCmd->zName)" setting:</h1> |
| 844 | 883 | }else{ |
| 845 | - @ <h1>The "%h(zCmd)" command:</h1> | |
| 884 | + @ <h1>The "%h(pCmd->zName)" command:</h1> | |
| 846 | 885 | } |
| 847 | - if( rc==1 ){ | |
| 848 | - @ unknown command: %h(zCmd) | |
| 886 | + if( rc==1 || (rc==2 && zCmd[0]=='/') ){ | |
| 887 | + if( zCmd && help_is_platform_command(zCmd) ){ | |
| 888 | + @ Not available in this build: "%h(zCmd)" | |
| 889 | + }else{ | |
| 890 | + @ Unknown topic: "%h(zCmd)" | |
| 891 | + } | |
| 849 | 892 | }else if( rc==2 ){ |
| 850 | - @ ambiguous command prefix: %h(zCmd) | |
| 893 | + @ Ambiguous prefix: "%h(zCmd)" | |
| 851 | 894 | }else{ |
| 852 | 895 | if( pCmd->zHelp[0]==0 ){ |
| 853 | 896 | @ No help available for "%h(pCmd->zName)" |
| 854 | 897 | }else if( P("plaintext") ){ |
| 855 | 898 | Blob txt; |
| 856 | 899 | blob_init(&txt, 0, 0); |
| 857 | - help_to_text(pCmd->zHelp, &txt); | |
| 900 | + help_to_text(pCmd->zHelp, &txt, 0); | |
| 858 | 901 | @ <pre class="helpPage"> |
| 859 | 902 | @ %h(blob_str(&txt)) |
| 860 | 903 | @ </pre> |
| 861 | 904 | blob_reset(&txt); |
| 862 | 905 | }else if( P("raw") ){ |
| @@ -873,10 +916,11 @@ | ||
| 873 | 916 | int i; |
| 874 | 917 | const char *zWidth = "28ex"; |
| 875 | 918 | unsigned char occHelp[FOSSIL_MX_CMDIDX] = {0}; /* Help str occurrences */ |
| 876 | 919 | int bktHelp[FOSSIL_MX_CMDIDX][MX_HELP_DUP] = {{0}};/* Help str->commands */ |
| 877 | 920 | style_header("Help"); |
| 921 | + search_screen(SRCH_HELP, 0x02); | |
| 878 | 922 | |
| 879 | 923 | @ <a name='commands'></a> |
| 880 | 924 | @ <h1>Available commands:</h1> |
| 881 | 925 | @ <div class="columns" style="column-width: %s(zWidth);"> |
| 882 | 926 | @ <ul> |
| @@ -1055,10 +1099,229 @@ | ||
| 1055 | 1099 | } |
| 1056 | 1100 | @ </dl> |
| 1057 | 1101 | blob_reset(&buf); |
| 1058 | 1102 | style_finish_page(); |
| 1059 | 1103 | } |
| 1104 | + | |
| 1105 | +/* | |
| 1106 | +** Analyze p and return one of three values: | |
| 1107 | +** | |
| 1108 | +** 0 p is the continuation of a prior subcommand. | |
| 1109 | +** | |
| 1110 | +** 1 p is text past the end of a prior subcommand. | |
| 1111 | +** | |
| 1112 | +** 2 p is the start of a new subcommand. | |
| 1113 | +*/ | |
| 1114 | +static int is_subcommand(Blob *p, int bAbbrevSubcmd){ | |
| 1115 | + int i, sz; | |
| 1116 | + const unsigned char *z = (const unsigned char*)blob_buffer(p); | |
| 1117 | + sz = blob_size(p); | |
| 1118 | + if( sz>6 ) sz = 6; | |
| 1119 | + for(i=0; i<sz && fossil_isspace(z[i]); i++){} | |
| 1120 | + if( i>=sz ) return 0; | |
| 1121 | + | |
| 1122 | + if( bAbbrevSubcmd==0 ){ | |
| 1123 | + if( i>1 ) return 0; | |
| 1124 | + return z[0]=='>' ? 2 : 1; | |
| 1125 | + }else{ | |
| 1126 | + return (i==3 && fossil_isalpha(z[3])) ? 2 : 1; | |
| 1127 | + } | |
| 1128 | +} | |
| 1129 | + | |
| 1130 | +/* | |
| 1131 | +** Input z[] is help text for zTopic. If zTopic has sub-command zSub, | |
| 1132 | +** then cut out all portions of the original help text that do not | |
| 1133 | +** directly pertain to zSub and write the zSub-relevant parts into | |
| 1134 | +** pOut. | |
| 1135 | +** | |
| 1136 | +** Return the number of lines of z[] written into pOut. A return of | |
| 1137 | +** zero means no simplification occurred. | |
| 1138 | +*/ | |
| 1139 | +static int simplify_to_subtopic( | |
| 1140 | + const char *z, /* Full original help text */ | |
| 1141 | + Blob *pOut, /* Write simplified help text here */ | |
| 1142 | + const char *zTopic, /* TOPIC */ | |
| 1143 | + const char *zSubtopic, /* SUBTOPIC */ | |
| 1144 | + int bAbbrevSubcmd /* True if z[] contains abbreviated subcommands */ | |
| 1145 | +){ | |
| 1146 | + Blob in, line; //, subsection; | |
| 1147 | + int n = 0; | |
| 1148 | + char *zQTop = re_quote(zTopic); | |
| 1149 | + char *zQSub = re_quote(zSubtopic); | |
| 1150 | + char *zPattern; | |
| 1151 | + ReCompiled *pRe = 0; | |
| 1152 | + | |
| 1153 | + if( bAbbrevSubcmd ){ | |
| 1154 | + zPattern = mprintf(" ([a-z]+ ?\\| ?)*%s\\b", zQSub); | |
| 1155 | + }else{ | |
| 1156 | + zPattern = mprintf("> ?fossil [-a-z]+ .*\\b%s\\b", zQSub); | |
| 1157 | + } | |
| 1158 | + fossil_free(zQTop); | |
| 1159 | + fossil_free(zQSub); | |
| 1160 | + re_compile(&pRe, zPattern, 0); | |
| 1161 | + fossil_free(zPattern); | |
| 1162 | + blob_init(&in, z, -1); | |
| 1163 | + while( blob_line(&in, &line) ){ | |
| 1164 | + if( re_match(pRe, (unsigned char*)blob_buffer(&line), blob_size(&line)) ){ | |
| 1165 | + int atStart = 1; | |
| 1166 | + blob_appendb(pOut, &line); | |
| 1167 | + n++; | |
| 1168 | + while( blob_line(&in, &line) ){ | |
| 1169 | + if( re_match(pRe,(unsigned char*)blob_buffer(&line),blob_size(&line)) ){ | |
| 1170 | + blob_appendb(pOut, &line); | |
| 1171 | + n++; | |
| 1172 | + atStart = 1; | |
| 1173 | + }else{ | |
| 1174 | + int x = is_subcommand(&line,bAbbrevSubcmd); | |
| 1175 | + if( x==2 ){ | |
| 1176 | + if( atStart ){ | |
| 1177 | + blob_appendb(pOut, &line); | |
| 1178 | + n++; | |
| 1179 | + }else{ | |
| 1180 | + break; | |
| 1181 | + } | |
| 1182 | + }else if( x==1 ){ | |
| 1183 | + break; | |
| 1184 | + }else{ | |
| 1185 | + blob_appendb(pOut, &line); | |
| 1186 | + n++; | |
| 1187 | + atStart = 0; | |
| 1188 | + } | |
| 1189 | + } | |
| 1190 | + } | |
| 1191 | + } | |
| 1192 | + } | |
| 1193 | + blob_reset(&line); | |
| 1194 | + re_free(pRe); | |
| 1195 | + if( n ){ | |
| 1196 | + blob_trim(pOut); | |
| 1197 | + blob_reset(&in); | |
| 1198 | + } | |
| 1199 | + return n; | |
| 1200 | +} | |
| 1201 | + | |
| 1202 | +/* | |
| 1203 | +** Input p is a "Usage:" line or a subcommand line. Simplify this line | |
| 1204 | +** for the --usage option and write it into pOut. | |
| 1205 | +*/ | |
| 1206 | +static void simplify_usage_line( | |
| 1207 | + Blob *p, | |
| 1208 | + Blob *pOut, | |
| 1209 | + int bAbbrevSubcmd, | |
| 1210 | + const char *zCmd | |
| 1211 | +){ | |
| 1212 | + const char *z = blob_buffer(p); | |
| 1213 | + int sz = blob_size(p); | |
| 1214 | + int i = 0; | |
| 1215 | + if( sz>6 && z[0]=='U' ){ | |
| 1216 | + for(i=1; i<sz && !fossil_isspace(z[i]); i++){} | |
| 1217 | + }else if( sz>0 && z[0]=='>' ){ | |
| 1218 | + i = 1; | |
| 1219 | + }else if( sz>4 && bAbbrevSubcmd | |
| 1220 | + && memcmp(z," ",3)==0 && !fossil_isspace(z[3]) ){ | |
| 1221 | + int j; | |
| 1222 | + for(j=3; j<sz-1 && (z[j]!=' ' || z[j+1]!=' '); j++){} | |
| 1223 | + blob_appendf(pOut, "fossil %s %.*s\n", zCmd, j-3, &z[3]); | |
| 1224 | + return; | |
| 1225 | + }else{ | |
| 1226 | + while( i<sz && fossil_isspace(z[i]) ) i++; | |
| 1227 | + if( i+2<sz && (z[i]=='o' || z[i]=='O') && z[i+1]=='r' ){ | |
| 1228 | + while( i<sz && !fossil_isspace(z[i]) ) i++; | |
| 1229 | + } | |
| 1230 | + } | |
| 1231 | + while( i<sz && fossil_isspace(z[i]) ) i++; | |
| 1232 | + blob_append(pOut, &z[i], sz-i); | |
| 1233 | +} | |
| 1234 | + | |
| 1235 | +/* | |
| 1236 | +** Input z[] is help text for a command zTopic. Write into pOut all lines of | |
| 1237 | +** z[] that show the command-line syntax for that command. Lines written | |
| 1238 | +** to pOut are lines that begin with out of: | |
| 1239 | +** | |
| 1240 | +** Usage: | |
| 1241 | +** or: | |
| 1242 | +** > fossil TOPIC | |
| 1243 | +** | |
| 1244 | +** Return the number of lines written into pOut. | |
| 1245 | +*/ | |
| 1246 | +static int simplify_to_usage( | |
| 1247 | + const char *z, /* Full original help text */ | |
| 1248 | + Blob *pOut, /* Write simplified help text here */ | |
| 1249 | + const char *zTopic, /* The command for which z[] is full help text */ | |
| 1250 | + int bAbbrevSubcmd /* z[] uses abbreviated subcommands */ | |
| 1251 | +){ | |
| 1252 | + ReCompiled *pRe = 0; | |
| 1253 | + Blob in, line; | |
| 1254 | + int n = 0; | |
| 1255 | + | |
| 1256 | + if( bAbbrevSubcmd ){ | |
| 1257 | + re_compile(&pRe, "^(Usage: | [a-z][-a-z|]+ .*)", 0); | |
| 1258 | + }else{ | |
| 1259 | + re_compile(&pRe, "^(Usage: | *[Oo]r: +%fossi |> ?fossil )", 0); | |
| 1260 | + } | |
| 1261 | + blob_init(&in, z, -1); | |
| 1262 | + while( blob_line(&in, &line) ){ | |
| 1263 | + if( re_match(pRe, (unsigned char*)blob_buffer(&line), blob_strlen(&line)) ){ | |
| 1264 | + simplify_usage_line(&line, pOut, bAbbrevSubcmd, zTopic); | |
| 1265 | + n++; | |
| 1266 | + } | |
| 1267 | + } | |
| 1268 | + re_free(pRe); | |
| 1269 | + if( n ) blob_trim(pOut); | |
| 1270 | + return n; | |
| 1271 | +} | |
| 1272 | + | |
| 1273 | +/* | |
| 1274 | +** Input z[] is help text. Write into pOut all lines of z[] that show | |
| 1275 | +** command-line options. Return the number of lines written. | |
| 1276 | +*/ | |
| 1277 | +static int simplify_to_options( | |
| 1278 | + const char *z, /* Full original help text */ | |
| 1279 | + Blob *pOut, /* Write simplified help text here */ | |
| 1280 | + int bAbbrevSubcmd, /* z[] uses abbreviated subcommands */ | |
| 1281 | + const char *zCmd /* Name of the command that z[] describes */ | |
| 1282 | +){ | |
| 1283 | + ReCompiled *pRe = 0; | |
| 1284 | + Blob txt, line, subsection; | |
| 1285 | + int n = 0; | |
| 1286 | + int bSubsectionSeen = 0; | |
| 1287 | + | |
| 1288 | + blob_init(&txt, z, -1); | |
| 1289 | + blob_init(&subsection, 0, 0); | |
| 1290 | + re_compile(&pRe, "^ +-.* ", 0); | |
| 1291 | + while( blob_line(&txt, &line) ){ | |
| 1292 | + int len = blob_size(&line); | |
| 1293 | + unsigned char *zLine = (unsigned char *)blob_buffer(&line); | |
| 1294 | + if( re_match(pRe, zLine, len) ){ | |
| 1295 | + if( blob_size(&subsection) ){ | |
| 1296 | + simplify_usage_line(&subsection, pOut, bAbbrevSubcmd, zCmd); | |
| 1297 | + blob_reset(&subsection); | |
| 1298 | + } | |
| 1299 | + blob_appendb(pOut, &line); | |
| 1300 | + }else if( len>7 && !fossil_isspace(zLine[0]) && bSubsectionSeen | |
| 1301 | + && sqlite3_strlike("%options:%",blob_str(&line),0)==0 ){ | |
| 1302 | + subsection = line; | |
| 1303 | + }else if( !bAbbrevSubcmd && len>9 | |
| 1304 | + && (memcmp(zLine,"> fossil ",9)==0 | |
| 1305 | + || memcmp(zLine,"> fossil",9)==0) ){ | |
| 1306 | + subsection = line; | |
| 1307 | + bSubsectionSeen = 1; | |
| 1308 | + }else if( bAbbrevSubcmd && len>5 && memcmp(zLine," ",3)==0 | |
| 1309 | + && fossil_isalpha(zLine[3]) ){ | |
| 1310 | + subsection = line; | |
| 1311 | + bSubsectionSeen = 1; | |
| 1312 | + }else if( len>1 && !fossil_isspace(zLine[0]) && bSubsectionSeen ){ | |
| 1313 | + blob_reset(&subsection); | |
| 1314 | + } | |
| 1315 | + } | |
| 1316 | + re_free(pRe); | |
| 1317 | + blob_trim(pOut); | |
| 1318 | + blob_reset(&subsection); | |
| 1319 | + return n; | |
| 1320 | +} | |
| 1321 | + | |
| 1322 | + | |
| 1060 | 1323 | |
| 1061 | 1324 | static void multi_column_list(const char **azWord, int nWord){ |
| 1062 | 1325 | int i, j, len; |
| 1063 | 1326 | int mxLen = 0; |
| 1064 | 1327 | int nCol; |
| @@ -1125,12 +1388,10 @@ | ||
| 1125 | 1388 | @ |
| 1126 | 1389 | @ --args FILENAME Read additional arguments and options from FILENAME |
| 1127 | 1390 | @ --case-sensitive BOOL Set case sensitivity for file names |
| 1128 | 1391 | @ --cgitrace Active CGI tracing |
| 1129 | 1392 | @ --chdir PATH Change to PATH before performing any operations |
| 1130 | -@ --comfmtflags VALUE Set comment formatting flags to VALUE | |
| 1131 | -@ --comment-format VALUE Alias for --comfmtflags | |
| 1132 | 1393 | @ --errorlog FILENAME Log errors to FILENAME |
| 1133 | 1394 | @ --help Show help on the command rather than running it |
| 1134 | 1395 | @ --httptrace Trace outbound HTTP requests |
| 1135 | 1396 | @ --localtime Display times using the local timezone |
| 1136 | 1397 | @ --nocgi Do not act as CGI |
| @@ -1147,55 +1408,68 @@ | ||
| 1147 | 1408 | ; |
| 1148 | 1409 | |
| 1149 | 1410 | /* |
| 1150 | 1411 | ** COMMAND: help |
| 1151 | 1412 | ** |
| 1152 | -** Usage: %fossil help [OPTIONS] [TOPIC] | |
| 1413 | +** Usage: %fossil help [OPTIONS] [TOPIC] [SUBCOMMAND] | |
| 1153 | 1414 | ** |
| 1154 | 1415 | ** Display information on how to use TOPIC, which may be a command, webpage, or |
| 1155 | 1416 | ** setting. Webpage names begin with "/". If TOPIC is omitted, a list of |
| 1156 | -** topics is returned. | |
| 1417 | +** topics is returned. If there is an extra argument after TOPIC, it is | |
| 1418 | +** the name of a subcommand, in which case only the help text for that one | |
| 1419 | +** subcommand is shown. | |
| 1157 | 1420 | ** |
| 1158 | 1421 | ** The following options can be used when TOPIC is omitted: |
| 1159 | 1422 | ** |
| 1160 | 1423 | ** -a|--all List both common and auxiliary commands |
| 1424 | +** -e|--everything List all help on all topics | |
| 1425 | +** -f|--full List full set of commands (including auxiliary | |
| 1426 | +** and unsupported "test" commands), options, | |
| 1427 | +** settings, and web pages | |
| 1161 | 1428 | ** -o|--options List command-line options common to all commands |
| 1162 | 1429 | ** -s|--setting List setting names |
| 1163 | 1430 | ** -t|--test List unsupported "test" commands |
| 1164 | 1431 | ** -v|--verbose List both names and help text |
| 1165 | 1432 | ** -x|--aux List only auxiliary commands |
| 1166 | 1433 | ** -w|--www List all web pages |
| 1167 | -** -f|--full List full set of commands (including auxiliary | |
| 1168 | -** and unsupported "test" commands), options, | |
| 1169 | -** settings, and web pages | |
| 1170 | -** -e|--everything List all help on all topics | |
| 1171 | 1434 | ** |
| 1172 | 1435 | ** These options can be used when TOPIC is present: |
| 1173 | 1436 | ** |
| 1174 | -** -h|--html Format output as HTML rather than plain text | |
| 1175 | 1437 | ** -c|--commands Restrict TOPIC search to commands |
| 1438 | +** -h|--html Format output as HTML rather than plain text | |
| 1439 | +** -o|--options Show command-line options for TOPIC | |
| 1440 | +** --raw Output raw, unformatted help text | |
| 1441 | +** -u|--usage Show a succinct usage summary, not full help text | |
| 1442 | +** | |
| 1443 | +** See also: [[usage]], [[options]], [[search]] with the -h option | |
| 1176 | 1444 | */ |
| 1177 | 1445 | void help_cmd(void){ |
| 1178 | 1446 | int rc; |
| 1179 | - int mask = CMDFLAG_ANY; | |
| 1180 | - int isPage = 0; | |
| 1181 | - int verboseFlag = 0; | |
| 1182 | - int commandsFlag = 0; | |
| 1183 | - const char *z; | |
| 1184 | - const char *zCmdOrPage; | |
| 1185 | - const CmdOrPage *pCmd = 0; | |
| 1186 | - int useHtml = 0; | |
| 1187 | - const char *zTopic; | |
| 1188 | - Blob txt; | |
| 1447 | + int mask = CMDFLAG_ANY; /* Mask of help topic types */ | |
| 1448 | + int isPage = 0; /* True if TOPIC is a page */ | |
| 1449 | + int verboseFlag = 0; /* -v option */ | |
| 1450 | + int commandsFlag = 0; /* -c option */ | |
| 1451 | + const char *z; /* Original, untranslated help text */ | |
| 1452 | + const char *zCmdOrPage; /* "command" or "page" or "setting" */ | |
| 1453 | + const CmdOrPage *pCmd = 0; /* ptr to aCommand[] entry for TOPIC */ | |
| 1454 | + int useHtml = 0; /* -h option */ | |
| 1455 | + int bUsage; /* --usage */ | |
| 1456 | + int bRaw; /* --raw option */ | |
| 1457 | + int bOptions; /* --options */ | |
| 1458 | + const char *zTopic; /* TOPIC argument */ | |
| 1459 | + const char *zSubtopic = 0; /* SUBTOPIC argument */ | |
| 1460 | + Blob subtext1, subtext2, s3; /* Subsets of z[] containing subtopic/usage */ | |
| 1461 | + Blob txt; /* Text after rendering */ | |
| 1462 | + int bAbbrevSubcmd = 0; /* Help text uses abbreviated subcommands */ | |
| 1463 | + | |
| 1189 | 1464 | verboseFlag = find_option("verbose","v",0)!=0; |
| 1190 | 1465 | commandsFlag = find_option("commands","c",0)!=0; |
| 1191 | 1466 | useHtml = find_option("html","h",0)!=0; |
| 1192 | - if( find_option("options","o",0) ){ | |
| 1193 | - fossil_print("%s", zOptions); | |
| 1194 | - return; | |
| 1195 | - } | |
| 1196 | - else if( find_option("all","a",0) ){ | |
| 1467 | + bRaw = find_option("raw",0,0)!=0; | |
| 1468 | + bOptions = find_option("options","o",0)!=0; | |
| 1469 | + bUsage = find_option("usage","u",0)!=0; | |
| 1470 | + if( find_option("all","a",0) ){ | |
| 1197 | 1471 | command_list(CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER, verboseFlag, useHtml); |
| 1198 | 1472 | return; |
| 1199 | 1473 | } |
| 1200 | 1474 | else if( find_option("www","w",0) ){ |
| 1201 | 1475 | command_list(CMDFLAG_WEBPAGE, verboseFlag, useHtml); |
| @@ -1223,34 +1497,45 @@ | ||
| 1223 | 1497 | command_list(CMDFLAG_SETTING, verboseFlag, useHtml); |
| 1224 | 1498 | fossil_print("\nfossil web pages:\n\n"); |
| 1225 | 1499 | command_list(CMDFLAG_WEBPAGE, verboseFlag, useHtml); |
| 1226 | 1500 | fossil_print("\nfossil test commands (unsupported):\n\n"); |
| 1227 | 1501 | command_list(CMDFLAG_TEST, verboseFlag, useHtml); |
| 1228 | - fossil_print("\n"); | |
| 1229 | - version_cmd(); | |
| 1502 | + if ( !verboseFlag ) { | |
| 1503 | + fossil_print("\n"); | |
| 1504 | + version_cmd(); | |
| 1505 | + } | |
| 1230 | 1506 | return; |
| 1231 | 1507 | } |
| 1232 | 1508 | else if( find_option("everything","e",0) ){ |
| 1233 | 1509 | display_all_help(CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER | CMDFLAG_WEBPAGE | |
| 1234 | 1510 | CMDFLAG_SETTING | CMDFLAG_TEST, useHtml, 0); |
| 1235 | 1511 | return; |
| 1236 | 1512 | } |
| 1237 | 1513 | verify_all_options(); |
| 1238 | 1514 | if( g.argc<3 ){ |
| 1515 | + if( bOptions ){ | |
| 1516 | + fossil_print("%s", zOptions); | |
| 1517 | + return; | |
| 1518 | + } | |
| 1239 | 1519 | z = g.argv[0]; |
| 1240 | 1520 | fossil_print( |
| 1241 | 1521 | "Usage: %s help TOPIC\n" |
| 1242 | - "Try \"%s help help\" or \"%s help -a\" for more options\n" | |
| 1243 | - "Frequently used commands:\n", | |
| 1244 | - z, z, z); | |
| 1522 | + "Things to try:\n\n" | |
| 1523 | + " %s help help\n" | |
| 1524 | + " %s help -o\n" | |
| 1525 | + " %s help -a\n" | |
| 1526 | + " %s search -h TOPIC\n\n" | |
| 1527 | + "Other common values for TOPIC:\n\n", | |
| 1528 | + z, z, z, z, z); | |
| 1245 | 1529 | command_list(CMDFLAG_1ST_TIER,verboseFlag,useHtml); |
| 1246 | 1530 | if( !verboseFlag ) version_cmd(); |
| 1247 | 1531 | return; |
| 1248 | 1532 | } |
| 1249 | 1533 | zTopic = g.argv[2]; |
| 1534 | + zSubtopic = g.argc>=4 ? g.argv[3] : 0; | |
| 1250 | 1535 | isPage = ('/' == zTopic[0]) ? 1 : 0; |
| 1251 | - if(isPage){ | |
| 1536 | + if( isPage ){ | |
| 1252 | 1537 | zCmdOrPage = "page"; |
| 1253 | 1538 | }else if( commandsFlag ){ |
| 1254 | 1539 | mask = CMDFLAG_COMMAND; |
| 1255 | 1540 | zCmdOrPage = "command"; |
| 1256 | 1541 | }else{ |
| @@ -1259,10 +1544,14 @@ | ||
| 1259 | 1544 | rc = dispatch_name_search(g.argv[2], mask|CMDFLAG_PREFIX, &pCmd); |
| 1260 | 1545 | if( rc ){ |
| 1261 | 1546 | int i, n; |
| 1262 | 1547 | const char *az[5]; |
| 1263 | 1548 | if( rc==1 ){ |
| 1549 | + if( help_is_platform_command(g.argv[2]) ){ | |
| 1550 | + fossil_print("Not available in this build: %s\n", g.argv[2]); | |
| 1551 | + return; | |
| 1552 | + } | |
| 1264 | 1553 | fossil_print("unknown %s: %s\n", zCmdOrPage, g.argv[2]); |
| 1265 | 1554 | }else{ |
| 1266 | 1555 | fossil_print("ambiguous %s prefix: %s\n", |
| 1267 | 1556 | zCmdOrPage, g.argv[2]); |
| 1268 | 1557 | } |
| @@ -1269,24 +1558,48 @@ | ||
| 1269 | 1558 | fossil_print("Did you mean one of these TOPICs:\n"); |
| 1270 | 1559 | n = dispatch_approx_match(g.argv[2], 5, az); |
| 1271 | 1560 | for(i=0; i<n; i++){ |
| 1272 | 1561 | fossil_print(" * %s\n", az[i]); |
| 1273 | 1562 | } |
| 1274 | - fossil_print("Also consider using:\n"); | |
| 1275 | - fossil_print(" fossil help TOPIC ;# show help on TOPIC\n"); | |
| 1276 | - fossil_print(" fossil help -a ;# show all commands\n"); | |
| 1277 | - fossil_print(" fossil help -w ;# show all web-pages\n"); | |
| 1278 | - fossil_print(" fossil help -s ;# show all settings\n"); | |
| 1279 | - fossil_print(" fossil help -o ;# show global options\n"); | |
| 1280 | - fossil_exit(1); | |
| 1281 | - } | |
| 1563 | + fossil_print("Other commands to try:\n"); | |
| 1564 | + fossil_print(" fossil search -h PATTERN ;# search all help text\n"); | |
| 1565 | + fossil_print(" fossil help -a ;# show all commands\n"); | |
| 1566 | + fossil_print(" fossil help -w ;# show all web-pages\n"); | |
| 1567 | + fossil_print(" fossil help -s ;# show all settings\n"); | |
| 1568 | + fossil_print(" fossil help -o ;# show global options\n"); | |
| 1569 | + return; | |
| 1570 | + } | |
| 1571 | + bAbbrevSubcmd = (pCmd->eCmdFlags & CMDFLAG_ABBREVSUBCMD)!=0; | |
| 1282 | 1572 | z = pCmd->zHelp; |
| 1283 | 1573 | if( z==0 ){ |
| 1284 | 1574 | fossil_fatal("no help available for the %s %s", |
| 1285 | 1575 | pCmd->zName, zCmdOrPage); |
| 1286 | 1576 | } |
| 1287 | - if( pCmd->eCmdFlags & CMDFLAG_SETTING ){ | |
| 1577 | + blob_init(&subtext1, 0, 0); | |
| 1578 | + blob_init(&subtext2, 0, 0); | |
| 1579 | + blob_init(&s3, 0, 0); | |
| 1580 | + if( zSubtopic!=0 ){ | |
| 1581 | + if( simplify_to_subtopic(z, &subtext1, zTopic, zSubtopic, bAbbrevSubcmd) ){ | |
| 1582 | + z = blob_str(&subtext1); | |
| 1583 | + }else{ | |
| 1584 | + fossil_print("No subtopic \"%s\" for \"%s\".\n", zSubtopic, zTopic); | |
| 1585 | + bUsage = 1; | |
| 1586 | + zSubtopic = 0; | |
| 1587 | + } | |
| 1588 | + } | |
| 1589 | + if( bUsage ){ | |
| 1590 | + if( simplify_to_usage(z, &subtext2, zTopic, bAbbrevSubcmd) ){ | |
| 1591 | + z = blob_str(&subtext2); | |
| 1592 | + }else{ | |
| 1593 | + bUsage = 0; | |
| 1594 | + } | |
| 1595 | + } | |
| 1596 | + if( bOptions ){ | |
| 1597 | + simplify_to_options(z, &s3, bAbbrevSubcmd, zTopic); | |
| 1598 | + z = blob_str(&s3); | |
| 1599 | + } | |
| 1600 | + if( pCmd && pCmd->eCmdFlags & CMDFLAG_SETTING ){ | |
| 1288 | 1601 | const Setting *pSetting = db_find_setting(pCmd->zName, 0); |
| 1289 | 1602 | char *zDflt = 0; |
| 1290 | 1603 | if( pSetting!=0 && pSetting->def!=0 && *pSetting->def!=0 ){ |
| 1291 | 1604 | zDflt = mprintf(" (default: %s)", pSetting->def); |
| 1292 | 1605 | } |
| @@ -1295,17 +1608,21 @@ | ||
| 1295 | 1608 | (pCmd->eCmdFlags & CMDFLAG_VERSIONABLE)!=0 ? " (versionable)" : "" |
| 1296 | 1609 | ); |
| 1297 | 1610 | fossil_free(zDflt); |
| 1298 | 1611 | } |
| 1299 | 1612 | blob_init(&txt, 0, 0); |
| 1300 | - if( useHtml ){ | |
| 1613 | + if( bRaw ){ | |
| 1614 | + blob_append(&txt, z, -1); | |
| 1615 | + }else if( useHtml ){ | |
| 1301 | 1616 | help_to_html(z, &txt); |
| 1302 | 1617 | }else{ |
| 1303 | - help_to_text(z, &txt); | |
| 1618 | + help_to_text(z, &txt, bUsage || zSubtopic!=0); | |
| 1304 | 1619 | } |
| 1305 | - fossil_print("%s\n", blob_str(&txt)); | |
| 1620 | + if( blob_strlen(&txt)>0 ) fossil_print("%s\n", blob_str(&txt)); | |
| 1306 | 1621 | blob_reset(&txt); |
| 1622 | + blob_reset(&subtext1); | |
| 1623 | + blob_reset(&subtext2); | |
| 1307 | 1624 | } |
| 1308 | 1625 | |
| 1309 | 1626 | /* |
| 1310 | 1627 | ** Return a pointer to the setting information array. |
| 1311 | 1628 | ** |
| @@ -1477,11 +1794,11 @@ | ||
| 1477 | 1794 | sqlite3_result_text(ctx, pPage->zHelp, -1, SQLITE_STATIC); |
| 1478 | 1795 | break; |
| 1479 | 1796 | case 4: { /* formatted */ |
| 1480 | 1797 | Blob txt; |
| 1481 | 1798 | blob_init(&txt, 0, 0); |
| 1482 | - help_to_text(pPage->zHelp, &txt); | |
| 1799 | + help_to_text(pPage->zHelp, &txt, 0); | |
| 1483 | 1800 | sqlite3_result_text(ctx, blob_str(&txt), -1, fossil_free); |
| 1484 | 1801 | break; |
| 1485 | 1802 | } |
| 1486 | 1803 | case 5: { /* formatted */ |
| 1487 | 1804 | Blob txt; |
| 1488 | 1805 |
| --- src/dispatch.c | |
| +++ src/dispatch.c | |
| @@ -54,10 +54,11 @@ | |
| 54 | /* NOTE: 0x0400 = CMDFLAG_SENSITIVE in mkindex.c! */ |
| 55 | #define CMDFLAG_HIDDEN 0x0800 /* Elide from most listings */ |
| 56 | #define CMDFLAG_LDAVG_EXEMPT 0x1000 /* Exempt from load_control() */ |
| 57 | #define CMDFLAG_ALIAS 0x2000 /* Command aliases */ |
| 58 | #define CMDFLAG_KEEPEMPTY 0x4000 /* Do not unset empty settings */ |
| 59 | /**************************************************************************/ |
| 60 | |
| 61 | /* Values for the 2nd parameter to dispatch_name_search() */ |
| 62 | #define CMDFLAG_ANY 0x0038 /* Match anything */ |
| 63 | #define CMDFLAG_PREFIX 0x0200 /* Prefix match is ok */ |
| @@ -522,13 +523,21 @@ | |
| 522 | } |
| 523 | |
| 524 | /* |
| 525 | ** Format help text for TTY display. |
| 526 | */ |
| 527 | static void help_to_text(const char *zHelp, Blob *pText){ |
| 528 | int i, x; |
| 529 | char c; |
| 530 | for(i=0; (c = zHelp[i])!=0; i++){ |
| 531 | if( c=='%' && strncmp(zHelp+i,"%fossil",7)==0 ){ |
| 532 | if( i>0 ) blob_append(pText, zHelp, i); |
| 533 | blob_append(pText, "fossil", 6); |
| 534 | zHelp += i+7; |
| @@ -603,11 +612,11 @@ | |
| 603 | fossil_print("# %s\n", aCommand[bktHelp[aCommand[i].iHelp][j]].zName); |
| 604 | fossil_print("%s\n\n", aCommand[i].zHelp); |
| 605 | }else{ |
| 606 | Blob txt; |
| 607 | blob_init(&txt, 0, 0); |
| 608 | help_to_text(aCommand[i].zHelp, &txt); |
| 609 | for(j=0; j<occHelp[aCommand[i].iHelp]; j++){ |
| 610 | fossil_print("# %s%s\n", |
| 611 | aCommand[bktHelp[aCommand[i].iHelp][j]].zName, |
| 612 | (aCommand[i].eCmdFlags & CMDFLAG_VERSIONABLE)!=0 ? |
| 613 | " (versionable)" : ""); |
| @@ -802,10 +811,32 @@ | |
| 802 | fossil_print(" %s\n", az[j]); |
| 803 | } |
| 804 | } |
| 805 | } |
| 806 | |
| 807 | /* |
| 808 | ** WEBPAGE: help |
| 809 | ** URL: /help?name=CMD |
| 810 | ** |
| 811 | ** Show the built-in help text for CMD. CMD can be a command-line interface |
| @@ -829,34 +860,46 @@ | |
| 829 | if( zCmd && *zCmd ){ |
| 830 | int rc; |
| 831 | const CmdOrPage *pCmd = 0; |
| 832 | |
| 833 | style_set_current_feature("tkt"); |
| 834 | style_header("Help: %s", zCmd); |
| 835 | |
| 836 | style_submenu_element("Command-List", "%R/help"); |
| 837 | rc = dispatch_name_search(zCmd, CMDFLAG_ANY|CMDFLAG_PREFIX, &pCmd); |
| 838 | if( *zCmd=='/' ){ |
| 839 | /* Some of the webpages require query parameters in order to work. |
| 840 | ** @ <h1>The "<a href='%R%s(zCmd)'>%s(zCmd)</a>" page:</h1> */ |
| 841 | @ <h1>The "%h(zCmd)" page:</h1> |
| 842 | }else if( rc==0 && (pCmd->eCmdFlags & CMDFLAG_SETTING)!=0 ){ |
| 843 | @ <h1>The "%h(pCmd->zName)" setting:</h1> |
| 844 | }else{ |
| 845 | @ <h1>The "%h(zCmd)" command:</h1> |
| 846 | } |
| 847 | if( rc==1 ){ |
| 848 | @ unknown command: %h(zCmd) |
| 849 | }else if( rc==2 ){ |
| 850 | @ ambiguous command prefix: %h(zCmd) |
| 851 | }else{ |
| 852 | if( pCmd->zHelp[0]==0 ){ |
| 853 | @ No help available for "%h(pCmd->zName)" |
| 854 | }else if( P("plaintext") ){ |
| 855 | Blob txt; |
| 856 | blob_init(&txt, 0, 0); |
| 857 | help_to_text(pCmd->zHelp, &txt); |
| 858 | @ <pre class="helpPage"> |
| 859 | @ %h(blob_str(&txt)) |
| 860 | @ </pre> |
| 861 | blob_reset(&txt); |
| 862 | }else if( P("raw") ){ |
| @@ -873,10 +916,11 @@ | |
| 873 | int i; |
| 874 | const char *zWidth = "28ex"; |
| 875 | unsigned char occHelp[FOSSIL_MX_CMDIDX] = {0}; /* Help str occurrences */ |
| 876 | int bktHelp[FOSSIL_MX_CMDIDX][MX_HELP_DUP] = {{0}};/* Help str->commands */ |
| 877 | style_header("Help"); |
| 878 | |
| 879 | @ <a name='commands'></a> |
| 880 | @ <h1>Available commands:</h1> |
| 881 | @ <div class="columns" style="column-width: %s(zWidth);"> |
| 882 | @ <ul> |
| @@ -1055,10 +1099,229 @@ | |
| 1055 | } |
| 1056 | @ </dl> |
| 1057 | blob_reset(&buf); |
| 1058 | style_finish_page(); |
| 1059 | } |
| 1060 | |
| 1061 | static void multi_column_list(const char **azWord, int nWord){ |
| 1062 | int i, j, len; |
| 1063 | int mxLen = 0; |
| 1064 | int nCol; |
| @@ -1125,12 +1388,10 @@ | |
| 1125 | @ |
| 1126 | @ --args FILENAME Read additional arguments and options from FILENAME |
| 1127 | @ --case-sensitive BOOL Set case sensitivity for file names |
| 1128 | @ --cgitrace Active CGI tracing |
| 1129 | @ --chdir PATH Change to PATH before performing any operations |
| 1130 | @ --comfmtflags VALUE Set comment formatting flags to VALUE |
| 1131 | @ --comment-format VALUE Alias for --comfmtflags |
| 1132 | @ --errorlog FILENAME Log errors to FILENAME |
| 1133 | @ --help Show help on the command rather than running it |
| 1134 | @ --httptrace Trace outbound HTTP requests |
| 1135 | @ --localtime Display times using the local timezone |
| 1136 | @ --nocgi Do not act as CGI |
| @@ -1147,55 +1408,68 @@ | |
| 1147 | ; |
| 1148 | |
| 1149 | /* |
| 1150 | ** COMMAND: help |
| 1151 | ** |
| 1152 | ** Usage: %fossil help [OPTIONS] [TOPIC] |
| 1153 | ** |
| 1154 | ** Display information on how to use TOPIC, which may be a command, webpage, or |
| 1155 | ** setting. Webpage names begin with "/". If TOPIC is omitted, a list of |
| 1156 | ** topics is returned. |
| 1157 | ** |
| 1158 | ** The following options can be used when TOPIC is omitted: |
| 1159 | ** |
| 1160 | ** -a|--all List both common and auxiliary commands |
| 1161 | ** -o|--options List command-line options common to all commands |
| 1162 | ** -s|--setting List setting names |
| 1163 | ** -t|--test List unsupported "test" commands |
| 1164 | ** -v|--verbose List both names and help text |
| 1165 | ** -x|--aux List only auxiliary commands |
| 1166 | ** -w|--www List all web pages |
| 1167 | ** -f|--full List full set of commands (including auxiliary |
| 1168 | ** and unsupported "test" commands), options, |
| 1169 | ** settings, and web pages |
| 1170 | ** -e|--everything List all help on all topics |
| 1171 | ** |
| 1172 | ** These options can be used when TOPIC is present: |
| 1173 | ** |
| 1174 | ** -h|--html Format output as HTML rather than plain text |
| 1175 | ** -c|--commands Restrict TOPIC search to commands |
| 1176 | */ |
| 1177 | void help_cmd(void){ |
| 1178 | int rc; |
| 1179 | int mask = CMDFLAG_ANY; |
| 1180 | int isPage = 0; |
| 1181 | int verboseFlag = 0; |
| 1182 | int commandsFlag = 0; |
| 1183 | const char *z; |
| 1184 | const char *zCmdOrPage; |
| 1185 | const CmdOrPage *pCmd = 0; |
| 1186 | int useHtml = 0; |
| 1187 | const char *zTopic; |
| 1188 | Blob txt; |
| 1189 | verboseFlag = find_option("verbose","v",0)!=0; |
| 1190 | commandsFlag = find_option("commands","c",0)!=0; |
| 1191 | useHtml = find_option("html","h",0)!=0; |
| 1192 | if( find_option("options","o",0) ){ |
| 1193 | fossil_print("%s", zOptions); |
| 1194 | return; |
| 1195 | } |
| 1196 | else if( find_option("all","a",0) ){ |
| 1197 | command_list(CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER, verboseFlag, useHtml); |
| 1198 | return; |
| 1199 | } |
| 1200 | else if( find_option("www","w",0) ){ |
| 1201 | command_list(CMDFLAG_WEBPAGE, verboseFlag, useHtml); |
| @@ -1223,34 +1497,45 @@ | |
| 1223 | command_list(CMDFLAG_SETTING, verboseFlag, useHtml); |
| 1224 | fossil_print("\nfossil web pages:\n\n"); |
| 1225 | command_list(CMDFLAG_WEBPAGE, verboseFlag, useHtml); |
| 1226 | fossil_print("\nfossil test commands (unsupported):\n\n"); |
| 1227 | command_list(CMDFLAG_TEST, verboseFlag, useHtml); |
| 1228 | fossil_print("\n"); |
| 1229 | version_cmd(); |
| 1230 | return; |
| 1231 | } |
| 1232 | else if( find_option("everything","e",0) ){ |
| 1233 | display_all_help(CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER | CMDFLAG_WEBPAGE | |
| 1234 | CMDFLAG_SETTING | CMDFLAG_TEST, useHtml, 0); |
| 1235 | return; |
| 1236 | } |
| 1237 | verify_all_options(); |
| 1238 | if( g.argc<3 ){ |
| 1239 | z = g.argv[0]; |
| 1240 | fossil_print( |
| 1241 | "Usage: %s help TOPIC\n" |
| 1242 | "Try \"%s help help\" or \"%s help -a\" for more options\n" |
| 1243 | "Frequently used commands:\n", |
| 1244 | z, z, z); |
| 1245 | command_list(CMDFLAG_1ST_TIER,verboseFlag,useHtml); |
| 1246 | if( !verboseFlag ) version_cmd(); |
| 1247 | return; |
| 1248 | } |
| 1249 | zTopic = g.argv[2]; |
| 1250 | isPage = ('/' == zTopic[0]) ? 1 : 0; |
| 1251 | if(isPage){ |
| 1252 | zCmdOrPage = "page"; |
| 1253 | }else if( commandsFlag ){ |
| 1254 | mask = CMDFLAG_COMMAND; |
| 1255 | zCmdOrPage = "command"; |
| 1256 | }else{ |
| @@ -1259,10 +1544,14 @@ | |
| 1259 | rc = dispatch_name_search(g.argv[2], mask|CMDFLAG_PREFIX, &pCmd); |
| 1260 | if( rc ){ |
| 1261 | int i, n; |
| 1262 | const char *az[5]; |
| 1263 | if( rc==1 ){ |
| 1264 | fossil_print("unknown %s: %s\n", zCmdOrPage, g.argv[2]); |
| 1265 | }else{ |
| 1266 | fossil_print("ambiguous %s prefix: %s\n", |
| 1267 | zCmdOrPage, g.argv[2]); |
| 1268 | } |
| @@ -1269,24 +1558,48 @@ | |
| 1269 | fossil_print("Did you mean one of these TOPICs:\n"); |
| 1270 | n = dispatch_approx_match(g.argv[2], 5, az); |
| 1271 | for(i=0; i<n; i++){ |
| 1272 | fossil_print(" * %s\n", az[i]); |
| 1273 | } |
| 1274 | fossil_print("Also consider using:\n"); |
| 1275 | fossil_print(" fossil help TOPIC ;# show help on TOPIC\n"); |
| 1276 | fossil_print(" fossil help -a ;# show all commands\n"); |
| 1277 | fossil_print(" fossil help -w ;# show all web-pages\n"); |
| 1278 | fossil_print(" fossil help -s ;# show all settings\n"); |
| 1279 | fossil_print(" fossil help -o ;# show global options\n"); |
| 1280 | fossil_exit(1); |
| 1281 | } |
| 1282 | z = pCmd->zHelp; |
| 1283 | if( z==0 ){ |
| 1284 | fossil_fatal("no help available for the %s %s", |
| 1285 | pCmd->zName, zCmdOrPage); |
| 1286 | } |
| 1287 | if( pCmd->eCmdFlags & CMDFLAG_SETTING ){ |
| 1288 | const Setting *pSetting = db_find_setting(pCmd->zName, 0); |
| 1289 | char *zDflt = 0; |
| 1290 | if( pSetting!=0 && pSetting->def!=0 && *pSetting->def!=0 ){ |
| 1291 | zDflt = mprintf(" (default: %s)", pSetting->def); |
| 1292 | } |
| @@ -1295,17 +1608,21 @@ | |
| 1295 | (pCmd->eCmdFlags & CMDFLAG_VERSIONABLE)!=0 ? " (versionable)" : "" |
| 1296 | ); |
| 1297 | fossil_free(zDflt); |
| 1298 | } |
| 1299 | blob_init(&txt, 0, 0); |
| 1300 | if( useHtml ){ |
| 1301 | help_to_html(z, &txt); |
| 1302 | }else{ |
| 1303 | help_to_text(z, &txt); |
| 1304 | } |
| 1305 | fossil_print("%s\n", blob_str(&txt)); |
| 1306 | blob_reset(&txt); |
| 1307 | } |
| 1308 | |
| 1309 | /* |
| 1310 | ** Return a pointer to the setting information array. |
| 1311 | ** |
| @@ -1477,11 +1794,11 @@ | |
| 1477 | sqlite3_result_text(ctx, pPage->zHelp, -1, SQLITE_STATIC); |
| 1478 | break; |
| 1479 | case 4: { /* formatted */ |
| 1480 | Blob txt; |
| 1481 | blob_init(&txt, 0, 0); |
| 1482 | help_to_text(pPage->zHelp, &txt); |
| 1483 | sqlite3_result_text(ctx, blob_str(&txt), -1, fossil_free); |
| 1484 | break; |
| 1485 | } |
| 1486 | case 5: { /* formatted */ |
| 1487 | Blob txt; |
| 1488 |
| --- src/dispatch.c | |
| +++ src/dispatch.c | |
| @@ -54,10 +54,11 @@ | |
| 54 | /* NOTE: 0x0400 = CMDFLAG_SENSITIVE in mkindex.c! */ |
| 55 | #define CMDFLAG_HIDDEN 0x0800 /* Elide from most listings */ |
| 56 | #define CMDFLAG_LDAVG_EXEMPT 0x1000 /* Exempt from load_control() */ |
| 57 | #define CMDFLAG_ALIAS 0x2000 /* Command aliases */ |
| 58 | #define CMDFLAG_KEEPEMPTY 0x4000 /* Do not unset empty settings */ |
| 59 | #define CMDFLAG_ABBREVSUBCMD 0x8000 /* Help text abbreviates subcommands */ |
| 60 | /**************************************************************************/ |
| 61 | |
| 62 | /* Values for the 2nd parameter to dispatch_name_search() */ |
| 63 | #define CMDFLAG_ANY 0x0038 /* Match anything */ |
| 64 | #define CMDFLAG_PREFIX 0x0200 /* Prefix match is ok */ |
| @@ -522,13 +523,21 @@ | |
| 523 | } |
| 524 | |
| 525 | /* |
| 526 | ** Format help text for TTY display. |
| 527 | */ |
| 528 | static void help_to_text(const char *zHelp, Blob *pText, int bUsage){ |
| 529 | int i, x; |
| 530 | char c; |
| 531 | if( zHelp[0]=='>' ){ |
| 532 | if( !bUsage ){ |
| 533 | blob_appendf(pText, "Usage:"); |
| 534 | }else{ |
| 535 | blob_append_char(pText, ' '); |
| 536 | } |
| 537 | zHelp++; |
| 538 | } |
| 539 | for(i=0; (c = zHelp[i])!=0; i++){ |
| 540 | if( c=='%' && strncmp(zHelp+i,"%fossil",7)==0 ){ |
| 541 | if( i>0 ) blob_append(pText, zHelp, i); |
| 542 | blob_append(pText, "fossil", 6); |
| 543 | zHelp += i+7; |
| @@ -603,11 +612,11 @@ | |
| 612 | fossil_print("# %s\n", aCommand[bktHelp[aCommand[i].iHelp][j]].zName); |
| 613 | fossil_print("%s\n\n", aCommand[i].zHelp); |
| 614 | }else{ |
| 615 | Blob txt; |
| 616 | blob_init(&txt, 0, 0); |
| 617 | help_to_text(aCommand[i].zHelp, &txt, 0); |
| 618 | for(j=0; j<occHelp[aCommand[i].iHelp]; j++){ |
| 619 | fossil_print("# %s%s\n", |
| 620 | aCommand[bktHelp[aCommand[i].iHelp][j]].zName, |
| 621 | (aCommand[i].eCmdFlags & CMDFLAG_VERSIONABLE)!=0 ? |
| 622 | " (versionable)" : ""); |
| @@ -802,10 +811,32 @@ | |
| 811 | fossil_print(" %s\n", az[j]); |
| 812 | } |
| 813 | } |
| 814 | } |
| 815 | |
| 816 | |
| 817 | /* |
| 818 | ** Returns 1 if the command or page name zName is known to be a |
| 819 | ** command/page which is only available in certain builds/platforms, |
| 820 | ** else returns 0. |
| 821 | */ |
| 822 | static int help_is_platform_command(const char *zName){ |
| 823 | const char *aList[] = { |
| 824 | /* List of commands/pages which are known to only be available in |
| 825 | ** certain builds/platforms. */ |
| 826 | "winsrv", |
| 827 | "json", "/json", |
| 828 | NULL /* end-of-list sentinel */ |
| 829 | }; |
| 830 | int i = 0; |
| 831 | const char *z; |
| 832 | for( z = aList[0]; z ; z = aList[++i] ){ |
| 833 | if( 0==fossil_strcmp(zName, z) ) return 1; |
| 834 | } |
| 835 | return 0; |
| 836 | } |
| 837 | |
| 838 | /* |
| 839 | ** WEBPAGE: help |
| 840 | ** URL: /help?name=CMD |
| 841 | ** |
| 842 | ** Show the built-in help text for CMD. CMD can be a command-line interface |
| @@ -829,34 +860,46 @@ | |
| 860 | if( zCmd && *zCmd ){ |
| 861 | int rc; |
| 862 | const CmdOrPage *pCmd = 0; |
| 863 | |
| 864 | style_set_current_feature("tkt"); |
| 865 | style_submenu_element("Topic-List", "%R/help"); |
| 866 | if( search_restrict(SRCH_HELP)!=0 ){ |
| 867 | style_submenu_element("Search","%R/search?y=h"); |
| 868 | } |
| 869 | rc = dispatch_name_search(zCmd, CMDFLAG_ANY|CMDFLAG_PREFIX, &pCmd); |
| 870 | if( pCmd ){ |
| 871 | style_header("Help: %s", pCmd->zName); |
| 872 | }else{ |
| 873 | style_header("Help"); |
| 874 | } |
| 875 | if( pCmd==0 ){ |
| 876 | /* No <h1> line in this case */ |
| 877 | }else if( *zCmd=='/' ){ |
| 878 | /* Some of the webpages require query parameters in order to work. |
| 879 | ** @ <h1>The "<a href='%R%s(zCmd)'>%s(zCmd)</a>" page:</h1> */ |
| 880 | @ <h1>The "%h(pCmd->zName)" page:</h1> |
| 881 | }else if( rc==0 && (pCmd->eCmdFlags & CMDFLAG_SETTING)!=0 ){ |
| 882 | @ <h1>The "%h(pCmd->zName)" setting:</h1> |
| 883 | }else{ |
| 884 | @ <h1>The "%h(pCmd->zName)" command:</h1> |
| 885 | } |
| 886 | if( rc==1 || (rc==2 && zCmd[0]=='/') ){ |
| 887 | if( zCmd && help_is_platform_command(zCmd) ){ |
| 888 | @ Not available in this build: "%h(zCmd)" |
| 889 | }else{ |
| 890 | @ Unknown topic: "%h(zCmd)" |
| 891 | } |
| 892 | }else if( rc==2 ){ |
| 893 | @ Ambiguous prefix: "%h(zCmd)" |
| 894 | }else{ |
| 895 | if( pCmd->zHelp[0]==0 ){ |
| 896 | @ No help available for "%h(pCmd->zName)" |
| 897 | }else if( P("plaintext") ){ |
| 898 | Blob txt; |
| 899 | blob_init(&txt, 0, 0); |
| 900 | help_to_text(pCmd->zHelp, &txt, 0); |
| 901 | @ <pre class="helpPage"> |
| 902 | @ %h(blob_str(&txt)) |
| 903 | @ </pre> |
| 904 | blob_reset(&txt); |
| 905 | }else if( P("raw") ){ |
| @@ -873,10 +916,11 @@ | |
| 916 | int i; |
| 917 | const char *zWidth = "28ex"; |
| 918 | unsigned char occHelp[FOSSIL_MX_CMDIDX] = {0}; /* Help str occurrences */ |
| 919 | int bktHelp[FOSSIL_MX_CMDIDX][MX_HELP_DUP] = {{0}};/* Help str->commands */ |
| 920 | style_header("Help"); |
| 921 | search_screen(SRCH_HELP, 0x02); |
| 922 | |
| 923 | @ <a name='commands'></a> |
| 924 | @ <h1>Available commands:</h1> |
| 925 | @ <div class="columns" style="column-width: %s(zWidth);"> |
| 926 | @ <ul> |
| @@ -1055,10 +1099,229 @@ | |
| 1099 | } |
| 1100 | @ </dl> |
| 1101 | blob_reset(&buf); |
| 1102 | style_finish_page(); |
| 1103 | } |
| 1104 | |
| 1105 | /* |
| 1106 | ** Analyze p and return one of three values: |
| 1107 | ** |
| 1108 | ** 0 p is the continuation of a prior subcommand. |
| 1109 | ** |
| 1110 | ** 1 p is text past the end of a prior subcommand. |
| 1111 | ** |
| 1112 | ** 2 p is the start of a new subcommand. |
| 1113 | */ |
| 1114 | static int is_subcommand(Blob *p, int bAbbrevSubcmd){ |
| 1115 | int i, sz; |
| 1116 | const unsigned char *z = (const unsigned char*)blob_buffer(p); |
| 1117 | sz = blob_size(p); |
| 1118 | if( sz>6 ) sz = 6; |
| 1119 | for(i=0; i<sz && fossil_isspace(z[i]); i++){} |
| 1120 | if( i>=sz ) return 0; |
| 1121 | |
| 1122 | if( bAbbrevSubcmd==0 ){ |
| 1123 | if( i>1 ) return 0; |
| 1124 | return z[0]=='>' ? 2 : 1; |
| 1125 | }else{ |
| 1126 | return (i==3 && fossil_isalpha(z[3])) ? 2 : 1; |
| 1127 | } |
| 1128 | } |
| 1129 | |
| 1130 | /* |
| 1131 | ** Input z[] is help text for zTopic. If zTopic has sub-command zSub, |
| 1132 | ** then cut out all portions of the original help text that do not |
| 1133 | ** directly pertain to zSub and write the zSub-relevant parts into |
| 1134 | ** pOut. |
| 1135 | ** |
| 1136 | ** Return the number of lines of z[] written into pOut. A return of |
| 1137 | ** zero means no simplification occurred. |
| 1138 | */ |
| 1139 | static int simplify_to_subtopic( |
| 1140 | const char *z, /* Full original help text */ |
| 1141 | Blob *pOut, /* Write simplified help text here */ |
| 1142 | const char *zTopic, /* TOPIC */ |
| 1143 | const char *zSubtopic, /* SUBTOPIC */ |
| 1144 | int bAbbrevSubcmd /* True if z[] contains abbreviated subcommands */ |
| 1145 | ){ |
| 1146 | Blob in, line; //, subsection; |
| 1147 | int n = 0; |
| 1148 | char *zQTop = re_quote(zTopic); |
| 1149 | char *zQSub = re_quote(zSubtopic); |
| 1150 | char *zPattern; |
| 1151 | ReCompiled *pRe = 0; |
| 1152 | |
| 1153 | if( bAbbrevSubcmd ){ |
| 1154 | zPattern = mprintf(" ([a-z]+ ?\\| ?)*%s\\b", zQSub); |
| 1155 | }else{ |
| 1156 | zPattern = mprintf("> ?fossil [-a-z]+ .*\\b%s\\b", zQSub); |
| 1157 | } |
| 1158 | fossil_free(zQTop); |
| 1159 | fossil_free(zQSub); |
| 1160 | re_compile(&pRe, zPattern, 0); |
| 1161 | fossil_free(zPattern); |
| 1162 | blob_init(&in, z, -1); |
| 1163 | while( blob_line(&in, &line) ){ |
| 1164 | if( re_match(pRe, (unsigned char*)blob_buffer(&line), blob_size(&line)) ){ |
| 1165 | int atStart = 1; |
| 1166 | blob_appendb(pOut, &line); |
| 1167 | n++; |
| 1168 | while( blob_line(&in, &line) ){ |
| 1169 | if( re_match(pRe,(unsigned char*)blob_buffer(&line),blob_size(&line)) ){ |
| 1170 | blob_appendb(pOut, &line); |
| 1171 | n++; |
| 1172 | atStart = 1; |
| 1173 | }else{ |
| 1174 | int x = is_subcommand(&line,bAbbrevSubcmd); |
| 1175 | if( x==2 ){ |
| 1176 | if( atStart ){ |
| 1177 | blob_appendb(pOut, &line); |
| 1178 | n++; |
| 1179 | }else{ |
| 1180 | break; |
| 1181 | } |
| 1182 | }else if( x==1 ){ |
| 1183 | break; |
| 1184 | }else{ |
| 1185 | blob_appendb(pOut, &line); |
| 1186 | n++; |
| 1187 | atStart = 0; |
| 1188 | } |
| 1189 | } |
| 1190 | } |
| 1191 | } |
| 1192 | } |
| 1193 | blob_reset(&line); |
| 1194 | re_free(pRe); |
| 1195 | if( n ){ |
| 1196 | blob_trim(pOut); |
| 1197 | blob_reset(&in); |
| 1198 | } |
| 1199 | return n; |
| 1200 | } |
| 1201 | |
| 1202 | /* |
| 1203 | ** Input p is a "Usage:" line or a subcommand line. Simplify this line |
| 1204 | ** for the --usage option and write it into pOut. |
| 1205 | */ |
| 1206 | static void simplify_usage_line( |
| 1207 | Blob *p, |
| 1208 | Blob *pOut, |
| 1209 | int bAbbrevSubcmd, |
| 1210 | const char *zCmd |
| 1211 | ){ |
| 1212 | const char *z = blob_buffer(p); |
| 1213 | int sz = blob_size(p); |
| 1214 | int i = 0; |
| 1215 | if( sz>6 && z[0]=='U' ){ |
| 1216 | for(i=1; i<sz && !fossil_isspace(z[i]); i++){} |
| 1217 | }else if( sz>0 && z[0]=='>' ){ |
| 1218 | i = 1; |
| 1219 | }else if( sz>4 && bAbbrevSubcmd |
| 1220 | && memcmp(z," ",3)==0 && !fossil_isspace(z[3]) ){ |
| 1221 | int j; |
| 1222 | for(j=3; j<sz-1 && (z[j]!=' ' || z[j+1]!=' '); j++){} |
| 1223 | blob_appendf(pOut, "fossil %s %.*s\n", zCmd, j-3, &z[3]); |
| 1224 | return; |
| 1225 | }else{ |
| 1226 | while( i<sz && fossil_isspace(z[i]) ) i++; |
| 1227 | if( i+2<sz && (z[i]=='o' || z[i]=='O') && z[i+1]=='r' ){ |
| 1228 | while( i<sz && !fossil_isspace(z[i]) ) i++; |
| 1229 | } |
| 1230 | } |
| 1231 | while( i<sz && fossil_isspace(z[i]) ) i++; |
| 1232 | blob_append(pOut, &z[i], sz-i); |
| 1233 | } |
| 1234 | |
| 1235 | /* |
| 1236 | ** Input z[] is help text for a command zTopic. Write into pOut all lines of |
| 1237 | ** z[] that show the command-line syntax for that command. Lines written |
| 1238 | ** to pOut are lines that begin with out of: |
| 1239 | ** |
| 1240 | ** Usage: |
| 1241 | ** or: |
| 1242 | ** > fossil TOPIC |
| 1243 | ** |
| 1244 | ** Return the number of lines written into pOut. |
| 1245 | */ |
| 1246 | static int simplify_to_usage( |
| 1247 | const char *z, /* Full original help text */ |
| 1248 | Blob *pOut, /* Write simplified help text here */ |
| 1249 | const char *zTopic, /* The command for which z[] is full help text */ |
| 1250 | int bAbbrevSubcmd /* z[] uses abbreviated subcommands */ |
| 1251 | ){ |
| 1252 | ReCompiled *pRe = 0; |
| 1253 | Blob in, line; |
| 1254 | int n = 0; |
| 1255 | |
| 1256 | if( bAbbrevSubcmd ){ |
| 1257 | re_compile(&pRe, "^(Usage: | [a-z][-a-z|]+ .*)", 0); |
| 1258 | }else{ |
| 1259 | re_compile(&pRe, "^(Usage: | *[Oo]r: +%fossi |> ?fossil )", 0); |
| 1260 | } |
| 1261 | blob_init(&in, z, -1); |
| 1262 | while( blob_line(&in, &line) ){ |
| 1263 | if( re_match(pRe, (unsigned char*)blob_buffer(&line), blob_strlen(&line)) ){ |
| 1264 | simplify_usage_line(&line, pOut, bAbbrevSubcmd, zTopic); |
| 1265 | n++; |
| 1266 | } |
| 1267 | } |
| 1268 | re_free(pRe); |
| 1269 | if( n ) blob_trim(pOut); |
| 1270 | return n; |
| 1271 | } |
| 1272 | |
| 1273 | /* |
| 1274 | ** Input z[] is help text. Write into pOut all lines of z[] that show |
| 1275 | ** command-line options. Return the number of lines written. |
| 1276 | */ |
| 1277 | static int simplify_to_options( |
| 1278 | const char *z, /* Full original help text */ |
| 1279 | Blob *pOut, /* Write simplified help text here */ |
| 1280 | int bAbbrevSubcmd, /* z[] uses abbreviated subcommands */ |
| 1281 | const char *zCmd /* Name of the command that z[] describes */ |
| 1282 | ){ |
| 1283 | ReCompiled *pRe = 0; |
| 1284 | Blob txt, line, subsection; |
| 1285 | int n = 0; |
| 1286 | int bSubsectionSeen = 0; |
| 1287 | |
| 1288 | blob_init(&txt, z, -1); |
| 1289 | blob_init(&subsection, 0, 0); |
| 1290 | re_compile(&pRe, "^ +-.* ", 0); |
| 1291 | while( blob_line(&txt, &line) ){ |
| 1292 | int len = blob_size(&line); |
| 1293 | unsigned char *zLine = (unsigned char *)blob_buffer(&line); |
| 1294 | if( re_match(pRe, zLine, len) ){ |
| 1295 | if( blob_size(&subsection) ){ |
| 1296 | simplify_usage_line(&subsection, pOut, bAbbrevSubcmd, zCmd); |
| 1297 | blob_reset(&subsection); |
| 1298 | } |
| 1299 | blob_appendb(pOut, &line); |
| 1300 | }else if( len>7 && !fossil_isspace(zLine[0]) && bSubsectionSeen |
| 1301 | && sqlite3_strlike("%options:%",blob_str(&line),0)==0 ){ |
| 1302 | subsection = line; |
| 1303 | }else if( !bAbbrevSubcmd && len>9 |
| 1304 | && (memcmp(zLine,"> fossil ",9)==0 |
| 1305 | || memcmp(zLine,"> fossil",9)==0) ){ |
| 1306 | subsection = line; |
| 1307 | bSubsectionSeen = 1; |
| 1308 | }else if( bAbbrevSubcmd && len>5 && memcmp(zLine," ",3)==0 |
| 1309 | && fossil_isalpha(zLine[3]) ){ |
| 1310 | subsection = line; |
| 1311 | bSubsectionSeen = 1; |
| 1312 | }else if( len>1 && !fossil_isspace(zLine[0]) && bSubsectionSeen ){ |
| 1313 | blob_reset(&subsection); |
| 1314 | } |
| 1315 | } |
| 1316 | re_free(pRe); |
| 1317 | blob_trim(pOut); |
| 1318 | blob_reset(&subsection); |
| 1319 | return n; |
| 1320 | } |
| 1321 | |
| 1322 | |
| 1323 | |
| 1324 | static void multi_column_list(const char **azWord, int nWord){ |
| 1325 | int i, j, len; |
| 1326 | int mxLen = 0; |
| 1327 | int nCol; |
| @@ -1125,12 +1388,10 @@ | |
| 1388 | @ |
| 1389 | @ --args FILENAME Read additional arguments and options from FILENAME |
| 1390 | @ --case-sensitive BOOL Set case sensitivity for file names |
| 1391 | @ --cgitrace Active CGI tracing |
| 1392 | @ --chdir PATH Change to PATH before performing any operations |
| 1393 | @ --errorlog FILENAME Log errors to FILENAME |
| 1394 | @ --help Show help on the command rather than running it |
| 1395 | @ --httptrace Trace outbound HTTP requests |
| 1396 | @ --localtime Display times using the local timezone |
| 1397 | @ --nocgi Do not act as CGI |
| @@ -1147,55 +1408,68 @@ | |
| 1408 | ; |
| 1409 | |
| 1410 | /* |
| 1411 | ** COMMAND: help |
| 1412 | ** |
| 1413 | ** Usage: %fossil help [OPTIONS] [TOPIC] [SUBCOMMAND] |
| 1414 | ** |
| 1415 | ** Display information on how to use TOPIC, which may be a command, webpage, or |
| 1416 | ** setting. Webpage names begin with "/". If TOPIC is omitted, a list of |
| 1417 | ** topics is returned. If there is an extra argument after TOPIC, it is |
| 1418 | ** the name of a subcommand, in which case only the help text for that one |
| 1419 | ** subcommand is shown. |
| 1420 | ** |
| 1421 | ** The following options can be used when TOPIC is omitted: |
| 1422 | ** |
| 1423 | ** -a|--all List both common and auxiliary commands |
| 1424 | ** -e|--everything List all help on all topics |
| 1425 | ** -f|--full List full set of commands (including auxiliary |
| 1426 | ** and unsupported "test" commands), options, |
| 1427 | ** settings, and web pages |
| 1428 | ** -o|--options List command-line options common to all commands |
| 1429 | ** -s|--setting List setting names |
| 1430 | ** -t|--test List unsupported "test" commands |
| 1431 | ** -v|--verbose List both names and help text |
| 1432 | ** -x|--aux List only auxiliary commands |
| 1433 | ** -w|--www List all web pages |
| 1434 | ** |
| 1435 | ** These options can be used when TOPIC is present: |
| 1436 | ** |
| 1437 | ** -c|--commands Restrict TOPIC search to commands |
| 1438 | ** -h|--html Format output as HTML rather than plain text |
| 1439 | ** -o|--options Show command-line options for TOPIC |
| 1440 | ** --raw Output raw, unformatted help text |
| 1441 | ** -u|--usage Show a succinct usage summary, not full help text |
| 1442 | ** |
| 1443 | ** See also: [[usage]], [[options]], [[search]] with the -h option |
| 1444 | */ |
| 1445 | void help_cmd(void){ |
| 1446 | int rc; |
| 1447 | int mask = CMDFLAG_ANY; /* Mask of help topic types */ |
| 1448 | int isPage = 0; /* True if TOPIC is a page */ |
| 1449 | int verboseFlag = 0; /* -v option */ |
| 1450 | int commandsFlag = 0; /* -c option */ |
| 1451 | const char *z; /* Original, untranslated help text */ |
| 1452 | const char *zCmdOrPage; /* "command" or "page" or "setting" */ |
| 1453 | const CmdOrPage *pCmd = 0; /* ptr to aCommand[] entry for TOPIC */ |
| 1454 | int useHtml = 0; /* -h option */ |
| 1455 | int bUsage; /* --usage */ |
| 1456 | int bRaw; /* --raw option */ |
| 1457 | int bOptions; /* --options */ |
| 1458 | const char *zTopic; /* TOPIC argument */ |
| 1459 | const char *zSubtopic = 0; /* SUBTOPIC argument */ |
| 1460 | Blob subtext1, subtext2, s3; /* Subsets of z[] containing subtopic/usage */ |
| 1461 | Blob txt; /* Text after rendering */ |
| 1462 | int bAbbrevSubcmd = 0; /* Help text uses abbreviated subcommands */ |
| 1463 | |
| 1464 | verboseFlag = find_option("verbose","v",0)!=0; |
| 1465 | commandsFlag = find_option("commands","c",0)!=0; |
| 1466 | useHtml = find_option("html","h",0)!=0; |
| 1467 | bRaw = find_option("raw",0,0)!=0; |
| 1468 | bOptions = find_option("options","o",0)!=0; |
| 1469 | bUsage = find_option("usage","u",0)!=0; |
| 1470 | if( find_option("all","a",0) ){ |
| 1471 | command_list(CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER, verboseFlag, useHtml); |
| 1472 | return; |
| 1473 | } |
| 1474 | else if( find_option("www","w",0) ){ |
| 1475 | command_list(CMDFLAG_WEBPAGE, verboseFlag, useHtml); |
| @@ -1223,34 +1497,45 @@ | |
| 1497 | command_list(CMDFLAG_SETTING, verboseFlag, useHtml); |
| 1498 | fossil_print("\nfossil web pages:\n\n"); |
| 1499 | command_list(CMDFLAG_WEBPAGE, verboseFlag, useHtml); |
| 1500 | fossil_print("\nfossil test commands (unsupported):\n\n"); |
| 1501 | command_list(CMDFLAG_TEST, verboseFlag, useHtml); |
| 1502 | if ( !verboseFlag ) { |
| 1503 | fossil_print("\n"); |
| 1504 | version_cmd(); |
| 1505 | } |
| 1506 | return; |
| 1507 | } |
| 1508 | else if( find_option("everything","e",0) ){ |
| 1509 | display_all_help(CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER | CMDFLAG_WEBPAGE | |
| 1510 | CMDFLAG_SETTING | CMDFLAG_TEST, useHtml, 0); |
| 1511 | return; |
| 1512 | } |
| 1513 | verify_all_options(); |
| 1514 | if( g.argc<3 ){ |
| 1515 | if( bOptions ){ |
| 1516 | fossil_print("%s", zOptions); |
| 1517 | return; |
| 1518 | } |
| 1519 | z = g.argv[0]; |
| 1520 | fossil_print( |
| 1521 | "Usage: %s help TOPIC\n" |
| 1522 | "Things to try:\n\n" |
| 1523 | " %s help help\n" |
| 1524 | " %s help -o\n" |
| 1525 | " %s help -a\n" |
| 1526 | " %s search -h TOPIC\n\n" |
| 1527 | "Other common values for TOPIC:\n\n", |
| 1528 | z, z, z, z, z); |
| 1529 | command_list(CMDFLAG_1ST_TIER,verboseFlag,useHtml); |
| 1530 | if( !verboseFlag ) version_cmd(); |
| 1531 | return; |
| 1532 | } |
| 1533 | zTopic = g.argv[2]; |
| 1534 | zSubtopic = g.argc>=4 ? g.argv[3] : 0; |
| 1535 | isPage = ('/' == zTopic[0]) ? 1 : 0; |
| 1536 | if( isPage ){ |
| 1537 | zCmdOrPage = "page"; |
| 1538 | }else if( commandsFlag ){ |
| 1539 | mask = CMDFLAG_COMMAND; |
| 1540 | zCmdOrPage = "command"; |
| 1541 | }else{ |
| @@ -1259,10 +1544,14 @@ | |
| 1544 | rc = dispatch_name_search(g.argv[2], mask|CMDFLAG_PREFIX, &pCmd); |
| 1545 | if( rc ){ |
| 1546 | int i, n; |
| 1547 | const char *az[5]; |
| 1548 | if( rc==1 ){ |
| 1549 | if( help_is_platform_command(g.argv[2]) ){ |
| 1550 | fossil_print("Not available in this build: %s\n", g.argv[2]); |
| 1551 | return; |
| 1552 | } |
| 1553 | fossil_print("unknown %s: %s\n", zCmdOrPage, g.argv[2]); |
| 1554 | }else{ |
| 1555 | fossil_print("ambiguous %s prefix: %s\n", |
| 1556 | zCmdOrPage, g.argv[2]); |
| 1557 | } |
| @@ -1269,24 +1558,48 @@ | |
| 1558 | fossil_print("Did you mean one of these TOPICs:\n"); |
| 1559 | n = dispatch_approx_match(g.argv[2], 5, az); |
| 1560 | for(i=0; i<n; i++){ |
| 1561 | fossil_print(" * %s\n", az[i]); |
| 1562 | } |
| 1563 | fossil_print("Other commands to try:\n"); |
| 1564 | fossil_print(" fossil search -h PATTERN ;# search all help text\n"); |
| 1565 | fossil_print(" fossil help -a ;# show all commands\n"); |
| 1566 | fossil_print(" fossil help -w ;# show all web-pages\n"); |
| 1567 | fossil_print(" fossil help -s ;# show all settings\n"); |
| 1568 | fossil_print(" fossil help -o ;# show global options\n"); |
| 1569 | return; |
| 1570 | } |
| 1571 | bAbbrevSubcmd = (pCmd->eCmdFlags & CMDFLAG_ABBREVSUBCMD)!=0; |
| 1572 | z = pCmd->zHelp; |
| 1573 | if( z==0 ){ |
| 1574 | fossil_fatal("no help available for the %s %s", |
| 1575 | pCmd->zName, zCmdOrPage); |
| 1576 | } |
| 1577 | blob_init(&subtext1, 0, 0); |
| 1578 | blob_init(&subtext2, 0, 0); |
| 1579 | blob_init(&s3, 0, 0); |
| 1580 | if( zSubtopic!=0 ){ |
| 1581 | if( simplify_to_subtopic(z, &subtext1, zTopic, zSubtopic, bAbbrevSubcmd) ){ |
| 1582 | z = blob_str(&subtext1); |
| 1583 | }else{ |
| 1584 | fossil_print("No subtopic \"%s\" for \"%s\".\n", zSubtopic, zTopic); |
| 1585 | bUsage = 1; |
| 1586 | zSubtopic = 0; |
| 1587 | } |
| 1588 | } |
| 1589 | if( bUsage ){ |
| 1590 | if( simplify_to_usage(z, &subtext2, zTopic, bAbbrevSubcmd) ){ |
| 1591 | z = blob_str(&subtext2); |
| 1592 | }else{ |
| 1593 | bUsage = 0; |
| 1594 | } |
| 1595 | } |
| 1596 | if( bOptions ){ |
| 1597 | simplify_to_options(z, &s3, bAbbrevSubcmd, zTopic); |
| 1598 | z = blob_str(&s3); |
| 1599 | } |
| 1600 | if( pCmd && pCmd->eCmdFlags & CMDFLAG_SETTING ){ |
| 1601 | const Setting *pSetting = db_find_setting(pCmd->zName, 0); |
| 1602 | char *zDflt = 0; |
| 1603 | if( pSetting!=0 && pSetting->def!=0 && *pSetting->def!=0 ){ |
| 1604 | zDflt = mprintf(" (default: %s)", pSetting->def); |
| 1605 | } |
| @@ -1295,17 +1608,21 @@ | |
| 1608 | (pCmd->eCmdFlags & CMDFLAG_VERSIONABLE)!=0 ? " (versionable)" : "" |
| 1609 | ); |
| 1610 | fossil_free(zDflt); |
| 1611 | } |
| 1612 | blob_init(&txt, 0, 0); |
| 1613 | if( bRaw ){ |
| 1614 | blob_append(&txt, z, -1); |
| 1615 | }else if( useHtml ){ |
| 1616 | help_to_html(z, &txt); |
| 1617 | }else{ |
| 1618 | help_to_text(z, &txt, bUsage || zSubtopic!=0); |
| 1619 | } |
| 1620 | if( blob_strlen(&txt)>0 ) fossil_print("%s\n", blob_str(&txt)); |
| 1621 | blob_reset(&txt); |
| 1622 | blob_reset(&subtext1); |
| 1623 | blob_reset(&subtext2); |
| 1624 | } |
| 1625 | |
| 1626 | /* |
| 1627 | ** Return a pointer to the setting information array. |
| 1628 | ** |
| @@ -1477,11 +1794,11 @@ | |
| 1794 | sqlite3_result_text(ctx, pPage->zHelp, -1, SQLITE_STATIC); |
| 1795 | break; |
| 1796 | case 4: { /* formatted */ |
| 1797 | Blob txt; |
| 1798 | blob_init(&txt, 0, 0); |
| 1799 | help_to_text(pPage->zHelp, &txt, 0); |
| 1800 | sqlite3_result_text(ctx, blob_str(&txt), -1, fossil_free); |
| 1801 | break; |
| 1802 | } |
| 1803 | case 5: { /* formatted */ |
| 1804 | Blob txt; |
| 1805 |
+2
-1
| --- src/doc.c | ||
| +++ src/doc.c | ||
| @@ -639,11 +639,11 @@ | ||
| 639 | 639 | ** are special to HTML encoded. We need to decode these before turning |
| 640 | 640 | ** the text into a title, as the title text will be reencoded later */ |
| 641 | 641 | char *zTitle = mprintf("%.*s", nValue, zValue); |
| 642 | 642 | int i; |
| 643 | 643 | for(i=0; fossil_isspace(zTitle[i]); i++){} |
| 644 | - html_to_plaintext(zTitle+i, pTitle); | |
| 644 | + html_to_plaintext(zTitle+i, pTitle, 0); | |
| 645 | 645 | fossil_free(zTitle); |
| 646 | 646 | seenTitle = 1; |
| 647 | 647 | if( seenClass ) return 1; |
| 648 | 648 | } |
| 649 | 649 | } |
| @@ -807,10 +807,11 @@ | ||
| 807 | 807 | }else if( fossil_strcmp(zMime, "text/x-markdown")==0 ){ |
| 808 | 808 | Blob tail = BLOB_INITIALIZER; |
| 809 | 809 | markdown_to_html(pBody, &title, &tail); |
| 810 | 810 | if( !isPopup ){ |
| 811 | 811 | if( blob_size(&title)>0 ){ |
| 812 | + markdown_dehtmlize_blob(&title); | |
| 812 | 813 | style_header("%s", blob_str(&title)); |
| 813 | 814 | }else{ |
| 814 | 815 | style_header("%s", zDefaultTitle); |
| 815 | 816 | } |
| 816 | 817 | } |
| 817 | 818 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -639,11 +639,11 @@ | |
| 639 | ** are special to HTML encoded. We need to decode these before turning |
| 640 | ** the text into a title, as the title text will be reencoded later */ |
| 641 | char *zTitle = mprintf("%.*s", nValue, zValue); |
| 642 | int i; |
| 643 | for(i=0; fossil_isspace(zTitle[i]); i++){} |
| 644 | html_to_plaintext(zTitle+i, pTitle); |
| 645 | fossil_free(zTitle); |
| 646 | seenTitle = 1; |
| 647 | if( seenClass ) return 1; |
| 648 | } |
| 649 | } |
| @@ -807,10 +807,11 @@ | |
| 807 | }else if( fossil_strcmp(zMime, "text/x-markdown")==0 ){ |
| 808 | Blob tail = BLOB_INITIALIZER; |
| 809 | markdown_to_html(pBody, &title, &tail); |
| 810 | if( !isPopup ){ |
| 811 | if( blob_size(&title)>0 ){ |
| 812 | style_header("%s", blob_str(&title)); |
| 813 | }else{ |
| 814 | style_header("%s", zDefaultTitle); |
| 815 | } |
| 816 | } |
| 817 |
| --- src/doc.c | |
| +++ src/doc.c | |
| @@ -639,11 +639,11 @@ | |
| 639 | ** are special to HTML encoded. We need to decode these before turning |
| 640 | ** the text into a title, as the title text will be reencoded later */ |
| 641 | char *zTitle = mprintf("%.*s", nValue, zValue); |
| 642 | int i; |
| 643 | for(i=0; fossil_isspace(zTitle[i]); i++){} |
| 644 | html_to_plaintext(zTitle+i, pTitle, 0); |
| 645 | fossil_free(zTitle); |
| 646 | seenTitle = 1; |
| 647 | if( seenClass ) return 1; |
| 648 | } |
| 649 | } |
| @@ -807,10 +807,11 @@ | |
| 807 | }else if( fossil_strcmp(zMime, "text/x-markdown")==0 ){ |
| 808 | Blob tail = BLOB_INITIALIZER; |
| 809 | markdown_to_html(pBody, &title, &tail); |
| 810 | if( !isPopup ){ |
| 811 | if( blob_size(&title)>0 ){ |
| 812 | markdown_dehtmlize_blob(&title); |
| 813 | style_header("%s", blob_str(&title)); |
| 814 | }else{ |
| 815 | style_header("%s", zDefaultTitle); |
| 816 | } |
| 817 | } |
| 818 |
+3
-3
| --- src/encode.c | ||
| +++ src/encode.c | ||
| @@ -244,11 +244,11 @@ | ||
| 244 | 244 | } |
| 245 | 245 | |
| 246 | 246 | /* |
| 247 | 247 | ** Convert a single HEX digit to an integer |
| 248 | 248 | */ |
| 249 | -static int AsciiToHex(int c){ | |
| 249 | +int fossil_hexvalue(int c){ | |
| 250 | 250 | if( c>='a' && c<='f' ){ |
| 251 | 251 | c += 10 - 'a'; |
| 252 | 252 | }else if( c>='A' && c<='F' ){ |
| 253 | 253 | c += 10 - 'A'; |
| 254 | 254 | }else if( c>='0' && c<='9' ){ |
| @@ -272,12 +272,12 @@ | ||
| 272 | 272 | i = j = 0; |
| 273 | 273 | while( z[i] ){ |
| 274 | 274 | switch( z[i] ){ |
| 275 | 275 | case '%': |
| 276 | 276 | if( z[i+1] && z[i+2] ){ |
| 277 | - z[j] = AsciiToHex(z[i+1]) << 4; | |
| 278 | - z[j] |= AsciiToHex(z[i+2]); | |
| 277 | + z[j] = fossil_hexvalue(z[i+1]) << 4; | |
| 278 | + z[j] |= fossil_hexvalue(z[i+2]); | |
| 279 | 279 | i += 2; |
| 280 | 280 | } |
| 281 | 281 | break; |
| 282 | 282 | case '+': |
| 283 | 283 | z[j] = ' '; |
| 284 | 284 |
| --- src/encode.c | |
| +++ src/encode.c | |
| @@ -244,11 +244,11 @@ | |
| 244 | } |
| 245 | |
| 246 | /* |
| 247 | ** Convert a single HEX digit to an integer |
| 248 | */ |
| 249 | static int AsciiToHex(int c){ |
| 250 | if( c>='a' && c<='f' ){ |
| 251 | c += 10 - 'a'; |
| 252 | }else if( c>='A' && c<='F' ){ |
| 253 | c += 10 - 'A'; |
| 254 | }else if( c>='0' && c<='9' ){ |
| @@ -272,12 +272,12 @@ | |
| 272 | i = j = 0; |
| 273 | while( z[i] ){ |
| 274 | switch( z[i] ){ |
| 275 | case '%': |
| 276 | if( z[i+1] && z[i+2] ){ |
| 277 | z[j] = AsciiToHex(z[i+1]) << 4; |
| 278 | z[j] |= AsciiToHex(z[i+2]); |
| 279 | i += 2; |
| 280 | } |
| 281 | break; |
| 282 | case '+': |
| 283 | z[j] = ' '; |
| 284 |
| --- src/encode.c | |
| +++ src/encode.c | |
| @@ -244,11 +244,11 @@ | |
| 244 | } |
| 245 | |
| 246 | /* |
| 247 | ** Convert a single HEX digit to an integer |
| 248 | */ |
| 249 | int fossil_hexvalue(int c){ |
| 250 | if( c>='a' && c<='f' ){ |
| 251 | c += 10 - 'a'; |
| 252 | }else if( c>='A' && c<='F' ){ |
| 253 | c += 10 - 'A'; |
| 254 | }else if( c>='0' && c<='9' ){ |
| @@ -272,12 +272,12 @@ | |
| 272 | i = j = 0; |
| 273 | while( z[i] ){ |
| 274 | switch( z[i] ){ |
| 275 | case '%': |
| 276 | if( z[i+1] && z[i+2] ){ |
| 277 | z[j] = fossil_hexvalue(z[i+1]) << 4; |
| 278 | z[j] |= fossil_hexvalue(z[i+2]); |
| 279 | i += 2; |
| 280 | } |
| 281 | break; |
| 282 | case '+': |
| 283 | z[j] = ' '; |
| 284 |
+15
-8
| --- src/export.c | ||
| +++ src/export.c | ||
| @@ -480,10 +480,12 @@ | ||
| 480 | 480 | ** |
| 481 | 481 | ** See also: import |
| 482 | 482 | */ |
| 483 | 483 | /* |
| 484 | 484 | ** COMMAND: export* |
| 485 | +** | |
| 486 | +** Usage: %fossil export --git [REPOSITORY] | |
| 485 | 487 | ** |
| 486 | 488 | ** This command is deprecated. Use "fossil git export" instead. |
| 487 | 489 | */ |
| 488 | 490 | void export_cmd(void){ |
| 489 | 491 | Stmt q, q2, q3; |
| @@ -1072,12 +1074,11 @@ | ||
| 1072 | 1074 | */ |
| 1073 | 1075 | static int gitmirror_send_checkin( |
| 1074 | 1076 | FILE *xCmd, /* Write fast-import text on this pipe */ |
| 1075 | 1077 | int rid, /* BLOB.RID for the check-in to export */ |
| 1076 | 1078 | const char *zUuid, /* BLOB.UUID for the check-in to export */ |
| 1077 | - int *pnLimit, /* Stop when the counter reaches zero */ | |
| 1078 | - int fManifest /* MFESTFLG_* values */ | |
| 1079 | + int *pnLimit /* Stop when the counter reaches zero */ | |
| 1079 | 1080 | ){ |
| 1080 | 1081 | Manifest *pMan; /* The check-in to be output */ |
| 1081 | 1082 | int i; /* Loop counter */ |
| 1082 | 1083 | int iParent; /* Which immediate ancestor is primary. -1 for none */ |
| 1083 | 1084 | Stmt q; /* An SQL query */ |
| @@ -1087,10 +1088,12 @@ | ||
| 1087 | 1088 | Blob comment; /* The comment text for the check-in */ |
| 1088 | 1089 | int nErr = 0; /* Number of errors */ |
| 1089 | 1090 | int bPhantomOk; /* True if phantom files should be ignored */ |
| 1090 | 1091 | char buf[24]; |
| 1091 | 1092 | char *zEmail; /* Contact info for Git committer field */ |
| 1093 | + int fManifest; /* Should the manifest files be included? */ | |
| 1094 | + int fPManifest = 0; /* OR of the manifest files for all parents */ | |
| 1092 | 1095 | |
| 1093 | 1096 | pMan = manifest_get(rid, CFTYPE_MANIFEST, 0); |
| 1094 | 1097 | if( pMan==0 ){ |
| 1095 | 1098 | /* Must be a phantom. Return without doing anything, and in particular |
| 1096 | 1099 | ** without creating a mark for this check-in. */ |
| @@ -1104,11 +1107,11 @@ | ||
| 1104 | 1107 | char *zPMark = gitmirror_find_mark(pMan->azParent[i], 0, 0); |
| 1105 | 1108 | if( zPMark==0 ){ |
| 1106 | 1109 | int prid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", |
| 1107 | 1110 | pMan->azParent[i]); |
| 1108 | 1111 | int rc = gitmirror_send_checkin(xCmd, prid, pMan->azParent[i], |
| 1109 | - pnLimit, fManifest); | |
| 1112 | + pnLimit); | |
| 1110 | 1113 | if( rc || *pnLimit<=0 ){ |
| 1111 | 1114 | manifest_destroy(pMan); |
| 1112 | 1115 | return 1; |
| 1113 | 1116 | } |
| 1114 | 1117 | } |
| @@ -1213,10 +1216,11 @@ | ||
| 1213 | 1216 | blob_reset(&comment); |
| 1214 | 1217 | iParent = -1; /* Which ancestor is the primary parent */ |
| 1215 | 1218 | for(i=0; i<pMan->nParent; i++){ |
| 1216 | 1219 | char *zOther = gitmirror_find_mark(pMan->azParent[i],0,0); |
| 1217 | 1220 | if( zOther==0 ) continue; |
| 1221 | + fPManifest |= db_get_manifest_setting(pMan->azParent[i]); | |
| 1218 | 1222 | if( iParent<0 ){ |
| 1219 | 1223 | iParent = i; |
| 1220 | 1224 | fprintf(xCmd, "from %s\n", zOther); |
| 1221 | 1225 | }else{ |
| 1222 | 1226 | fprintf(xCmd, "merge %s\n", zOther); |
| @@ -1269,29 +1273,36 @@ | ||
| 1269 | 1273 | db_finalize(&q); |
| 1270 | 1274 | manifest_destroy(pMan); |
| 1271 | 1275 | pMan = 0; |
| 1272 | 1276 | |
| 1273 | 1277 | /* Include Fossil-generated auxiliary files in the check-in */ |
| 1278 | + fManifest = db_get_manifest_setting(zUuid); | |
| 1274 | 1279 | if( fManifest & MFESTFLG_RAW ){ |
| 1275 | 1280 | Blob manifest; |
| 1276 | 1281 | content_get(rid, &manifest); |
| 1277 | 1282 | sterilize_manifest(&manifest, CFTYPE_MANIFEST); |
| 1278 | 1283 | fprintf(xCmd,"M 100644 inline manifest\ndata %d\n%s\n", |
| 1279 | 1284 | blob_strlen(&manifest), blob_str(&manifest)); |
| 1280 | 1285 | blob_reset(&manifest); |
| 1286 | + }else if( fPManifest & MFESTFLG_RAW ){ | |
| 1287 | + fprintf(xCmd, "D manifest\n"); | |
| 1281 | 1288 | } |
| 1282 | 1289 | if( fManifest & MFESTFLG_UUID ){ |
| 1283 | 1290 | int n = (int)strlen(zUuid); |
| 1284 | 1291 | fprintf(xCmd,"M 100644 inline manifest.uuid\ndata %d\n%s\n\n", n+1, zUuid); |
| 1292 | + }else if( fPManifest & MFESTFLG_UUID ){ | |
| 1293 | + fprintf(xCmd, "D manifest.uuid\n"); | |
| 1285 | 1294 | } |
| 1286 | 1295 | if( fManifest & MFESTFLG_TAGS ){ |
| 1287 | 1296 | Blob tagslist; |
| 1288 | 1297 | blob_init(&tagslist, 0, 0); |
| 1289 | 1298 | get_checkin_taglist(rid, &tagslist); |
| 1290 | 1299 | fprintf(xCmd,"M 100644 inline manifest.tags\ndata %d\n%s\n", |
| 1291 | 1300 | blob_strlen(&tagslist), blob_str(&tagslist)); |
| 1292 | 1301 | blob_reset(&tagslist); |
| 1302 | + }else if( fPManifest & MFESTFLG_TAGS ){ | |
| 1303 | + fprintf(xCmd, "D manifest.tags\n"); | |
| 1293 | 1304 | } |
| 1294 | 1305 | |
| 1295 | 1306 | /* The check-in is finished, so decrement the counter */ |
| 1296 | 1307 | (*pnLimit)--; |
| 1297 | 1308 | return 0; |
| @@ -1381,11 +1392,10 @@ | ||
| 1381 | 1392 | char *zPushUrl; /* URL to sync the mirror to */ |
| 1382 | 1393 | double rEnd; /* time of most recent export */ |
| 1383 | 1394 | int rc; /* Result code */ |
| 1384 | 1395 | int bForce; /* Do the export and sync even if no changes*/ |
| 1385 | 1396 | int bNeedRepack = 0; /* True if we should run repack at the end */ |
| 1386 | - int fManifest; /* Current "manifest" setting */ | |
| 1387 | 1397 | int bIfExists; /* The --if-mirrored flag */ |
| 1388 | 1398 | FILE *xCmd; /* Pipe to the "git fast-import" command */ |
| 1389 | 1399 | FILE *pMarks; /* Git mark files */ |
| 1390 | 1400 | Stmt q; /* Queries */ |
| 1391 | 1401 | char zLine[200]; /* One line of a mark file */ |
| @@ -1519,13 +1529,10 @@ | ||
| 1519 | 1529 | gitmirror_message(VERB_NORMAL, "no changes\n"); |
| 1520 | 1530 | db_commit_transaction(); |
| 1521 | 1531 | return; |
| 1522 | 1532 | } |
| 1523 | 1533 | |
| 1524 | - /* Do we need to include manifest files in the clone? */ | |
| 1525 | - fManifest = db_get_manifest_setting(); | |
| 1526 | - | |
| 1527 | 1534 | /* Change to the MIRROR directory so that the Git commands will work */ |
| 1528 | 1535 | rc = file_chdir(zMirror, 0); |
| 1529 | 1536 | if( rc ) fossil_fatal("cannot change the working directory to \"%s\"", |
| 1530 | 1537 | zMirror); |
| 1531 | 1538 | |
| @@ -1577,11 +1584,11 @@ | ||
| 1577 | 1584 | while( nLimit && db_step(&q)==SQLITE_ROW ){ |
| 1578 | 1585 | int rid = db_column_int(&q, 0); |
| 1579 | 1586 | double rMTime = db_column_double(&q, 1); |
| 1580 | 1587 | const char *zUuid = db_column_text(&q, 2); |
| 1581 | 1588 | if( rMTime>rEnd ) rEnd = rMTime; |
| 1582 | - rc = gitmirror_send_checkin(xCmd, rid, zUuid, &nLimit, fManifest); | |
| 1589 | + rc = gitmirror_send_checkin(xCmd, rid, zUuid, &nLimit); | |
| 1583 | 1590 | if( rc ) break; |
| 1584 | 1591 | gitmirror_message(VERB_NORMAL,"%d/%d \r", nTotal-nLimit, nTotal); |
| 1585 | 1592 | fflush(stdout); |
| 1586 | 1593 | } |
| 1587 | 1594 | db_finalize(&q); |
| 1588 | 1595 |
| --- src/export.c | |
| +++ src/export.c | |
| @@ -480,10 +480,12 @@ | |
| 480 | ** |
| 481 | ** See also: import |
| 482 | */ |
| 483 | /* |
| 484 | ** COMMAND: export* |
| 485 | ** |
| 486 | ** This command is deprecated. Use "fossil git export" instead. |
| 487 | */ |
| 488 | void export_cmd(void){ |
| 489 | Stmt q, q2, q3; |
| @@ -1072,12 +1074,11 @@ | |
| 1072 | */ |
| 1073 | static int gitmirror_send_checkin( |
| 1074 | FILE *xCmd, /* Write fast-import text on this pipe */ |
| 1075 | int rid, /* BLOB.RID for the check-in to export */ |
| 1076 | const char *zUuid, /* BLOB.UUID for the check-in to export */ |
| 1077 | int *pnLimit, /* Stop when the counter reaches zero */ |
| 1078 | int fManifest /* MFESTFLG_* values */ |
| 1079 | ){ |
| 1080 | Manifest *pMan; /* The check-in to be output */ |
| 1081 | int i; /* Loop counter */ |
| 1082 | int iParent; /* Which immediate ancestor is primary. -1 for none */ |
| 1083 | Stmt q; /* An SQL query */ |
| @@ -1087,10 +1088,12 @@ | |
| 1087 | Blob comment; /* The comment text for the check-in */ |
| 1088 | int nErr = 0; /* Number of errors */ |
| 1089 | int bPhantomOk; /* True if phantom files should be ignored */ |
| 1090 | char buf[24]; |
| 1091 | char *zEmail; /* Contact info for Git committer field */ |
| 1092 | |
| 1093 | pMan = manifest_get(rid, CFTYPE_MANIFEST, 0); |
| 1094 | if( pMan==0 ){ |
| 1095 | /* Must be a phantom. Return without doing anything, and in particular |
| 1096 | ** without creating a mark for this check-in. */ |
| @@ -1104,11 +1107,11 @@ | |
| 1104 | char *zPMark = gitmirror_find_mark(pMan->azParent[i], 0, 0); |
| 1105 | if( zPMark==0 ){ |
| 1106 | int prid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", |
| 1107 | pMan->azParent[i]); |
| 1108 | int rc = gitmirror_send_checkin(xCmd, prid, pMan->azParent[i], |
| 1109 | pnLimit, fManifest); |
| 1110 | if( rc || *pnLimit<=0 ){ |
| 1111 | manifest_destroy(pMan); |
| 1112 | return 1; |
| 1113 | } |
| 1114 | } |
| @@ -1213,10 +1216,11 @@ | |
| 1213 | blob_reset(&comment); |
| 1214 | iParent = -1; /* Which ancestor is the primary parent */ |
| 1215 | for(i=0; i<pMan->nParent; i++){ |
| 1216 | char *zOther = gitmirror_find_mark(pMan->azParent[i],0,0); |
| 1217 | if( zOther==0 ) continue; |
| 1218 | if( iParent<0 ){ |
| 1219 | iParent = i; |
| 1220 | fprintf(xCmd, "from %s\n", zOther); |
| 1221 | }else{ |
| 1222 | fprintf(xCmd, "merge %s\n", zOther); |
| @@ -1269,29 +1273,36 @@ | |
| 1269 | db_finalize(&q); |
| 1270 | manifest_destroy(pMan); |
| 1271 | pMan = 0; |
| 1272 | |
| 1273 | /* Include Fossil-generated auxiliary files in the check-in */ |
| 1274 | if( fManifest & MFESTFLG_RAW ){ |
| 1275 | Blob manifest; |
| 1276 | content_get(rid, &manifest); |
| 1277 | sterilize_manifest(&manifest, CFTYPE_MANIFEST); |
| 1278 | fprintf(xCmd,"M 100644 inline manifest\ndata %d\n%s\n", |
| 1279 | blob_strlen(&manifest), blob_str(&manifest)); |
| 1280 | blob_reset(&manifest); |
| 1281 | } |
| 1282 | if( fManifest & MFESTFLG_UUID ){ |
| 1283 | int n = (int)strlen(zUuid); |
| 1284 | fprintf(xCmd,"M 100644 inline manifest.uuid\ndata %d\n%s\n\n", n+1, zUuid); |
| 1285 | } |
| 1286 | if( fManifest & MFESTFLG_TAGS ){ |
| 1287 | Blob tagslist; |
| 1288 | blob_init(&tagslist, 0, 0); |
| 1289 | get_checkin_taglist(rid, &tagslist); |
| 1290 | fprintf(xCmd,"M 100644 inline manifest.tags\ndata %d\n%s\n", |
| 1291 | blob_strlen(&tagslist), blob_str(&tagslist)); |
| 1292 | blob_reset(&tagslist); |
| 1293 | } |
| 1294 | |
| 1295 | /* The check-in is finished, so decrement the counter */ |
| 1296 | (*pnLimit)--; |
| 1297 | return 0; |
| @@ -1381,11 +1392,10 @@ | |
| 1381 | char *zPushUrl; /* URL to sync the mirror to */ |
| 1382 | double rEnd; /* time of most recent export */ |
| 1383 | int rc; /* Result code */ |
| 1384 | int bForce; /* Do the export and sync even if no changes*/ |
| 1385 | int bNeedRepack = 0; /* True if we should run repack at the end */ |
| 1386 | int fManifest; /* Current "manifest" setting */ |
| 1387 | int bIfExists; /* The --if-mirrored flag */ |
| 1388 | FILE *xCmd; /* Pipe to the "git fast-import" command */ |
| 1389 | FILE *pMarks; /* Git mark files */ |
| 1390 | Stmt q; /* Queries */ |
| 1391 | char zLine[200]; /* One line of a mark file */ |
| @@ -1519,13 +1529,10 @@ | |
| 1519 | gitmirror_message(VERB_NORMAL, "no changes\n"); |
| 1520 | db_commit_transaction(); |
| 1521 | return; |
| 1522 | } |
| 1523 | |
| 1524 | /* Do we need to include manifest files in the clone? */ |
| 1525 | fManifest = db_get_manifest_setting(); |
| 1526 | |
| 1527 | /* Change to the MIRROR directory so that the Git commands will work */ |
| 1528 | rc = file_chdir(zMirror, 0); |
| 1529 | if( rc ) fossil_fatal("cannot change the working directory to \"%s\"", |
| 1530 | zMirror); |
| 1531 | |
| @@ -1577,11 +1584,11 @@ | |
| 1577 | while( nLimit && db_step(&q)==SQLITE_ROW ){ |
| 1578 | int rid = db_column_int(&q, 0); |
| 1579 | double rMTime = db_column_double(&q, 1); |
| 1580 | const char *zUuid = db_column_text(&q, 2); |
| 1581 | if( rMTime>rEnd ) rEnd = rMTime; |
| 1582 | rc = gitmirror_send_checkin(xCmd, rid, zUuid, &nLimit, fManifest); |
| 1583 | if( rc ) break; |
| 1584 | gitmirror_message(VERB_NORMAL,"%d/%d \r", nTotal-nLimit, nTotal); |
| 1585 | fflush(stdout); |
| 1586 | } |
| 1587 | db_finalize(&q); |
| 1588 |
| --- src/export.c | |
| +++ src/export.c | |
| @@ -480,10 +480,12 @@ | |
| 480 | ** |
| 481 | ** See also: import |
| 482 | */ |
| 483 | /* |
| 484 | ** COMMAND: export* |
| 485 | ** |
| 486 | ** Usage: %fossil export --git [REPOSITORY] |
| 487 | ** |
| 488 | ** This command is deprecated. Use "fossil git export" instead. |
| 489 | */ |
| 490 | void export_cmd(void){ |
| 491 | Stmt q, q2, q3; |
| @@ -1072,12 +1074,11 @@ | |
| 1074 | */ |
| 1075 | static int gitmirror_send_checkin( |
| 1076 | FILE *xCmd, /* Write fast-import text on this pipe */ |
| 1077 | int rid, /* BLOB.RID for the check-in to export */ |
| 1078 | const char *zUuid, /* BLOB.UUID for the check-in to export */ |
| 1079 | int *pnLimit /* Stop when the counter reaches zero */ |
| 1080 | ){ |
| 1081 | Manifest *pMan; /* The check-in to be output */ |
| 1082 | int i; /* Loop counter */ |
| 1083 | int iParent; /* Which immediate ancestor is primary. -1 for none */ |
| 1084 | Stmt q; /* An SQL query */ |
| @@ -1087,10 +1088,12 @@ | |
| 1088 | Blob comment; /* The comment text for the check-in */ |
| 1089 | int nErr = 0; /* Number of errors */ |
| 1090 | int bPhantomOk; /* True if phantom files should be ignored */ |
| 1091 | char buf[24]; |
| 1092 | char *zEmail; /* Contact info for Git committer field */ |
| 1093 | int fManifest; /* Should the manifest files be included? */ |
| 1094 | int fPManifest = 0; /* OR of the manifest files for all parents */ |
| 1095 | |
| 1096 | pMan = manifest_get(rid, CFTYPE_MANIFEST, 0); |
| 1097 | if( pMan==0 ){ |
| 1098 | /* Must be a phantom. Return without doing anything, and in particular |
| 1099 | ** without creating a mark for this check-in. */ |
| @@ -1104,11 +1107,11 @@ | |
| 1107 | char *zPMark = gitmirror_find_mark(pMan->azParent[i], 0, 0); |
| 1108 | if( zPMark==0 ){ |
| 1109 | int prid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", |
| 1110 | pMan->azParent[i]); |
| 1111 | int rc = gitmirror_send_checkin(xCmd, prid, pMan->azParent[i], |
| 1112 | pnLimit); |
| 1113 | if( rc || *pnLimit<=0 ){ |
| 1114 | manifest_destroy(pMan); |
| 1115 | return 1; |
| 1116 | } |
| 1117 | } |
| @@ -1213,10 +1216,11 @@ | |
| 1216 | blob_reset(&comment); |
| 1217 | iParent = -1; /* Which ancestor is the primary parent */ |
| 1218 | for(i=0; i<pMan->nParent; i++){ |
| 1219 | char *zOther = gitmirror_find_mark(pMan->azParent[i],0,0); |
| 1220 | if( zOther==0 ) continue; |
| 1221 | fPManifest |= db_get_manifest_setting(pMan->azParent[i]); |
| 1222 | if( iParent<0 ){ |
| 1223 | iParent = i; |
| 1224 | fprintf(xCmd, "from %s\n", zOther); |
| 1225 | }else{ |
| 1226 | fprintf(xCmd, "merge %s\n", zOther); |
| @@ -1269,29 +1273,36 @@ | |
| 1273 | db_finalize(&q); |
| 1274 | manifest_destroy(pMan); |
| 1275 | pMan = 0; |
| 1276 | |
| 1277 | /* Include Fossil-generated auxiliary files in the check-in */ |
| 1278 | fManifest = db_get_manifest_setting(zUuid); |
| 1279 | if( fManifest & MFESTFLG_RAW ){ |
| 1280 | Blob manifest; |
| 1281 | content_get(rid, &manifest); |
| 1282 | sterilize_manifest(&manifest, CFTYPE_MANIFEST); |
| 1283 | fprintf(xCmd,"M 100644 inline manifest\ndata %d\n%s\n", |
| 1284 | blob_strlen(&manifest), blob_str(&manifest)); |
| 1285 | blob_reset(&manifest); |
| 1286 | }else if( fPManifest & MFESTFLG_RAW ){ |
| 1287 | fprintf(xCmd, "D manifest\n"); |
| 1288 | } |
| 1289 | if( fManifest & MFESTFLG_UUID ){ |
| 1290 | int n = (int)strlen(zUuid); |
| 1291 | fprintf(xCmd,"M 100644 inline manifest.uuid\ndata %d\n%s\n\n", n+1, zUuid); |
| 1292 | }else if( fPManifest & MFESTFLG_UUID ){ |
| 1293 | fprintf(xCmd, "D manifest.uuid\n"); |
| 1294 | } |
| 1295 | if( fManifest & MFESTFLG_TAGS ){ |
| 1296 | Blob tagslist; |
| 1297 | blob_init(&tagslist, 0, 0); |
| 1298 | get_checkin_taglist(rid, &tagslist); |
| 1299 | fprintf(xCmd,"M 100644 inline manifest.tags\ndata %d\n%s\n", |
| 1300 | blob_strlen(&tagslist), blob_str(&tagslist)); |
| 1301 | blob_reset(&tagslist); |
| 1302 | }else if( fPManifest & MFESTFLG_TAGS ){ |
| 1303 | fprintf(xCmd, "D manifest.tags\n"); |
| 1304 | } |
| 1305 | |
| 1306 | /* The check-in is finished, so decrement the counter */ |
| 1307 | (*pnLimit)--; |
| 1308 | return 0; |
| @@ -1381,11 +1392,10 @@ | |
| 1392 | char *zPushUrl; /* URL to sync the mirror to */ |
| 1393 | double rEnd; /* time of most recent export */ |
| 1394 | int rc; /* Result code */ |
| 1395 | int bForce; /* Do the export and sync even if no changes*/ |
| 1396 | int bNeedRepack = 0; /* True if we should run repack at the end */ |
| 1397 | int bIfExists; /* The --if-mirrored flag */ |
| 1398 | FILE *xCmd; /* Pipe to the "git fast-import" command */ |
| 1399 | FILE *pMarks; /* Git mark files */ |
| 1400 | Stmt q; /* Queries */ |
| 1401 | char zLine[200]; /* One line of a mark file */ |
| @@ -1519,13 +1529,10 @@ | |
| 1529 | gitmirror_message(VERB_NORMAL, "no changes\n"); |
| 1530 | db_commit_transaction(); |
| 1531 | return; |
| 1532 | } |
| 1533 | |
| 1534 | /* Change to the MIRROR directory so that the Git commands will work */ |
| 1535 | rc = file_chdir(zMirror, 0); |
| 1536 | if( rc ) fossil_fatal("cannot change the working directory to \"%s\"", |
| 1537 | zMirror); |
| 1538 | |
| @@ -1577,11 +1584,11 @@ | |
| 1584 | while( nLimit && db_step(&q)==SQLITE_ROW ){ |
| 1585 | int rid = db_column_int(&q, 0); |
| 1586 | double rMTime = db_column_double(&q, 1); |
| 1587 | const char *zUuid = db_column_text(&q, 2); |
| 1588 | if( rMTime>rEnd ) rEnd = rMTime; |
| 1589 | rc = gitmirror_send_checkin(xCmd, rid, zUuid, &nLimit); |
| 1590 | if( rc ) break; |
| 1591 | gitmirror_message(VERB_NORMAL,"%d/%d \r", nTotal-nLimit, nTotal); |
| 1592 | fflush(stdout); |
| 1593 | } |
| 1594 | db_finalize(&q); |
| 1595 |
+7
| --- src/file.c | ||
| +++ src/file.c | ||
| @@ -1572,10 +1572,17 @@ | ||
| 1572 | 1572 | ** |
| 1573 | 1573 | ** Usage: %fossil test-which ARGS... |
| 1574 | 1574 | ** |
| 1575 | 1575 | ** For each argument, search the PATH for the executable with the name |
| 1576 | 1576 | ** and print its full pathname. |
| 1577 | +** | |
| 1578 | +** See also the "which" command (without the "test-" prefix). The plain | |
| 1579 | +** "which" command is more convenient to use since it provides the -a/-all | |
| 1580 | +** option, and because it is shorter. The "fossil which" command without | |
| 1581 | +** the "test-" prefix is recommended for day-to-day use. This command is | |
| 1582 | +** retained because it tests the internal file_fullexename() function | |
| 1583 | +** whereas plain "which" does not. | |
| 1577 | 1584 | */ |
| 1578 | 1585 | void test_which_cmd(void){ |
| 1579 | 1586 | int i; |
| 1580 | 1587 | for(i=2; i<g.argc; i++){ |
| 1581 | 1588 | char *z = file_fullexename(g.argv[i]); |
| 1582 | 1589 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -1572,10 +1572,17 @@ | |
| 1572 | ** |
| 1573 | ** Usage: %fossil test-which ARGS... |
| 1574 | ** |
| 1575 | ** For each argument, search the PATH for the executable with the name |
| 1576 | ** and print its full pathname. |
| 1577 | */ |
| 1578 | void test_which_cmd(void){ |
| 1579 | int i; |
| 1580 | for(i=2; i<g.argc; i++){ |
| 1581 | char *z = file_fullexename(g.argv[i]); |
| 1582 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -1572,10 +1572,17 @@ | |
| 1572 | ** |
| 1573 | ** Usage: %fossil test-which ARGS... |
| 1574 | ** |
| 1575 | ** For each argument, search the PATH for the executable with the name |
| 1576 | ** and print its full pathname. |
| 1577 | ** |
| 1578 | ** See also the "which" command (without the "test-" prefix). The plain |
| 1579 | ** "which" command is more convenient to use since it provides the -a/-all |
| 1580 | ** option, and because it is shorter. The "fossil which" command without |
| 1581 | ** the "test-" prefix is recommended for day-to-day use. This command is |
| 1582 | ** retained because it tests the internal file_fullexename() function |
| 1583 | ** whereas plain "which" does not. |
| 1584 | */ |
| 1585 | void test_which_cmd(void){ |
| 1586 | int i; |
| 1587 | for(i=2; i<g.argc; i++){ |
| 1588 | char *z = file_fullexename(g.argv[i]); |
| 1589 |
+4
-2
| --- src/finfo.c | ||
| +++ src/finfo.c | ||
| @@ -498,16 +498,16 @@ | ||
| 498 | 498 | " AND event.objid=mlink.mid\n", |
| 499 | 499 | TAG_BRANCH |
| 500 | 500 | ); |
| 501 | 501 | if( (zA = P("a"))!=0 ){ |
| 502 | 502 | blob_append_sql(&sql, " AND event.mtime>=%.16g\n", |
| 503 | - symbolic_name_to_mtime(zA,0)); | |
| 503 | + symbolic_name_to_mtime(zA,0,0)); | |
| 504 | 504 | url_add_parameter(&url, "a", zA); |
| 505 | 505 | } |
| 506 | 506 | if( (zB = P("b"))!=0 ){ |
| 507 | 507 | blob_append_sql(&sql, " AND event.mtime<=%.16g\n", |
| 508 | - symbolic_name_to_mtime(zB,0)); | |
| 508 | + symbolic_name_to_mtime(zB,0,1)); | |
| 509 | 509 | url_add_parameter(&url, "b", zB); |
| 510 | 510 | } |
| 511 | 511 | if( ridFrom ){ |
| 512 | 512 | blob_append_sql(&sql, |
| 513 | 513 | " AND mlink.mid IN (SELECT rid FROM ancestor)\n" |
| @@ -636,10 +636,12 @@ | ||
| 636 | 636 | if( zBr==0 ) zBr = "trunk"; |
| 637 | 637 | if( uBg ){ |
| 638 | 638 | zBgClr = user_color(zUser); |
| 639 | 639 | }else if( brBg || zBgClr==0 || zBgClr[0]==0 ){ |
| 640 | 640 | zBgClr = strcmp(zBr,"trunk")==0 ? "" : hash_color(zBr); |
| 641 | + }else if( zBgClr ){ | |
| 642 | + zBgClr = reasonable_bg_color(zBgClr,0); | |
| 641 | 643 | } |
| 642 | 644 | gidx = graph_add_row(pGraph, |
| 643 | 645 | frid>0 ? (GraphRowId)frid*(mxfnid+1)+fnid : fpid+1000000000, |
| 644 | 646 | nParent, 0, aParent, zBr, zBgClr, |
| 645 | 647 | zUuid, 0); |
| 646 | 648 |
| --- src/finfo.c | |
| +++ src/finfo.c | |
| @@ -498,16 +498,16 @@ | |
| 498 | " AND event.objid=mlink.mid\n", |
| 499 | TAG_BRANCH |
| 500 | ); |
| 501 | if( (zA = P("a"))!=0 ){ |
| 502 | blob_append_sql(&sql, " AND event.mtime>=%.16g\n", |
| 503 | symbolic_name_to_mtime(zA,0)); |
| 504 | url_add_parameter(&url, "a", zA); |
| 505 | } |
| 506 | if( (zB = P("b"))!=0 ){ |
| 507 | blob_append_sql(&sql, " AND event.mtime<=%.16g\n", |
| 508 | symbolic_name_to_mtime(zB,0)); |
| 509 | url_add_parameter(&url, "b", zB); |
| 510 | } |
| 511 | if( ridFrom ){ |
| 512 | blob_append_sql(&sql, |
| 513 | " AND mlink.mid IN (SELECT rid FROM ancestor)\n" |
| @@ -636,10 +636,12 @@ | |
| 636 | if( zBr==0 ) zBr = "trunk"; |
| 637 | if( uBg ){ |
| 638 | zBgClr = user_color(zUser); |
| 639 | }else if( brBg || zBgClr==0 || zBgClr[0]==0 ){ |
| 640 | zBgClr = strcmp(zBr,"trunk")==0 ? "" : hash_color(zBr); |
| 641 | } |
| 642 | gidx = graph_add_row(pGraph, |
| 643 | frid>0 ? (GraphRowId)frid*(mxfnid+1)+fnid : fpid+1000000000, |
| 644 | nParent, 0, aParent, zBr, zBgClr, |
| 645 | zUuid, 0); |
| 646 |
| --- src/finfo.c | |
| +++ src/finfo.c | |
| @@ -498,16 +498,16 @@ | |
| 498 | " AND event.objid=mlink.mid\n", |
| 499 | TAG_BRANCH |
| 500 | ); |
| 501 | if( (zA = P("a"))!=0 ){ |
| 502 | blob_append_sql(&sql, " AND event.mtime>=%.16g\n", |
| 503 | symbolic_name_to_mtime(zA,0,0)); |
| 504 | url_add_parameter(&url, "a", zA); |
| 505 | } |
| 506 | if( (zB = P("b"))!=0 ){ |
| 507 | blob_append_sql(&sql, " AND event.mtime<=%.16g\n", |
| 508 | symbolic_name_to_mtime(zB,0,1)); |
| 509 | url_add_parameter(&url, "b", zB); |
| 510 | } |
| 511 | if( ridFrom ){ |
| 512 | blob_append_sql(&sql, |
| 513 | " AND mlink.mid IN (SELECT rid FROM ancestor)\n" |
| @@ -636,10 +636,12 @@ | |
| 636 | if( zBr==0 ) zBr = "trunk"; |
| 637 | if( uBg ){ |
| 638 | zBgClr = user_color(zUser); |
| 639 | }else if( brBg || zBgClr==0 || zBgClr[0]==0 ){ |
| 640 | zBgClr = strcmp(zBr,"trunk")==0 ? "" : hash_color(zBr); |
| 641 | }else if( zBgClr ){ |
| 642 | zBgClr = reasonable_bg_color(zBgClr,0); |
| 643 | } |
| 644 | gidx = graph_add_row(pGraph, |
| 645 | frid>0 ? (GraphRowId)frid*(mxfnid+1)+fnid : fpid+1000000000, |
| 646 | nParent, 0, aParent, zBr, zBgClr, |
| 647 | zUuid, 0); |
| 648 |
+13
-5
| --- src/fossil.page.pikchrshowasm.js | ||
| +++ src/fossil.page.pikchrshowasm.js | ||
| @@ -120,11 +120,16 @@ | ||
| 120 | 120 | } |
| 121 | 121 | }); |
| 122 | 122 | delete PS._config; |
| 123 | 123 | } |
| 124 | 124 | |
| 125 | - PS.worker = new Worker('builtin/extsrc/pikchr-worker.js'); | |
| 125 | + /* Randomize the name of the worker script so that it is never cached. | |
| 126 | + ** The Fossil /builtin method will automatically remove the "-v000000000" | |
| 127 | + ** part of the filename, resolving it to just "pikchr-worker.js". */ | |
| 128 | + PS.worker = new Worker('builtin/extsrc/pikchr-worker-v'+ | |
| 129 | + (Math.floor(Math.random()*10000000000) + 1000000000)+ | |
| 130 | + '.js'); | |
| 126 | 131 | PS.worker.onmessage = (ev)=>PS.runMsgHandlers(ev.data); |
| 127 | 132 | PS.addMsgHandler('stdout', console.log.bind(console)); |
| 128 | 133 | PS.addMsgHandler('stderr', console.error.bind(console)); |
| 129 | 134 | |
| 130 | 135 | /** Handles status updates from the Module object. */ |
| @@ -172,21 +177,21 @@ | ||
| 172 | 177 | PS.renderModeLabels[PS.renderModes[PS.renderModes.selectedIndex]]; |
| 173 | 178 | |
| 174 | 179 | /** |
| 175 | 180 | The 'pikchr-ready' event is fired (with no payload) when the |
| 176 | 181 | wasm module has finished loading. */ |
| 177 | - PS.addMsgHandler('pikchr-ready', function(){ | |
| 182 | + PS.addMsgHandler('pikchr-ready', function(event){ | |
| 178 | 183 | PS.clearMsgHandlers('pikchr-ready'); |
| 179 | - F.page.onPikchrshowLoaded(); | |
| 184 | + F.page.onPikchrshowLoaded(event.data); | |
| 180 | 185 | }); |
| 181 | 186 | |
| 182 | 187 | /** |
| 183 | 188 | Performs all app initialization which must wait until after the |
| 184 | 189 | worker module is loaded. This function removes itself when it's |
| 185 | 190 | called. |
| 186 | 191 | */ |
| 187 | - F.page.onPikchrshowLoaded = function(){ | |
| 192 | + F.page.onPikchrshowLoaded = function(pikchrVersion){ | |
| 188 | 193 | delete this.onPikchrshowLoaded; |
| 189 | 194 | // Unhide all elements which start out hidden |
| 190 | 195 | EAll('.initially-hidden').forEach((e)=>e.classList.remove('initially-hidden')); |
| 191 | 196 | const taInput = E('#input'); |
| 192 | 197 | const btnClearIn = E('#btn-clear'); |
| @@ -439,10 +444,13 @@ | ||
| 439 | 444 | if( src && (new URL(self.location.href).searchParams).has('fromSession') ){ |
| 440 | 445 | taInput.value = src; |
| 441 | 446 | window.sessionStorage.removeItem('pikchr-xfer'); |
| 442 | 447 | } |
| 443 | 448 | } |
| 449 | + D.append(E('fieldset.options > div'), | |
| 450 | + D.append(D.addClass(D.span(), 'labeled-input'), | |
| 451 | + 'pikchr v. '+pikchrVersion)); | |
| 444 | 452 | |
| 445 | 453 | PS.e.btnRender.click(); |
| 446 | 454 | |
| 447 | 455 | /** Debounce handler for auto-rendering while typing. */ |
| 448 | 456 | const debounceAutoRender = F.debounce(function f(){ |
| @@ -524,11 +532,11 @@ | ||
| 524 | 532 | ForceResizeKludge(); |
| 525 | 533 | }/*onPikchrshowLoaded()*/; |
| 526 | 534 | |
| 527 | 535 | |
| 528 | 536 | /** |
| 529 | - Predefined scripts. Each entry is an object: | |
| 537 | + Predefined example pikchr scripts. Each entry is an object: | |
| 530 | 538 | |
| 531 | 539 | { |
| 532 | 540 | name: required string, |
| 533 | 541 | code: optional code string. An entry with a falsy code is treated |
| 534 | 542 | like a separator in the resulting SELECT element (a |
| 535 | 543 |
| --- src/fossil.page.pikchrshowasm.js | |
| +++ src/fossil.page.pikchrshowasm.js | |
| @@ -120,11 +120,16 @@ | |
| 120 | } |
| 121 | }); |
| 122 | delete PS._config; |
| 123 | } |
| 124 | |
| 125 | PS.worker = new Worker('builtin/extsrc/pikchr-worker.js'); |
| 126 | PS.worker.onmessage = (ev)=>PS.runMsgHandlers(ev.data); |
| 127 | PS.addMsgHandler('stdout', console.log.bind(console)); |
| 128 | PS.addMsgHandler('stderr', console.error.bind(console)); |
| 129 | |
| 130 | /** Handles status updates from the Module object. */ |
| @@ -172,21 +177,21 @@ | |
| 172 | PS.renderModeLabels[PS.renderModes[PS.renderModes.selectedIndex]]; |
| 173 | |
| 174 | /** |
| 175 | The 'pikchr-ready' event is fired (with no payload) when the |
| 176 | wasm module has finished loading. */ |
| 177 | PS.addMsgHandler('pikchr-ready', function(){ |
| 178 | PS.clearMsgHandlers('pikchr-ready'); |
| 179 | F.page.onPikchrshowLoaded(); |
| 180 | }); |
| 181 | |
| 182 | /** |
| 183 | Performs all app initialization which must wait until after the |
| 184 | worker module is loaded. This function removes itself when it's |
| 185 | called. |
| 186 | */ |
| 187 | F.page.onPikchrshowLoaded = function(){ |
| 188 | delete this.onPikchrshowLoaded; |
| 189 | // Unhide all elements which start out hidden |
| 190 | EAll('.initially-hidden').forEach((e)=>e.classList.remove('initially-hidden')); |
| 191 | const taInput = E('#input'); |
| 192 | const btnClearIn = E('#btn-clear'); |
| @@ -439,10 +444,13 @@ | |
| 439 | if( src && (new URL(self.location.href).searchParams).has('fromSession') ){ |
| 440 | taInput.value = src; |
| 441 | window.sessionStorage.removeItem('pikchr-xfer'); |
| 442 | } |
| 443 | } |
| 444 | |
| 445 | PS.e.btnRender.click(); |
| 446 | |
| 447 | /** Debounce handler for auto-rendering while typing. */ |
| 448 | const debounceAutoRender = F.debounce(function f(){ |
| @@ -524,11 +532,11 @@ | |
| 524 | ForceResizeKludge(); |
| 525 | }/*onPikchrshowLoaded()*/; |
| 526 | |
| 527 | |
| 528 | /** |
| 529 | Predefined scripts. Each entry is an object: |
| 530 | |
| 531 | { |
| 532 | name: required string, |
| 533 | code: optional code string. An entry with a falsy code is treated |
| 534 | like a separator in the resulting SELECT element (a |
| 535 |
| --- src/fossil.page.pikchrshowasm.js | |
| +++ src/fossil.page.pikchrshowasm.js | |
| @@ -120,11 +120,16 @@ | |
| 120 | } |
| 121 | }); |
| 122 | delete PS._config; |
| 123 | } |
| 124 | |
| 125 | /* Randomize the name of the worker script so that it is never cached. |
| 126 | ** The Fossil /builtin method will automatically remove the "-v000000000" |
| 127 | ** part of the filename, resolving it to just "pikchr-worker.js". */ |
| 128 | PS.worker = new Worker('builtin/extsrc/pikchr-worker-v'+ |
| 129 | (Math.floor(Math.random()*10000000000) + 1000000000)+ |
| 130 | '.js'); |
| 131 | PS.worker.onmessage = (ev)=>PS.runMsgHandlers(ev.data); |
| 132 | PS.addMsgHandler('stdout', console.log.bind(console)); |
| 133 | PS.addMsgHandler('stderr', console.error.bind(console)); |
| 134 | |
| 135 | /** Handles status updates from the Module object. */ |
| @@ -172,21 +177,21 @@ | |
| 177 | PS.renderModeLabels[PS.renderModes[PS.renderModes.selectedIndex]]; |
| 178 | |
| 179 | /** |
| 180 | The 'pikchr-ready' event is fired (with no payload) when the |
| 181 | wasm module has finished loading. */ |
| 182 | PS.addMsgHandler('pikchr-ready', function(event){ |
| 183 | PS.clearMsgHandlers('pikchr-ready'); |
| 184 | F.page.onPikchrshowLoaded(event.data); |
| 185 | }); |
| 186 | |
| 187 | /** |
| 188 | Performs all app initialization which must wait until after the |
| 189 | worker module is loaded. This function removes itself when it's |
| 190 | called. |
| 191 | */ |
| 192 | F.page.onPikchrshowLoaded = function(pikchrVersion){ |
| 193 | delete this.onPikchrshowLoaded; |
| 194 | // Unhide all elements which start out hidden |
| 195 | EAll('.initially-hidden').forEach((e)=>e.classList.remove('initially-hidden')); |
| 196 | const taInput = E('#input'); |
| 197 | const btnClearIn = E('#btn-clear'); |
| @@ -439,10 +444,13 @@ | |
| 444 | if( src && (new URL(self.location.href).searchParams).has('fromSession') ){ |
| 445 | taInput.value = src; |
| 446 | window.sessionStorage.removeItem('pikchr-xfer'); |
| 447 | } |
| 448 | } |
| 449 | D.append(E('fieldset.options > div'), |
| 450 | D.append(D.addClass(D.span(), 'labeled-input'), |
| 451 | 'pikchr v. '+pikchrVersion)); |
| 452 | |
| 453 | PS.e.btnRender.click(); |
| 454 | |
| 455 | /** Debounce handler for auto-rendering while typing. */ |
| 456 | const debounceAutoRender = F.debounce(function f(){ |
| @@ -524,11 +532,11 @@ | |
| 532 | ForceResizeKludge(); |
| 533 | }/*onPikchrshowLoaded()*/; |
| 534 | |
| 535 | |
| 536 | /** |
| 537 | Predefined example pikchr scripts. Each entry is an object: |
| 538 | |
| 539 | { |
| 540 | name: required string, |
| 541 | code: optional code string. An entry with a falsy code is treated |
| 542 | like a separator in the resulting SELECT element (a |
| 543 |
+9
-8
| --- src/fossil.page.wikiedit.js | ||
| +++ src/fossil.page.wikiedit.js | ||
| @@ -14,11 +14,11 @@ | ||
| 14 | 14 | cache), in the form of an "winfo" object: |
| 15 | 15 | |
| 16 | 16 | { |
| 17 | 17 | name: string, |
| 18 | 18 | mimetype: mimetype string, |
| 19 | - type: "normal" | "tag" | "checkin" | "branch" | "sandbox", | |
| 19 | + type: "normal" | "tag" | "checkin" | "branch" | "ticket" | "sandbox", | |
| 20 | 20 | version: UUID string or null for a sandbox page or new page, |
| 21 | 21 | parent: parent UUID string or null if no parent, |
| 22 | 22 | isEmpty: true if page has no content (is "deleted"). |
| 23 | 23 | content: string, optional in most contexts |
| 24 | 24 | } |
| @@ -192,11 +192,11 @@ | ||
| 192 | 192 | name: winfo.name |
| 193 | 193 | }); |
| 194 | 194 | record.mimetype = winfo.mimetype; |
| 195 | 195 | record.type = winfo.type; |
| 196 | 196 | record.parent = winfo.parent; |
| 197 | - record.version = winfo.version; | |
| 197 | + record.version = winfo.version; | |
| 198 | 198 | record.stashTime = new Date().getTime(); |
| 199 | 199 | record.isEmpty = !!winfo.isEmpty; |
| 200 | 200 | record.attachments = winfo.attachments; |
| 201 | 201 | this.storeIndex(); |
| 202 | 202 | if(arguments.length>1){ |
| @@ -207,11 +207,11 @@ | ||
| 207 | 207 | return this; |
| 208 | 208 | }, |
| 209 | 209 | /** |
| 210 | 210 | Returns the stashed content, if any, for the given winfo |
| 211 | 211 | object. |
| 212 | - */ | |
| 212 | + */ | |
| 213 | 213 | stashedContent: function(winfo){ |
| 214 | 214 | return F.storage.get(this.contentKey(this.indexKey(winfo))); |
| 215 | 215 | }, |
| 216 | 216 | /** Returns true if we have stashed content for the given winfo |
| 217 | 217 | record or page name. */ |
| @@ -270,11 +270,11 @@ | ||
| 270 | 270 | if(n) this._fireStashEvent(); |
| 271 | 271 | } |
| 272 | 272 | }; |
| 273 | 273 | $stash.prune.defaultMaxCount = P.config.defaultMaxStashSize || 10; |
| 274 | 274 | P.$stash = $stash /* we have to expose this for the new-page case :/ */; |
| 275 | - | |
| 275 | + | |
| 276 | 276 | /** |
| 277 | 277 | Internal workaround to select the current preview mode |
| 278 | 278 | and fire a change event if the value actually changes |
| 279 | 279 | or if forceEvent is truthy. |
| 280 | 280 | */ |
| @@ -536,10 +536,11 @@ | ||
| 536 | 536 | name = name.trim(); |
| 537 | 537 | if(!this.validatePageName(name)) return false; |
| 538 | 538 | var wtype = 'normal'; |
| 539 | 539 | if(0===name.indexOf('checkin/')) wtype = 'checkin'; |
| 540 | 540 | else if(0===name.indexOf('branch/')) wtype = 'branch'; |
| 541 | + else if(0===name.indexOf('ticket/')) wtype = 'ticket'; | |
| 541 | 542 | else if(0===name.indexOf('tag/')) wtype = 'tag'; |
| 542 | 543 | /* ^^^ note that we're not validating that, e.g., checkin/XYZ |
| 543 | 544 | has a full artifact ID after "checkin/". */ |
| 544 | 545 | const winfo = { |
| 545 | 546 | name: name, type: wtype, mimetype: 'text/x-markdown', |
| @@ -573,11 +574,11 @@ | ||
| 573 | 574 | |
| 574 | 575 | /** Set up filter checkboxes for the various types |
| 575 | 576 | of wiki pages... */ |
| 576 | 577 | const fsFilter = D.addClass(D.fieldset("Page types"),"page-types-list"), |
| 577 | 578 | fsFilterBody = D.div(), |
| 578 | - filters = ['normal', 'branch/...', 'tag/...', 'checkin/...'] | |
| 579 | + filters = ['normal', 'branch/...', 'tag/...', 'checkin/...', 'ticket/...'] | |
| 579 | 580 | ; |
| 580 | 581 | D.append(fsFilter, fsFilterBody); |
| 581 | 582 | D.addClass(fsFilterBody, 'flex-container', 'flex-column', 'stretch'); |
| 582 | 583 | |
| 583 | 584 | // Add filters by page type... |
| @@ -1045,11 +1046,11 @@ | ||
| 1045 | 1046 | P.e.btnSave.addEventListener('click', ()=>doSave(), false); |
| 1046 | 1047 | P.e.btnSaveClose.addEventListener('click', ()=>doSave(true), false); |
| 1047 | 1048 | } |
| 1048 | 1049 | |
| 1049 | 1050 | P.e.taEditor.addEventListener('change', ()=>P.notifyOfChange(), false); |
| 1050 | - | |
| 1051 | + | |
| 1051 | 1052 | P.selectMimetype(false, true); |
| 1052 | 1053 | P.e.selectMimetype.addEventListener( |
| 1053 | 1054 | 'change', |
| 1054 | 1055 | function(e){ |
| 1055 | 1056 | if(P.winfo && P.winfo.mimetype !== e.target.value){ |
| @@ -1058,11 +1059,11 @@ | ||
| 1058 | 1059 | P.stashContentChange(true); |
| 1059 | 1060 | } |
| 1060 | 1061 | }, |
| 1061 | 1062 | false |
| 1062 | 1063 | ); |
| 1063 | - | |
| 1064 | + | |
| 1064 | 1065 | const selectFontSize = E('select[name=editor_font_size]'); |
| 1065 | 1066 | if(selectFontSize){ |
| 1066 | 1067 | selectFontSize.addEventListener( |
| 1067 | 1068 | "change",function(e){ |
| 1068 | 1069 | const ed = P.e.taEditor; |
| @@ -1590,11 +1591,11 @@ | ||
| 1590 | 1591 | responseType: 'json', |
| 1591 | 1592 | onload: callee.onload |
| 1592 | 1593 | }); |
| 1593 | 1594 | return this; |
| 1594 | 1595 | }; |
| 1595 | - | |
| 1596 | + | |
| 1596 | 1597 | /** |
| 1597 | 1598 | Updates P.winfo for certain state and stashes P.winfo, with the |
| 1598 | 1599 | current content fetched via P.wikiContent(). |
| 1599 | 1600 | |
| 1600 | 1601 | If passed truthy AND the stash already has stashed content for |
| 1601 | 1602 |
| --- src/fossil.page.wikiedit.js | |
| +++ src/fossil.page.wikiedit.js | |
| @@ -14,11 +14,11 @@ | |
| 14 | cache), in the form of an "winfo" object: |
| 15 | |
| 16 | { |
| 17 | name: string, |
| 18 | mimetype: mimetype string, |
| 19 | type: "normal" | "tag" | "checkin" | "branch" | "sandbox", |
| 20 | version: UUID string or null for a sandbox page or new page, |
| 21 | parent: parent UUID string or null if no parent, |
| 22 | isEmpty: true if page has no content (is "deleted"). |
| 23 | content: string, optional in most contexts |
| 24 | } |
| @@ -192,11 +192,11 @@ | |
| 192 | name: winfo.name |
| 193 | }); |
| 194 | record.mimetype = winfo.mimetype; |
| 195 | record.type = winfo.type; |
| 196 | record.parent = winfo.parent; |
| 197 | record.version = winfo.version; |
| 198 | record.stashTime = new Date().getTime(); |
| 199 | record.isEmpty = !!winfo.isEmpty; |
| 200 | record.attachments = winfo.attachments; |
| 201 | this.storeIndex(); |
| 202 | if(arguments.length>1){ |
| @@ -207,11 +207,11 @@ | |
| 207 | return this; |
| 208 | }, |
| 209 | /** |
| 210 | Returns the stashed content, if any, for the given winfo |
| 211 | object. |
| 212 | */ |
| 213 | stashedContent: function(winfo){ |
| 214 | return F.storage.get(this.contentKey(this.indexKey(winfo))); |
| 215 | }, |
| 216 | /** Returns true if we have stashed content for the given winfo |
| 217 | record or page name. */ |
| @@ -270,11 +270,11 @@ | |
| 270 | if(n) this._fireStashEvent(); |
| 271 | } |
| 272 | }; |
| 273 | $stash.prune.defaultMaxCount = P.config.defaultMaxStashSize || 10; |
| 274 | P.$stash = $stash /* we have to expose this for the new-page case :/ */; |
| 275 | |
| 276 | /** |
| 277 | Internal workaround to select the current preview mode |
| 278 | and fire a change event if the value actually changes |
| 279 | or if forceEvent is truthy. |
| 280 | */ |
| @@ -536,10 +536,11 @@ | |
| 536 | name = name.trim(); |
| 537 | if(!this.validatePageName(name)) return false; |
| 538 | var wtype = 'normal'; |
| 539 | if(0===name.indexOf('checkin/')) wtype = 'checkin'; |
| 540 | else if(0===name.indexOf('branch/')) wtype = 'branch'; |
| 541 | else if(0===name.indexOf('tag/')) wtype = 'tag'; |
| 542 | /* ^^^ note that we're not validating that, e.g., checkin/XYZ |
| 543 | has a full artifact ID after "checkin/". */ |
| 544 | const winfo = { |
| 545 | name: name, type: wtype, mimetype: 'text/x-markdown', |
| @@ -573,11 +574,11 @@ | |
| 573 | |
| 574 | /** Set up filter checkboxes for the various types |
| 575 | of wiki pages... */ |
| 576 | const fsFilter = D.addClass(D.fieldset("Page types"),"page-types-list"), |
| 577 | fsFilterBody = D.div(), |
| 578 | filters = ['normal', 'branch/...', 'tag/...', 'checkin/...'] |
| 579 | ; |
| 580 | D.append(fsFilter, fsFilterBody); |
| 581 | D.addClass(fsFilterBody, 'flex-container', 'flex-column', 'stretch'); |
| 582 | |
| 583 | // Add filters by page type... |
| @@ -1045,11 +1046,11 @@ | |
| 1045 | P.e.btnSave.addEventListener('click', ()=>doSave(), false); |
| 1046 | P.e.btnSaveClose.addEventListener('click', ()=>doSave(true), false); |
| 1047 | } |
| 1048 | |
| 1049 | P.e.taEditor.addEventListener('change', ()=>P.notifyOfChange(), false); |
| 1050 | |
| 1051 | P.selectMimetype(false, true); |
| 1052 | P.e.selectMimetype.addEventListener( |
| 1053 | 'change', |
| 1054 | function(e){ |
| 1055 | if(P.winfo && P.winfo.mimetype !== e.target.value){ |
| @@ -1058,11 +1059,11 @@ | |
| 1058 | P.stashContentChange(true); |
| 1059 | } |
| 1060 | }, |
| 1061 | false |
| 1062 | ); |
| 1063 | |
| 1064 | const selectFontSize = E('select[name=editor_font_size]'); |
| 1065 | if(selectFontSize){ |
| 1066 | selectFontSize.addEventListener( |
| 1067 | "change",function(e){ |
| 1068 | const ed = P.e.taEditor; |
| @@ -1590,11 +1591,11 @@ | |
| 1590 | responseType: 'json', |
| 1591 | onload: callee.onload |
| 1592 | }); |
| 1593 | return this; |
| 1594 | }; |
| 1595 | |
| 1596 | /** |
| 1597 | Updates P.winfo for certain state and stashes P.winfo, with the |
| 1598 | current content fetched via P.wikiContent(). |
| 1599 | |
| 1600 | If passed truthy AND the stash already has stashed content for |
| 1601 |
| --- src/fossil.page.wikiedit.js | |
| +++ src/fossil.page.wikiedit.js | |
| @@ -14,11 +14,11 @@ | |
| 14 | cache), in the form of an "winfo" object: |
| 15 | |
| 16 | { |
| 17 | name: string, |
| 18 | mimetype: mimetype string, |
| 19 | type: "normal" | "tag" | "checkin" | "branch" | "ticket" | "sandbox", |
| 20 | version: UUID string or null for a sandbox page or new page, |
| 21 | parent: parent UUID string or null if no parent, |
| 22 | isEmpty: true if page has no content (is "deleted"). |
| 23 | content: string, optional in most contexts |
| 24 | } |
| @@ -192,11 +192,11 @@ | |
| 192 | name: winfo.name |
| 193 | }); |
| 194 | record.mimetype = winfo.mimetype; |
| 195 | record.type = winfo.type; |
| 196 | record.parent = winfo.parent; |
| 197 | record.version = winfo.version; |
| 198 | record.stashTime = new Date().getTime(); |
| 199 | record.isEmpty = !!winfo.isEmpty; |
| 200 | record.attachments = winfo.attachments; |
| 201 | this.storeIndex(); |
| 202 | if(arguments.length>1){ |
| @@ -207,11 +207,11 @@ | |
| 207 | return this; |
| 208 | }, |
| 209 | /** |
| 210 | Returns the stashed content, if any, for the given winfo |
| 211 | object. |
| 212 | */ |
| 213 | stashedContent: function(winfo){ |
| 214 | return F.storage.get(this.contentKey(this.indexKey(winfo))); |
| 215 | }, |
| 216 | /** Returns true if we have stashed content for the given winfo |
| 217 | record or page name. */ |
| @@ -270,11 +270,11 @@ | |
| 270 | if(n) this._fireStashEvent(); |
| 271 | } |
| 272 | }; |
| 273 | $stash.prune.defaultMaxCount = P.config.defaultMaxStashSize || 10; |
| 274 | P.$stash = $stash /* we have to expose this for the new-page case :/ */; |
| 275 | |
| 276 | /** |
| 277 | Internal workaround to select the current preview mode |
| 278 | and fire a change event if the value actually changes |
| 279 | or if forceEvent is truthy. |
| 280 | */ |
| @@ -536,10 +536,11 @@ | |
| 536 | name = name.trim(); |
| 537 | if(!this.validatePageName(name)) return false; |
| 538 | var wtype = 'normal'; |
| 539 | if(0===name.indexOf('checkin/')) wtype = 'checkin'; |
| 540 | else if(0===name.indexOf('branch/')) wtype = 'branch'; |
| 541 | else if(0===name.indexOf('ticket/')) wtype = 'ticket'; |
| 542 | else if(0===name.indexOf('tag/')) wtype = 'tag'; |
| 543 | /* ^^^ note that we're not validating that, e.g., checkin/XYZ |
| 544 | has a full artifact ID after "checkin/". */ |
| 545 | const winfo = { |
| 546 | name: name, type: wtype, mimetype: 'text/x-markdown', |
| @@ -573,11 +574,11 @@ | |
| 574 | |
| 575 | /** Set up filter checkboxes for the various types |
| 576 | of wiki pages... */ |
| 577 | const fsFilter = D.addClass(D.fieldset("Page types"),"page-types-list"), |
| 578 | fsFilterBody = D.div(), |
| 579 | filters = ['normal', 'branch/...', 'tag/...', 'checkin/...', 'ticket/...'] |
| 580 | ; |
| 581 | D.append(fsFilter, fsFilterBody); |
| 582 | D.addClass(fsFilterBody, 'flex-container', 'flex-column', 'stretch'); |
| 583 | |
| 584 | // Add filters by page type... |
| @@ -1045,11 +1046,11 @@ | |
| 1046 | P.e.btnSave.addEventListener('click', ()=>doSave(), false); |
| 1047 | P.e.btnSaveClose.addEventListener('click', ()=>doSave(true), false); |
| 1048 | } |
| 1049 | |
| 1050 | P.e.taEditor.addEventListener('change', ()=>P.notifyOfChange(), false); |
| 1051 | |
| 1052 | P.selectMimetype(false, true); |
| 1053 | P.e.selectMimetype.addEventListener( |
| 1054 | 'change', |
| 1055 | function(e){ |
| 1056 | if(P.winfo && P.winfo.mimetype !== e.target.value){ |
| @@ -1058,11 +1059,11 @@ | |
| 1059 | P.stashContentChange(true); |
| 1060 | } |
| 1061 | }, |
| 1062 | false |
| 1063 | ); |
| 1064 | |
| 1065 | const selectFontSize = E('select[name=editor_font_size]'); |
| 1066 | if(selectFontSize){ |
| 1067 | selectFontSize.addEventListener( |
| 1068 | "change",function(e){ |
| 1069 | const ed = P.e.taEditor; |
| @@ -1590,11 +1591,11 @@ | |
| 1591 | responseType: 'json', |
| 1592 | onload: callee.onload |
| 1593 | }); |
| 1594 | return this; |
| 1595 | }; |
| 1596 | |
| 1597 | /** |
| 1598 | Updates P.winfo for certain state and stashes P.winfo, with the |
| 1599 | current content fetched via P.wikiContent(). |
| 1600 | |
| 1601 | If passed truthy AND the stash already has stashed content for |
| 1602 |
+12
-1
| --- src/fuzz.c | ||
| +++ src/fuzz.c | ||
| @@ -60,10 +60,11 @@ | ||
| 60 | 60 | */ |
| 61 | 61 | #define FUZZ_WIKI 0 /* The Fossil-Wiki formatter */ |
| 62 | 62 | #define FUZZ_MARKDOWN 1 /* The Markdown formatter */ |
| 63 | 63 | #define FUZZ_ARTIFACT 2 /* Fuzz the artifact parser */ |
| 64 | 64 | #define FUZZ_WIKI2 3 /* FOSSIL_WIKI and FOSSIL_MARKDOWN */ |
| 65 | +#define FUZZ_COMFORMAT 4 /* comment_print() */ | |
| 65 | 66 | #endif |
| 66 | 67 | |
| 67 | 68 | /* The type of fuzzing to do */ |
| 68 | 69 | static int eFuzzType = FUZZ_WIKI; |
| 69 | 70 | |
| @@ -93,13 +94,20 @@ | ||
| 93 | 94 | blob_reset(&out); |
| 94 | 95 | markdown_to_html(&in, &title, &out); |
| 95 | 96 | blob_reset(&title); |
| 96 | 97 | break; |
| 97 | 98 | } |
| 98 | - case FUZZ_ARTIFACT: | |
| 99 | + case FUZZ_ARTIFACT: { | |
| 99 | 100 | fossil_fatal("FUZZ_ARTIFACT is not implemented."); |
| 100 | 101 | break; |
| 102 | + } | |
| 103 | + case FUZZ_COMFORMAT: { | |
| 104 | + if( nByte>=3 && aData[1]!=0 && memchr(&aData[1], 0, nByte-1)!=0 ){ | |
| 105 | + int flags = (int)aData[0]; | |
| 106 | + comment_print((const char*)&aData[1],0,15,80,flags); | |
| 107 | + } | |
| 108 | + } | |
| 101 | 109 | } |
| 102 | 110 | blob_reset(&in); |
| 103 | 111 | blob_reset(&out); |
| 104 | 112 | return 0; |
| 105 | 113 | } |
| @@ -116,10 +124,12 @@ | ||
| 116 | 124 | eFuzzType = FUZZ_WIKI; |
| 117 | 125 | }else if( fossil_strcmp(zType,"markdown")==0 ){ |
| 118 | 126 | eFuzzType = FUZZ_MARKDOWN; |
| 119 | 127 | }else if( fossil_strcmp(zType,"wiki2")==0 ){ |
| 120 | 128 | eFuzzType = FUZZ_WIKI2; |
| 129 | + }else if( fossil_strcmp(zType,"comformat")==0 ){ | |
| 130 | + eFuzzType = FUZZ_COMFORMAT; | |
| 121 | 131 | }else{ |
| 122 | 132 | fossil_fatal("unknown fuzz type: \"%s\"", zType); |
| 123 | 133 | } |
| 124 | 134 | } |
| 125 | 135 | |
| @@ -139,10 +149,11 @@ | ||
| 139 | 149 | ** |
| 140 | 150 | ** Usage: %fossil test-fuzz [-fuzztype TYPE] INPUTFILE... |
| 141 | 151 | ** |
| 142 | 152 | ** Run a fuzz test using INPUTFILE as the test data. TYPE can be one of: |
| 143 | 153 | ** |
| 154 | +** comformat Fuzz the comment_print() routine | |
| 144 | 155 | ** wiki Fuzz the Fossil-wiki translator |
| 145 | 156 | ** markdown Fuzz the markdown translator |
| 146 | 157 | ** artifact Fuzz the artifact parser |
| 147 | 158 | ** wiki2 Fuzz the Fossil-wiki and markdown translator |
| 148 | 159 | */ |
| 149 | 160 |
| --- src/fuzz.c | |
| +++ src/fuzz.c | |
| @@ -60,10 +60,11 @@ | |
| 60 | */ |
| 61 | #define FUZZ_WIKI 0 /* The Fossil-Wiki formatter */ |
| 62 | #define FUZZ_MARKDOWN 1 /* The Markdown formatter */ |
| 63 | #define FUZZ_ARTIFACT 2 /* Fuzz the artifact parser */ |
| 64 | #define FUZZ_WIKI2 3 /* FOSSIL_WIKI and FOSSIL_MARKDOWN */ |
| 65 | #endif |
| 66 | |
| 67 | /* The type of fuzzing to do */ |
| 68 | static int eFuzzType = FUZZ_WIKI; |
| 69 | |
| @@ -93,13 +94,20 @@ | |
| 93 | blob_reset(&out); |
| 94 | markdown_to_html(&in, &title, &out); |
| 95 | blob_reset(&title); |
| 96 | break; |
| 97 | } |
| 98 | case FUZZ_ARTIFACT: |
| 99 | fossil_fatal("FUZZ_ARTIFACT is not implemented."); |
| 100 | break; |
| 101 | } |
| 102 | blob_reset(&in); |
| 103 | blob_reset(&out); |
| 104 | return 0; |
| 105 | } |
| @@ -116,10 +124,12 @@ | |
| 116 | eFuzzType = FUZZ_WIKI; |
| 117 | }else if( fossil_strcmp(zType,"markdown")==0 ){ |
| 118 | eFuzzType = FUZZ_MARKDOWN; |
| 119 | }else if( fossil_strcmp(zType,"wiki2")==0 ){ |
| 120 | eFuzzType = FUZZ_WIKI2; |
| 121 | }else{ |
| 122 | fossil_fatal("unknown fuzz type: \"%s\"", zType); |
| 123 | } |
| 124 | } |
| 125 | |
| @@ -139,10 +149,11 @@ | |
| 139 | ** |
| 140 | ** Usage: %fossil test-fuzz [-fuzztype TYPE] INPUTFILE... |
| 141 | ** |
| 142 | ** Run a fuzz test using INPUTFILE as the test data. TYPE can be one of: |
| 143 | ** |
| 144 | ** wiki Fuzz the Fossil-wiki translator |
| 145 | ** markdown Fuzz the markdown translator |
| 146 | ** artifact Fuzz the artifact parser |
| 147 | ** wiki2 Fuzz the Fossil-wiki and markdown translator |
| 148 | */ |
| 149 |
| --- src/fuzz.c | |
| +++ src/fuzz.c | |
| @@ -60,10 +60,11 @@ | |
| 60 | */ |
| 61 | #define FUZZ_WIKI 0 /* The Fossil-Wiki formatter */ |
| 62 | #define FUZZ_MARKDOWN 1 /* The Markdown formatter */ |
| 63 | #define FUZZ_ARTIFACT 2 /* Fuzz the artifact parser */ |
| 64 | #define FUZZ_WIKI2 3 /* FOSSIL_WIKI and FOSSIL_MARKDOWN */ |
| 65 | #define FUZZ_COMFORMAT 4 /* comment_print() */ |
| 66 | #endif |
| 67 | |
| 68 | /* The type of fuzzing to do */ |
| 69 | static int eFuzzType = FUZZ_WIKI; |
| 70 | |
| @@ -93,13 +94,20 @@ | |
| 94 | blob_reset(&out); |
| 95 | markdown_to_html(&in, &title, &out); |
| 96 | blob_reset(&title); |
| 97 | break; |
| 98 | } |
| 99 | case FUZZ_ARTIFACT: { |
| 100 | fossil_fatal("FUZZ_ARTIFACT is not implemented."); |
| 101 | break; |
| 102 | } |
| 103 | case FUZZ_COMFORMAT: { |
| 104 | if( nByte>=3 && aData[1]!=0 && memchr(&aData[1], 0, nByte-1)!=0 ){ |
| 105 | int flags = (int)aData[0]; |
| 106 | comment_print((const char*)&aData[1],0,15,80,flags); |
| 107 | } |
| 108 | } |
| 109 | } |
| 110 | blob_reset(&in); |
| 111 | blob_reset(&out); |
| 112 | return 0; |
| 113 | } |
| @@ -116,10 +124,12 @@ | |
| 124 | eFuzzType = FUZZ_WIKI; |
| 125 | }else if( fossil_strcmp(zType,"markdown")==0 ){ |
| 126 | eFuzzType = FUZZ_MARKDOWN; |
| 127 | }else if( fossil_strcmp(zType,"wiki2")==0 ){ |
| 128 | eFuzzType = FUZZ_WIKI2; |
| 129 | }else if( fossil_strcmp(zType,"comformat")==0 ){ |
| 130 | eFuzzType = FUZZ_COMFORMAT; |
| 131 | }else{ |
| 132 | fossil_fatal("unknown fuzz type: \"%s\"", zType); |
| 133 | } |
| 134 | } |
| 135 | |
| @@ -139,10 +149,11 @@ | |
| 149 | ** |
| 150 | ** Usage: %fossil test-fuzz [-fuzztype TYPE] INPUTFILE... |
| 151 | ** |
| 152 | ** Run a fuzz test using INPUTFILE as the test data. TYPE can be one of: |
| 153 | ** |
| 154 | ** comformat Fuzz the comment_print() routine |
| 155 | ** wiki Fuzz the Fossil-wiki translator |
| 156 | ** markdown Fuzz the markdown translator |
| 157 | ** artifact Fuzz the artifact parser |
| 158 | ** wiki2 Fuzz the Fossil-wiki and markdown translator |
| 159 | */ |
| 160 |
+30
-12
| --- src/graph.c | ||
| +++ src/graph.c | ||
| @@ -59,11 +59,11 @@ | ||
| 59 | 59 | ** check-ins and many files. For this reason, we make the identifier |
| 60 | 60 | ** a 64-bit integer, to dramatically reduce the risk of an overflow. |
| 61 | 61 | */ |
| 62 | 62 | typedef sqlite3_int64 GraphRowId; |
| 63 | 63 | |
| 64 | -#define GR_MAX_RAIL 40 /* Max number of "rails" to display */ | |
| 64 | +#define GR_MAX_RAIL 64 /* Max number of "rails" to display */ | |
| 65 | 65 | |
| 66 | 66 | /* The graph appears vertically beside a timeline. Each row in the |
| 67 | 67 | ** timeline corresponds to a row in the graph. GraphRow.idx is 0 for |
| 68 | 68 | ** the top-most row and increases moving down. Hence (in the absence of |
| 69 | 69 | ** time skew) parents have a larger index than their children. |
| @@ -84,11 +84,11 @@ | ||
| 84 | 84 | |
| 85 | 85 | GraphRow *pNext; /* Next row down in the list of all rows */ |
| 86 | 86 | GraphRow *pPrev; /* Previous row */ |
| 87 | 87 | |
| 88 | 88 | int idx; /* Row index. Top row is smallest. */ |
| 89 | - int idxTop; /* Direct descendent highest up on the graph */ | |
| 89 | + int idxTop; /* Direct descendant highest up on the graph */ | |
| 90 | 90 | GraphRow *pChild; /* Child immediately above this node */ |
| 91 | 91 | u8 isDup; /* True if this is duplicate of a prior entry */ |
| 92 | 92 | u8 isLeaf; /* True if this is a leaf node */ |
| 93 | 93 | u8 isStepParent; /* pChild is actually a step-child. The thick |
| 94 | 94 | ** arrow up to the child is dashed, not solid */ |
| @@ -118,10 +118,11 @@ | ||
| 118 | 118 | char **azBranch; /* Names of the branches */ |
| 119 | 119 | int nRow; /* Number of rows */ |
| 120 | 120 | int nHash; /* Number of slots in apHash[] */ |
| 121 | 121 | u8 hasOffsetMergeRiser; /* Merge arrow from leaf goes up on a different |
| 122 | 122 | ** rail that the node */ |
| 123 | + u8 bOverfull; /* Unable to allocate sufficient rails */ | |
| 123 | 124 | u64 mergeRail; /* Rails used for merge lines */ |
| 124 | 125 | GraphRow **apHash; /* Hash table of GraphRow objects. Key: rid */ |
| 125 | 126 | u8 aiRailMap[GR_MAX_RAIL]; /* Mapping of rails to actually columns */ |
| 126 | 127 | }; |
| 127 | 128 | |
| @@ -336,11 +337,18 @@ | ||
| 336 | 337 | iBestDist = dist; |
| 337 | 338 | iBest = i; |
| 338 | 339 | } |
| 339 | 340 | } |
| 340 | 341 | } |
| 341 | - if( iBestDist>1000 ) p->nErr++; | |
| 342 | + if( iBestDist>1000 ){ | |
| 343 | + p->bOverfull = 1; | |
| 344 | + iBest = GR_MAX_RAIL; | |
| 345 | + } | |
| 346 | + if( iBest>GR_MAX_RAIL ){ | |
| 347 | + p->bOverfull = 1; | |
| 348 | + iBest = GR_MAX_RAIL; | |
| 349 | + } | |
| 342 | 350 | if( iBest>p->mxRail ) p->mxRail = iBest; |
| 343 | 351 | if( bMergeRail ) p->mergeRail |= BIT(iBest); |
| 344 | 352 | return iBest; |
| 345 | 353 | } |
| 346 | 354 | |
| @@ -709,11 +717,11 @@ | ||
| 709 | 717 | if( pRow->iRail>=0 ) continue; |
| 710 | 718 | if( pRow->isDup ) continue; |
| 711 | 719 | if( pRow->nParent<0 ) continue; |
| 712 | 720 | if( pRow->nParent==0 || hashFind(p,pRow->aParent[0])==0 ){ |
| 713 | 721 | pRow->iRail = findFreeRail(p, pRow->idxTop, pRow->idx+riserMargin,0,0); |
| 714 | - if( p->mxRail>=GR_MAX_RAIL ) return; | |
| 722 | + /* if( p->mxRail>=GR_MAX_RAIL ) return; */ | |
| 715 | 723 | mask = BIT(pRow->iRail); |
| 716 | 724 | if( !omitDescenders ){ |
| 717 | 725 | int n = RISER_MARGIN; |
| 718 | 726 | pRow->bDescender = pRow->nParent>0; |
| 719 | 727 | for(pLoop=pRow; pLoop && (n--)>0; pLoop=pLoop->pNext){ |
| @@ -744,28 +752,38 @@ | ||
| 744 | 752 | assert( pRow->nParent>0 ); |
| 745 | 753 | parentRid = pRow->aParent[0]; |
| 746 | 754 | pParent = hashFind(p, parentRid); |
| 747 | 755 | if( pParent==0 ){ |
| 748 | 756 | pRow->iRail = ++p->mxRail; |
| 749 | - if( p->mxRail>=GR_MAX_RAIL ) return; | |
| 757 | + if( p->mxRail>=GR_MAX_RAIL ){ | |
| 758 | + pRow->iRail = p->mxRail = GR_MAX_RAIL; | |
| 759 | + p->bOverfull = 1; | |
| 760 | + } | |
| 750 | 761 | pRow->railInUse = BIT(pRow->iRail); |
| 751 | 762 | continue; |
| 752 | 763 | } |
| 753 | 764 | if( pParent->idx>pRow->idx ){ |
| 754 | 765 | /* Common case: Child occurs after parent and is above the |
| 755 | 766 | ** parent in the timeline */ |
| 756 | 767 | pRow->iRail = findFreeRail(p, pRow->idxTop, pParent->idx, |
| 757 | 768 | pParent->iRail, 0); |
| 758 | - if( p->mxRail>=GR_MAX_RAIL ) return; | |
| 769 | + /* if( p->mxRail>=GR_MAX_RAIL ) return; */ | |
| 759 | 770 | pParent->aiRiser[pRow->iRail] = pRow->idx; |
| 760 | 771 | }else{ |
| 761 | 772 | /* Timewarp case: Child occurs earlier in time than parent and |
| 762 | 773 | ** appears below the parent in the timeline. */ |
| 763 | 774 | int iDownRail = ++p->mxRail; |
| 764 | 775 | if( iDownRail<1 ) iDownRail = ++p->mxRail; |
| 776 | + if( p->mxRail>GR_MAX_RAIL ){ | |
| 777 | + iDownRail = p->mxRail = GR_MAX_RAIL; | |
| 778 | + p->bOverfull = 1; | |
| 779 | + } | |
| 765 | 780 | pRow->iRail = ++p->mxRail; |
| 766 | - if( p->mxRail>=GR_MAX_RAIL ) return; | |
| 781 | + if( p->mxRail>=GR_MAX_RAIL ){ | |
| 782 | + pRow->iRail = p->mxRail = GR_MAX_RAIL; | |
| 783 | + p->bOverfull = 1; | |
| 784 | + } | |
| 767 | 785 | pRow->railInUse = BIT(pRow->iRail); |
| 768 | 786 | pParent->aiRiser[iDownRail] = pRow->idx; |
| 769 | 787 | mask = BIT(iDownRail); |
| 770 | 788 | for(pLoop=p->pFirst; pLoop; pLoop=pLoop->pNext){ |
| 771 | 789 | pLoop->railInUse |= mask; |
| @@ -824,11 +842,11 @@ | ||
| 824 | 842 | break; |
| 825 | 843 | } |
| 826 | 844 | } |
| 827 | 845 | if( iMrail==-1 ){ |
| 828 | 846 | iMrail = findFreeRail(p, pRow->idx, p->pLast->idx, 0, 1); |
| 829 | - if( p->mxRail>=GR_MAX_RAIL ) return; | |
| 847 | + /*if( p->mxRail>=GR_MAX_RAIL ) return;*/ | |
| 830 | 848 | mergeRiserFrom[iMrail] = parentRid; |
| 831 | 849 | } |
| 832 | 850 | iReuseIdx = p->nRow+1; |
| 833 | 851 | iReuseRail = iMrail; |
| 834 | 852 | mask = BIT(iMrail); |
| @@ -856,11 +874,11 @@ | ||
| 856 | 874 | pDesc->mergeUpto = pDesc->idx; |
| 857 | 875 | } |
| 858 | 876 | }else{ |
| 859 | 877 | /* Create a new merge for an on-screen node */ |
| 860 | 878 | createMergeRiser(p, pDesc, pRow, isCherrypick); |
| 861 | - if( p->mxRail>=GR_MAX_RAIL ) return; | |
| 879 | + /* if( p->mxRail>=GR_MAX_RAIL ) return; */ | |
| 862 | 880 | if( iReuseIdx<0 |
| 863 | 881 | && pDesc->nMergeChild==1 |
| 864 | 882 | && (pDesc->iRail!=pDesc->mergeOut || pDesc->isLeaf) |
| 865 | 883 | ){ |
| 866 | 884 | iReuseIdx = pDesc->idx; |
| @@ -872,17 +890,17 @@ | ||
| 872 | 890 | } |
| 873 | 891 | |
| 874 | 892 | /* |
| 875 | 893 | ** Insert merge rails from primaries to duplicates. |
| 876 | 894 | */ |
| 877 | - if( hasDup ){ | |
| 895 | + if( hasDup && p->mxRail<GR_MAX_RAIL ){ | |
| 878 | 896 | int dupRail; |
| 879 | 897 | int mxRail; |
| 880 | 898 | find_max_rail(p); |
| 881 | 899 | mxRail = p->mxRail; |
| 882 | 900 | dupRail = mxRail+1; |
| 883 | - if( p->mxRail>=GR_MAX_RAIL ) return; | |
| 901 | + /* if( p->mxRail>=GR_MAX_RAIL ) return; */ | |
| 884 | 902 | for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ |
| 885 | 903 | if( !pRow->isDup ) continue; |
| 886 | 904 | pRow->iRail = dupRail; |
| 887 | 905 | pDesc = hashFind(p, pRow->rid); |
| 888 | 906 | assert( pDesc!=0 && pDesc!=pRow ); |
| @@ -893,11 +911,11 @@ | ||
| 893 | 911 | dupRail = mxRail+1; |
| 894 | 912 | for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ |
| 895 | 913 | if( pRow->isDup ) pRow->iRail = dupRail; |
| 896 | 914 | } |
| 897 | 915 | } |
| 898 | - if( mxRail>=GR_MAX_RAIL ) return; | |
| 916 | + /* if( mxRail>=GR_MAX_RAIL ) return; */ | |
| 899 | 917 | } |
| 900 | 918 | |
| 901 | 919 | /* |
| 902 | 920 | ** Find the maximum rail number. |
| 903 | 921 | */ |
| 904 | 922 |
| --- src/graph.c | |
| +++ src/graph.c | |
| @@ -59,11 +59,11 @@ | |
| 59 | ** check-ins and many files. For this reason, we make the identifier |
| 60 | ** a 64-bit integer, to dramatically reduce the risk of an overflow. |
| 61 | */ |
| 62 | typedef sqlite3_int64 GraphRowId; |
| 63 | |
| 64 | #define GR_MAX_RAIL 40 /* Max number of "rails" to display */ |
| 65 | |
| 66 | /* The graph appears vertically beside a timeline. Each row in the |
| 67 | ** timeline corresponds to a row in the graph. GraphRow.idx is 0 for |
| 68 | ** the top-most row and increases moving down. Hence (in the absence of |
| 69 | ** time skew) parents have a larger index than their children. |
| @@ -84,11 +84,11 @@ | |
| 84 | |
| 85 | GraphRow *pNext; /* Next row down in the list of all rows */ |
| 86 | GraphRow *pPrev; /* Previous row */ |
| 87 | |
| 88 | int idx; /* Row index. Top row is smallest. */ |
| 89 | int idxTop; /* Direct descendent highest up on the graph */ |
| 90 | GraphRow *pChild; /* Child immediately above this node */ |
| 91 | u8 isDup; /* True if this is duplicate of a prior entry */ |
| 92 | u8 isLeaf; /* True if this is a leaf node */ |
| 93 | u8 isStepParent; /* pChild is actually a step-child. The thick |
| 94 | ** arrow up to the child is dashed, not solid */ |
| @@ -118,10 +118,11 @@ | |
| 118 | char **azBranch; /* Names of the branches */ |
| 119 | int nRow; /* Number of rows */ |
| 120 | int nHash; /* Number of slots in apHash[] */ |
| 121 | u8 hasOffsetMergeRiser; /* Merge arrow from leaf goes up on a different |
| 122 | ** rail that the node */ |
| 123 | u64 mergeRail; /* Rails used for merge lines */ |
| 124 | GraphRow **apHash; /* Hash table of GraphRow objects. Key: rid */ |
| 125 | u8 aiRailMap[GR_MAX_RAIL]; /* Mapping of rails to actually columns */ |
| 126 | }; |
| 127 | |
| @@ -336,11 +337,18 @@ | |
| 336 | iBestDist = dist; |
| 337 | iBest = i; |
| 338 | } |
| 339 | } |
| 340 | } |
| 341 | if( iBestDist>1000 ) p->nErr++; |
| 342 | if( iBest>p->mxRail ) p->mxRail = iBest; |
| 343 | if( bMergeRail ) p->mergeRail |= BIT(iBest); |
| 344 | return iBest; |
| 345 | } |
| 346 | |
| @@ -709,11 +717,11 @@ | |
| 709 | if( pRow->iRail>=0 ) continue; |
| 710 | if( pRow->isDup ) continue; |
| 711 | if( pRow->nParent<0 ) continue; |
| 712 | if( pRow->nParent==0 || hashFind(p,pRow->aParent[0])==0 ){ |
| 713 | pRow->iRail = findFreeRail(p, pRow->idxTop, pRow->idx+riserMargin,0,0); |
| 714 | if( p->mxRail>=GR_MAX_RAIL ) return; |
| 715 | mask = BIT(pRow->iRail); |
| 716 | if( !omitDescenders ){ |
| 717 | int n = RISER_MARGIN; |
| 718 | pRow->bDescender = pRow->nParent>0; |
| 719 | for(pLoop=pRow; pLoop && (n--)>0; pLoop=pLoop->pNext){ |
| @@ -744,28 +752,38 @@ | |
| 744 | assert( pRow->nParent>0 ); |
| 745 | parentRid = pRow->aParent[0]; |
| 746 | pParent = hashFind(p, parentRid); |
| 747 | if( pParent==0 ){ |
| 748 | pRow->iRail = ++p->mxRail; |
| 749 | if( p->mxRail>=GR_MAX_RAIL ) return; |
| 750 | pRow->railInUse = BIT(pRow->iRail); |
| 751 | continue; |
| 752 | } |
| 753 | if( pParent->idx>pRow->idx ){ |
| 754 | /* Common case: Child occurs after parent and is above the |
| 755 | ** parent in the timeline */ |
| 756 | pRow->iRail = findFreeRail(p, pRow->idxTop, pParent->idx, |
| 757 | pParent->iRail, 0); |
| 758 | if( p->mxRail>=GR_MAX_RAIL ) return; |
| 759 | pParent->aiRiser[pRow->iRail] = pRow->idx; |
| 760 | }else{ |
| 761 | /* Timewarp case: Child occurs earlier in time than parent and |
| 762 | ** appears below the parent in the timeline. */ |
| 763 | int iDownRail = ++p->mxRail; |
| 764 | if( iDownRail<1 ) iDownRail = ++p->mxRail; |
| 765 | pRow->iRail = ++p->mxRail; |
| 766 | if( p->mxRail>=GR_MAX_RAIL ) return; |
| 767 | pRow->railInUse = BIT(pRow->iRail); |
| 768 | pParent->aiRiser[iDownRail] = pRow->idx; |
| 769 | mask = BIT(iDownRail); |
| 770 | for(pLoop=p->pFirst; pLoop; pLoop=pLoop->pNext){ |
| 771 | pLoop->railInUse |= mask; |
| @@ -824,11 +842,11 @@ | |
| 824 | break; |
| 825 | } |
| 826 | } |
| 827 | if( iMrail==-1 ){ |
| 828 | iMrail = findFreeRail(p, pRow->idx, p->pLast->idx, 0, 1); |
| 829 | if( p->mxRail>=GR_MAX_RAIL ) return; |
| 830 | mergeRiserFrom[iMrail] = parentRid; |
| 831 | } |
| 832 | iReuseIdx = p->nRow+1; |
| 833 | iReuseRail = iMrail; |
| 834 | mask = BIT(iMrail); |
| @@ -856,11 +874,11 @@ | |
| 856 | pDesc->mergeUpto = pDesc->idx; |
| 857 | } |
| 858 | }else{ |
| 859 | /* Create a new merge for an on-screen node */ |
| 860 | createMergeRiser(p, pDesc, pRow, isCherrypick); |
| 861 | if( p->mxRail>=GR_MAX_RAIL ) return; |
| 862 | if( iReuseIdx<0 |
| 863 | && pDesc->nMergeChild==1 |
| 864 | && (pDesc->iRail!=pDesc->mergeOut || pDesc->isLeaf) |
| 865 | ){ |
| 866 | iReuseIdx = pDesc->idx; |
| @@ -872,17 +890,17 @@ | |
| 872 | } |
| 873 | |
| 874 | /* |
| 875 | ** Insert merge rails from primaries to duplicates. |
| 876 | */ |
| 877 | if( hasDup ){ |
| 878 | int dupRail; |
| 879 | int mxRail; |
| 880 | find_max_rail(p); |
| 881 | mxRail = p->mxRail; |
| 882 | dupRail = mxRail+1; |
| 883 | if( p->mxRail>=GR_MAX_RAIL ) return; |
| 884 | for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ |
| 885 | if( !pRow->isDup ) continue; |
| 886 | pRow->iRail = dupRail; |
| 887 | pDesc = hashFind(p, pRow->rid); |
| 888 | assert( pDesc!=0 && pDesc!=pRow ); |
| @@ -893,11 +911,11 @@ | |
| 893 | dupRail = mxRail+1; |
| 894 | for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ |
| 895 | if( pRow->isDup ) pRow->iRail = dupRail; |
| 896 | } |
| 897 | } |
| 898 | if( mxRail>=GR_MAX_RAIL ) return; |
| 899 | } |
| 900 | |
| 901 | /* |
| 902 | ** Find the maximum rail number. |
| 903 | */ |
| 904 |
| --- src/graph.c | |
| +++ src/graph.c | |
| @@ -59,11 +59,11 @@ | |
| 59 | ** check-ins and many files. For this reason, we make the identifier |
| 60 | ** a 64-bit integer, to dramatically reduce the risk of an overflow. |
| 61 | */ |
| 62 | typedef sqlite3_int64 GraphRowId; |
| 63 | |
| 64 | #define GR_MAX_RAIL 64 /* Max number of "rails" to display */ |
| 65 | |
| 66 | /* The graph appears vertically beside a timeline. Each row in the |
| 67 | ** timeline corresponds to a row in the graph. GraphRow.idx is 0 for |
| 68 | ** the top-most row and increases moving down. Hence (in the absence of |
| 69 | ** time skew) parents have a larger index than their children. |
| @@ -84,11 +84,11 @@ | |
| 84 | |
| 85 | GraphRow *pNext; /* Next row down in the list of all rows */ |
| 86 | GraphRow *pPrev; /* Previous row */ |
| 87 | |
| 88 | int idx; /* Row index. Top row is smallest. */ |
| 89 | int idxTop; /* Direct descendant highest up on the graph */ |
| 90 | GraphRow *pChild; /* Child immediately above this node */ |
| 91 | u8 isDup; /* True if this is duplicate of a prior entry */ |
| 92 | u8 isLeaf; /* True if this is a leaf node */ |
| 93 | u8 isStepParent; /* pChild is actually a step-child. The thick |
| 94 | ** arrow up to the child is dashed, not solid */ |
| @@ -118,10 +118,11 @@ | |
| 118 | char **azBranch; /* Names of the branches */ |
| 119 | int nRow; /* Number of rows */ |
| 120 | int nHash; /* Number of slots in apHash[] */ |
| 121 | u8 hasOffsetMergeRiser; /* Merge arrow from leaf goes up on a different |
| 122 | ** rail that the node */ |
| 123 | u8 bOverfull; /* Unable to allocate sufficient rails */ |
| 124 | u64 mergeRail; /* Rails used for merge lines */ |
| 125 | GraphRow **apHash; /* Hash table of GraphRow objects. Key: rid */ |
| 126 | u8 aiRailMap[GR_MAX_RAIL]; /* Mapping of rails to actually columns */ |
| 127 | }; |
| 128 | |
| @@ -336,11 +337,18 @@ | |
| 337 | iBestDist = dist; |
| 338 | iBest = i; |
| 339 | } |
| 340 | } |
| 341 | } |
| 342 | if( iBestDist>1000 ){ |
| 343 | p->bOverfull = 1; |
| 344 | iBest = GR_MAX_RAIL; |
| 345 | } |
| 346 | if( iBest>GR_MAX_RAIL ){ |
| 347 | p->bOverfull = 1; |
| 348 | iBest = GR_MAX_RAIL; |
| 349 | } |
| 350 | if( iBest>p->mxRail ) p->mxRail = iBest; |
| 351 | if( bMergeRail ) p->mergeRail |= BIT(iBest); |
| 352 | return iBest; |
| 353 | } |
| 354 | |
| @@ -709,11 +717,11 @@ | |
| 717 | if( pRow->iRail>=0 ) continue; |
| 718 | if( pRow->isDup ) continue; |
| 719 | if( pRow->nParent<0 ) continue; |
| 720 | if( pRow->nParent==0 || hashFind(p,pRow->aParent[0])==0 ){ |
| 721 | pRow->iRail = findFreeRail(p, pRow->idxTop, pRow->idx+riserMargin,0,0); |
| 722 | /* if( p->mxRail>=GR_MAX_RAIL ) return; */ |
| 723 | mask = BIT(pRow->iRail); |
| 724 | if( !omitDescenders ){ |
| 725 | int n = RISER_MARGIN; |
| 726 | pRow->bDescender = pRow->nParent>0; |
| 727 | for(pLoop=pRow; pLoop && (n--)>0; pLoop=pLoop->pNext){ |
| @@ -744,28 +752,38 @@ | |
| 752 | assert( pRow->nParent>0 ); |
| 753 | parentRid = pRow->aParent[0]; |
| 754 | pParent = hashFind(p, parentRid); |
| 755 | if( pParent==0 ){ |
| 756 | pRow->iRail = ++p->mxRail; |
| 757 | if( p->mxRail>=GR_MAX_RAIL ){ |
| 758 | pRow->iRail = p->mxRail = GR_MAX_RAIL; |
| 759 | p->bOverfull = 1; |
| 760 | } |
| 761 | pRow->railInUse = BIT(pRow->iRail); |
| 762 | continue; |
| 763 | } |
| 764 | if( pParent->idx>pRow->idx ){ |
| 765 | /* Common case: Child occurs after parent and is above the |
| 766 | ** parent in the timeline */ |
| 767 | pRow->iRail = findFreeRail(p, pRow->idxTop, pParent->idx, |
| 768 | pParent->iRail, 0); |
| 769 | /* if( p->mxRail>=GR_MAX_RAIL ) return; */ |
| 770 | pParent->aiRiser[pRow->iRail] = pRow->idx; |
| 771 | }else{ |
| 772 | /* Timewarp case: Child occurs earlier in time than parent and |
| 773 | ** appears below the parent in the timeline. */ |
| 774 | int iDownRail = ++p->mxRail; |
| 775 | if( iDownRail<1 ) iDownRail = ++p->mxRail; |
| 776 | if( p->mxRail>GR_MAX_RAIL ){ |
| 777 | iDownRail = p->mxRail = GR_MAX_RAIL; |
| 778 | p->bOverfull = 1; |
| 779 | } |
| 780 | pRow->iRail = ++p->mxRail; |
| 781 | if( p->mxRail>=GR_MAX_RAIL ){ |
| 782 | pRow->iRail = p->mxRail = GR_MAX_RAIL; |
| 783 | p->bOverfull = 1; |
| 784 | } |
| 785 | pRow->railInUse = BIT(pRow->iRail); |
| 786 | pParent->aiRiser[iDownRail] = pRow->idx; |
| 787 | mask = BIT(iDownRail); |
| 788 | for(pLoop=p->pFirst; pLoop; pLoop=pLoop->pNext){ |
| 789 | pLoop->railInUse |= mask; |
| @@ -824,11 +842,11 @@ | |
| 842 | break; |
| 843 | } |
| 844 | } |
| 845 | if( iMrail==-1 ){ |
| 846 | iMrail = findFreeRail(p, pRow->idx, p->pLast->idx, 0, 1); |
| 847 | /*if( p->mxRail>=GR_MAX_RAIL ) return;*/ |
| 848 | mergeRiserFrom[iMrail] = parentRid; |
| 849 | } |
| 850 | iReuseIdx = p->nRow+1; |
| 851 | iReuseRail = iMrail; |
| 852 | mask = BIT(iMrail); |
| @@ -856,11 +874,11 @@ | |
| 874 | pDesc->mergeUpto = pDesc->idx; |
| 875 | } |
| 876 | }else{ |
| 877 | /* Create a new merge for an on-screen node */ |
| 878 | createMergeRiser(p, pDesc, pRow, isCherrypick); |
| 879 | /* if( p->mxRail>=GR_MAX_RAIL ) return; */ |
| 880 | if( iReuseIdx<0 |
| 881 | && pDesc->nMergeChild==1 |
| 882 | && (pDesc->iRail!=pDesc->mergeOut || pDesc->isLeaf) |
| 883 | ){ |
| 884 | iReuseIdx = pDesc->idx; |
| @@ -872,17 +890,17 @@ | |
| 890 | } |
| 891 | |
| 892 | /* |
| 893 | ** Insert merge rails from primaries to duplicates. |
| 894 | */ |
| 895 | if( hasDup && p->mxRail<GR_MAX_RAIL ){ |
| 896 | int dupRail; |
| 897 | int mxRail; |
| 898 | find_max_rail(p); |
| 899 | mxRail = p->mxRail; |
| 900 | dupRail = mxRail+1; |
| 901 | /* if( p->mxRail>=GR_MAX_RAIL ) return; */ |
| 902 | for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ |
| 903 | if( !pRow->isDup ) continue; |
| 904 | pRow->iRail = dupRail; |
| 905 | pDesc = hashFind(p, pRow->rid); |
| 906 | assert( pDesc!=0 && pDesc!=pRow ); |
| @@ -893,11 +911,11 @@ | |
| 911 | dupRail = mxRail+1; |
| 912 | for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ |
| 913 | if( pRow->isDup ) pRow->iRail = dupRail; |
| 914 | } |
| 915 | } |
| 916 | /* if( mxRail>=GR_MAX_RAIL ) return; */ |
| 917 | } |
| 918 | |
| 919 | /* |
| 920 | ** Find the maximum rail number. |
| 921 | */ |
| 922 |
+6
-6
| --- src/hook.c | ||
| +++ src/hook.c | ||
| @@ -198,39 +198,39 @@ | ||
| 198 | 198 | ** |
| 199 | 199 | ** Usage: %fossil hook COMMAND ... |
| 200 | 200 | ** |
| 201 | 201 | ** Commands include: |
| 202 | 202 | ** |
| 203 | -** > fossil hook add --command COMMAND --type TYPE --sequence NUMBER | |
| 203 | +** > fossil hook add --command COMMAND --type TYPE --sequence NUMBER | |
| 204 | 204 | ** |
| 205 | 205 | ** Create a new hook. The --command and --type arguments are |
| 206 | 206 | ** required. --sequence is optional. |
| 207 | 207 | ** |
| 208 | -** > fossil hook delete ID ... | |
| 208 | +** > fossil hook delete ID ... | |
| 209 | 209 | ** |
| 210 | 210 | ** Delete one or more hooks by their IDs. ID can be "all" |
| 211 | 211 | ** to delete all hooks. Caution: There is no "undo" for |
| 212 | 212 | ** this operation. Deleted hooks are permanently lost. |
| 213 | 213 | ** |
| 214 | -** > fossil hook edit --command COMMAND --type TYPE --sequence NUMBER ID ... | |
| 214 | +** > fossil hook edit --command COMMAND --type TYPE --sequence NUMBER ID ... | |
| 215 | 215 | ** |
| 216 | 216 | ** Make changes to one or more existing hooks. The ID argument |
| 217 | 217 | ** is either a hook-id, or a list of hook-ids, or the keyword |
| 218 | 218 | ** "all". For example, to disable hook number 2, use: |
| 219 | 219 | ** |
| 220 | 220 | ** fossil hook edit --type disabled 2 |
| 221 | 221 | ** |
| 222 | -** > fossil hook list | |
| 222 | +** > fossil hook list | |
| 223 | 223 | ** |
| 224 | 224 | ** Show all current hooks |
| 225 | 225 | ** |
| 226 | -** > fossil hook status | |
| 226 | +** > fossil hook status | |
| 227 | 227 | ** |
| 228 | 228 | ** Print the values of CONFIG table entries that are relevant to |
| 229 | 229 | ** hook processing. Used for debugging. |
| 230 | 230 | ** |
| 231 | -** > fossil hook test [OPTIONS] ID | |
| 231 | +** > fossil hook test [OPTIONS] ID | |
| 232 | 232 | ** |
| 233 | 233 | ** Run the hook script given by ID for testing purposes. |
| 234 | 234 | ** Options: |
| 235 | 235 | ** |
| 236 | 236 | ** --dry-run Print the script on stdout rather than run it |
| 237 | 237 |
| --- src/hook.c | |
| +++ src/hook.c | |
| @@ -198,39 +198,39 @@ | |
| 198 | ** |
| 199 | ** Usage: %fossil hook COMMAND ... |
| 200 | ** |
| 201 | ** Commands include: |
| 202 | ** |
| 203 | ** > fossil hook add --command COMMAND --type TYPE --sequence NUMBER |
| 204 | ** |
| 205 | ** Create a new hook. The --command and --type arguments are |
| 206 | ** required. --sequence is optional. |
| 207 | ** |
| 208 | ** > fossil hook delete ID ... |
| 209 | ** |
| 210 | ** Delete one or more hooks by their IDs. ID can be "all" |
| 211 | ** to delete all hooks. Caution: There is no "undo" for |
| 212 | ** this operation. Deleted hooks are permanently lost. |
| 213 | ** |
| 214 | ** > fossil hook edit --command COMMAND --type TYPE --sequence NUMBER ID ... |
| 215 | ** |
| 216 | ** Make changes to one or more existing hooks. The ID argument |
| 217 | ** is either a hook-id, or a list of hook-ids, or the keyword |
| 218 | ** "all". For example, to disable hook number 2, use: |
| 219 | ** |
| 220 | ** fossil hook edit --type disabled 2 |
| 221 | ** |
| 222 | ** > fossil hook list |
| 223 | ** |
| 224 | ** Show all current hooks |
| 225 | ** |
| 226 | ** > fossil hook status |
| 227 | ** |
| 228 | ** Print the values of CONFIG table entries that are relevant to |
| 229 | ** hook processing. Used for debugging. |
| 230 | ** |
| 231 | ** > fossil hook test [OPTIONS] ID |
| 232 | ** |
| 233 | ** Run the hook script given by ID for testing purposes. |
| 234 | ** Options: |
| 235 | ** |
| 236 | ** --dry-run Print the script on stdout rather than run it |
| 237 |
| --- src/hook.c | |
| +++ src/hook.c | |
| @@ -198,39 +198,39 @@ | |
| 198 | ** |
| 199 | ** Usage: %fossil hook COMMAND ... |
| 200 | ** |
| 201 | ** Commands include: |
| 202 | ** |
| 203 | ** > fossil hook add --command COMMAND --type TYPE --sequence NUMBER |
| 204 | ** |
| 205 | ** Create a new hook. The --command and --type arguments are |
| 206 | ** required. --sequence is optional. |
| 207 | ** |
| 208 | ** > fossil hook delete ID ... |
| 209 | ** |
| 210 | ** Delete one or more hooks by their IDs. ID can be "all" |
| 211 | ** to delete all hooks. Caution: There is no "undo" for |
| 212 | ** this operation. Deleted hooks are permanently lost. |
| 213 | ** |
| 214 | ** > fossil hook edit --command COMMAND --type TYPE --sequence NUMBER ID ... |
| 215 | ** |
| 216 | ** Make changes to one or more existing hooks. The ID argument |
| 217 | ** is either a hook-id, or a list of hook-ids, or the keyword |
| 218 | ** "all". For example, to disable hook number 2, use: |
| 219 | ** |
| 220 | ** fossil hook edit --type disabled 2 |
| 221 | ** |
| 222 | ** > fossil hook list |
| 223 | ** |
| 224 | ** Show all current hooks |
| 225 | ** |
| 226 | ** > fossil hook status |
| 227 | ** |
| 228 | ** Print the values of CONFIG table entries that are relevant to |
| 229 | ** hook processing. Used for debugging. |
| 230 | ** |
| 231 | ** > fossil hook test [OPTIONS] ID |
| 232 | ** |
| 233 | ** Run the hook script given by ID for testing purposes. |
| 234 | ** Options: |
| 235 | ** |
| 236 | ** --dry-run Print the script on stdout rather than run it |
| 237 |
+10
-1
| --- src/http.c | ||
| +++ src/http.c | ||
| @@ -512,10 +512,13 @@ | ||
| 512 | 512 | transport_send(&g.url, &hdr); |
| 513 | 513 | transport_send(&g.url, &payload); |
| 514 | 514 | blob_reset(&hdr); |
| 515 | 515 | blob_reset(&payload); |
| 516 | 516 | transport_flip(&g.url); |
| 517 | + if( mHttpFlags & HTTP_VERBOSE ){ | |
| 518 | + fossil_print("IP-Address: %s\n", g.zIpAddr); | |
| 519 | + } | |
| 517 | 520 | |
| 518 | 521 | /* |
| 519 | 522 | ** Read and interpret the server reply |
| 520 | 523 | */ |
| 521 | 524 | closeConnection = 1; |
| @@ -572,10 +575,11 @@ | ||
| 572 | 575 | } |
| 573 | 576 | }else if( ( rc==301 || rc==302 || rc==307 || rc==308 ) && |
| 574 | 577 | fossil_strnicmp(zLine, "location:", 9)==0 ){ |
| 575 | 578 | int i, j; |
| 576 | 579 | int wasHttps; |
| 580 | + int priorUrlFlags; | |
| 577 | 581 | |
| 578 | 582 | if ( --maxRedirect == 0){ |
| 579 | 583 | fossil_warning("redirect limit exceeded"); |
| 580 | 584 | goto write_err; |
| 581 | 585 | } |
| @@ -596,10 +600,11 @@ | ||
| 596 | 600 | fossil_warning("cannot redirect from %s to %s", g.url.canonical, |
| 597 | 601 | &zLine[i]); |
| 598 | 602 | goto write_err; |
| 599 | 603 | } |
| 600 | 604 | wasHttps = g.url.isHttps; |
| 605 | + priorUrlFlags = g.url.flags; | |
| 601 | 606 | url_parse(&zLine[i], 0); |
| 602 | 607 | if( wasHttps && !g.url.isHttps ){ |
| 603 | 608 | fossil_warning("cannot redirect from HTTPS to HTTP"); |
| 604 | 609 | goto write_err; |
| 605 | 610 | } |
| @@ -610,11 +615,14 @@ | ||
| 610 | 615 | transport_close(&g.url); |
| 611 | 616 | transport_global_shutdown(&g.url); |
| 612 | 617 | fSeenHttpAuth = 0; |
| 613 | 618 | if( g.zHttpAuth ) free(g.zHttpAuth); |
| 614 | 619 | g.zHttpAuth = get_httpauth(); |
| 615 | - if( rc==301 || rc==308 ) url_remember(); | |
| 620 | + if( (rc==301 || rc==308) && (priorUrlFlags & URL_REMEMBER)!=0 ){ | |
| 621 | + g.url.flags |= URL_REMEMBER; | |
| 622 | + url_remember(); | |
| 623 | + } | |
| 616 | 624 | return http_exchange(pSend, pReply, mHttpFlags, |
| 617 | 625 | maxRedirect, zAltMimetype); |
| 618 | 626 | }else if( fossil_strnicmp(zLine, "content-type: ", 14)==0 ){ |
| 619 | 627 | if( fossil_strnicmp(&zLine[14], "application/x-fossil-debug", -1)==0 ){ |
| 620 | 628 | isCompressed = 0; |
| @@ -793,10 +801,11 @@ | ||
| 793 | 801 | } |
| 794 | 802 | if( find_option("xfer",0,0)!=0 ){ |
| 795 | 803 | mHttpFlags |= HTTP_USE_LOGIN; |
| 796 | 804 | mHttpFlags &= ~HTTP_GENERIC; |
| 797 | 805 | } |
| 806 | + if( find_option("ipv4",0,0) ) g.fIPv4 = 1; | |
| 798 | 807 | verify_all_options(); |
| 799 | 808 | if( g.argc<3 || g.argc>5 ){ |
| 800 | 809 | usage("URL ?PAYLOAD? ?OUTPUT?"); |
| 801 | 810 | } |
| 802 | 811 | zInFile = g.argc>=4 ? g.argv[3] : 0; |
| 803 | 812 |
| --- src/http.c | |
| +++ src/http.c | |
| @@ -512,10 +512,13 @@ | |
| 512 | transport_send(&g.url, &hdr); |
| 513 | transport_send(&g.url, &payload); |
| 514 | blob_reset(&hdr); |
| 515 | blob_reset(&payload); |
| 516 | transport_flip(&g.url); |
| 517 | |
| 518 | /* |
| 519 | ** Read and interpret the server reply |
| 520 | */ |
| 521 | closeConnection = 1; |
| @@ -572,10 +575,11 @@ | |
| 572 | } |
| 573 | }else if( ( rc==301 || rc==302 || rc==307 || rc==308 ) && |
| 574 | fossil_strnicmp(zLine, "location:", 9)==0 ){ |
| 575 | int i, j; |
| 576 | int wasHttps; |
| 577 | |
| 578 | if ( --maxRedirect == 0){ |
| 579 | fossil_warning("redirect limit exceeded"); |
| 580 | goto write_err; |
| 581 | } |
| @@ -596,10 +600,11 @@ | |
| 596 | fossil_warning("cannot redirect from %s to %s", g.url.canonical, |
| 597 | &zLine[i]); |
| 598 | goto write_err; |
| 599 | } |
| 600 | wasHttps = g.url.isHttps; |
| 601 | url_parse(&zLine[i], 0); |
| 602 | if( wasHttps && !g.url.isHttps ){ |
| 603 | fossil_warning("cannot redirect from HTTPS to HTTP"); |
| 604 | goto write_err; |
| 605 | } |
| @@ -610,11 +615,14 @@ | |
| 610 | transport_close(&g.url); |
| 611 | transport_global_shutdown(&g.url); |
| 612 | fSeenHttpAuth = 0; |
| 613 | if( g.zHttpAuth ) free(g.zHttpAuth); |
| 614 | g.zHttpAuth = get_httpauth(); |
| 615 | if( rc==301 || rc==308 ) url_remember(); |
| 616 | return http_exchange(pSend, pReply, mHttpFlags, |
| 617 | maxRedirect, zAltMimetype); |
| 618 | }else if( fossil_strnicmp(zLine, "content-type: ", 14)==0 ){ |
| 619 | if( fossil_strnicmp(&zLine[14], "application/x-fossil-debug", -1)==0 ){ |
| 620 | isCompressed = 0; |
| @@ -793,10 +801,11 @@ | |
| 793 | } |
| 794 | if( find_option("xfer",0,0)!=0 ){ |
| 795 | mHttpFlags |= HTTP_USE_LOGIN; |
| 796 | mHttpFlags &= ~HTTP_GENERIC; |
| 797 | } |
| 798 | verify_all_options(); |
| 799 | if( g.argc<3 || g.argc>5 ){ |
| 800 | usage("URL ?PAYLOAD? ?OUTPUT?"); |
| 801 | } |
| 802 | zInFile = g.argc>=4 ? g.argv[3] : 0; |
| 803 |
| --- src/http.c | |
| +++ src/http.c | |
| @@ -512,10 +512,13 @@ | |
| 512 | transport_send(&g.url, &hdr); |
| 513 | transport_send(&g.url, &payload); |
| 514 | blob_reset(&hdr); |
| 515 | blob_reset(&payload); |
| 516 | transport_flip(&g.url); |
| 517 | if( mHttpFlags & HTTP_VERBOSE ){ |
| 518 | fossil_print("IP-Address: %s\n", g.zIpAddr); |
| 519 | } |
| 520 | |
| 521 | /* |
| 522 | ** Read and interpret the server reply |
| 523 | */ |
| 524 | closeConnection = 1; |
| @@ -572,10 +575,11 @@ | |
| 575 | } |
| 576 | }else if( ( rc==301 || rc==302 || rc==307 || rc==308 ) && |
| 577 | fossil_strnicmp(zLine, "location:", 9)==0 ){ |
| 578 | int i, j; |
| 579 | int wasHttps; |
| 580 | int priorUrlFlags; |
| 581 | |
| 582 | if ( --maxRedirect == 0){ |
| 583 | fossil_warning("redirect limit exceeded"); |
| 584 | goto write_err; |
| 585 | } |
| @@ -596,10 +600,11 @@ | |
| 600 | fossil_warning("cannot redirect from %s to %s", g.url.canonical, |
| 601 | &zLine[i]); |
| 602 | goto write_err; |
| 603 | } |
| 604 | wasHttps = g.url.isHttps; |
| 605 | priorUrlFlags = g.url.flags; |
| 606 | url_parse(&zLine[i], 0); |
| 607 | if( wasHttps && !g.url.isHttps ){ |
| 608 | fossil_warning("cannot redirect from HTTPS to HTTP"); |
| 609 | goto write_err; |
| 610 | } |
| @@ -610,11 +615,14 @@ | |
| 615 | transport_close(&g.url); |
| 616 | transport_global_shutdown(&g.url); |
| 617 | fSeenHttpAuth = 0; |
| 618 | if( g.zHttpAuth ) free(g.zHttpAuth); |
| 619 | g.zHttpAuth = get_httpauth(); |
| 620 | if( (rc==301 || rc==308) && (priorUrlFlags & URL_REMEMBER)!=0 ){ |
| 621 | g.url.flags |= URL_REMEMBER; |
| 622 | url_remember(); |
| 623 | } |
| 624 | return http_exchange(pSend, pReply, mHttpFlags, |
| 625 | maxRedirect, zAltMimetype); |
| 626 | }else if( fossil_strnicmp(zLine, "content-type: ", 14)==0 ){ |
| 627 | if( fossil_strnicmp(&zLine[14], "application/x-fossil-debug", -1)==0 ){ |
| 628 | isCompressed = 0; |
| @@ -793,10 +801,11 @@ | |
| 801 | } |
| 802 | if( find_option("xfer",0,0)!=0 ){ |
| 803 | mHttpFlags |= HTTP_USE_LOGIN; |
| 804 | mHttpFlags &= ~HTTP_GENERIC; |
| 805 | } |
| 806 | if( find_option("ipv4",0,0) ) g.fIPv4 = 1; |
| 807 | verify_all_options(); |
| 808 | if( g.argc<3 || g.argc>5 ){ |
| 809 | usage("URL ?PAYLOAD? ?OUTPUT?"); |
| 810 | } |
| 811 | zInFile = g.argc>=4 ? g.argv[3] : 0; |
| 812 |
+28
-14
| --- src/http_ssl.c | ||
| +++ src/http_ssl.c | ||
| @@ -451,11 +451,18 @@ | ||
| 451 | 451 | ssl_global_init_client(); |
| 452 | 452 | if( pUrlData->useProxy ){ |
| 453 | 453 | int rc; |
| 454 | 454 | char *connStr = mprintf("%s:%d", g.url.name, pUrlData->port); |
| 455 | 455 | BIO *sBio = BIO_new_connect(connStr); |
| 456 | - free(connStr); | |
| 456 | + if( g.fIPv4 ){ | |
| 457 | +#ifdef BIO_FAMILY_IPV4 | |
| 458 | + BIO_set_conn_ip_family(sBio, BIO_FAMILY_IPV4); | |
| 459 | +#else | |
| 460 | + fossil_warning("The --ipv4 option is not supported in this build\n"); | |
| 461 | +#endif | |
| 462 | + } | |
| 463 | + fossil_free(connStr); | |
| 457 | 464 | if( BIO_do_connect(sBio)<=0 ){ |
| 458 | 465 | ssl_set_errmsg("SSL: cannot connect to proxy %s:%d (%s)", |
| 459 | 466 | pUrlData->name, pUrlData->port, |
| 460 | 467 | ERR_reason_error_string(ERR_get_error())); |
| 461 | 468 | ssl_close_client(); |
| @@ -503,11 +510,18 @@ | ||
| 503 | 510 | #endif |
| 504 | 511 | |
| 505 | 512 | if( !pUrlData->useProxy ){ |
| 506 | 513 | char *connStr = mprintf("%s:%d", pUrlData->name, pUrlData->port); |
| 507 | 514 | BIO_set_conn_hostname(iBio, connStr); |
| 508 | - free(connStr); | |
| 515 | + fossil_free(connStr); | |
| 516 | + if( g.fIPv4 ){ | |
| 517 | +#ifdef BIO_FAMILY_IPV4 | |
| 518 | + BIO_set_conn_ip_family(iBio, BIO_FAMILY_IPV4); | |
| 519 | +#else | |
| 520 | + fossil_warning("The --ipv4 option is not supported in this build\n"); | |
| 521 | +#endif | |
| 522 | + } | |
| 509 | 523 | if( BIO_do_connect(iBio)<=0 ){ |
| 510 | 524 | ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)", |
| 511 | 525 | pUrlData->name, pUrlData->port, |
| 512 | 526 | ERR_reason_error_string(ERR_get_error())); |
| 513 | 527 | ssl_close_client(); |
| @@ -910,31 +924,31 @@ | ||
| 910 | 924 | if( file_isdir(zPath, ExtFILE)>0 ) *pzStore = zPath; |
| 911 | 925 | } |
| 912 | 926 | #endif /* FOSSIL_ENABLE_SSL */ |
| 913 | 927 | |
| 914 | 928 | /* |
| 915 | -** COMMAND: tls-config* | |
| 916 | -** COMMAND: ssl-config | |
| 929 | +** COMMAND: tls-config* abbrv-subcom | |
| 930 | +** COMMAND: ssl-config abbrv-subcom | |
| 917 | 931 | ** |
| 918 | 932 | ** Usage: %fossil ssl-config [SUBCOMMAND] [OPTIONS...] [ARGS...] |
| 919 | 933 | ** |
| 920 | 934 | ** This command is used to view or modify the TLS (Transport Layer |
| 921 | 935 | ** Security) configuration for Fossil. TLS (formerly SSL) is the |
| 922 | 936 | ** encryption technology used for secure HTTPS transport. |
| 923 | 937 | ** |
| 924 | 938 | ** Sub-commands: |
| 925 | 939 | ** |
| 926 | -** remove-exception DOMAINS Remove TLS cert exceptions for the domains | |
| 927 | -** listed. Or remove them all if the --all | |
| 928 | -** option is specified. | |
| 929 | -** | |
| 930 | -** scrub ?--force? Remove all SSL configuration data from the | |
| 931 | -** repository. Use --force to omit the | |
| 932 | -** confirmation. | |
| 933 | -** | |
| 934 | -** show ?-v? Show the TLS configuration. Add -v to see | |
| 935 | -** additional explanation | |
| 940 | +** remove-exception DOMAINS Remove TLS cert exceptions for the domains | |
| 941 | +** listed. Or remove them all if the --all | |
| 942 | +** option is specified. | |
| 943 | +** | |
| 944 | +** scrub ?--force? Remove all SSL configuration data from the | |
| 945 | +** repository. Use --force to omit the | |
| 946 | +** confirmation. | |
| 947 | +** | |
| 948 | +** show ?-v? Show the TLS configuration. Add -v to see | |
| 949 | +** additional explanation | |
| 936 | 950 | */ |
| 937 | 951 | void test_tlsconfig_info(void){ |
| 938 | 952 | const char *zCmd; |
| 939 | 953 | size_t nCmd; |
| 940 | 954 | int nHit = 0; |
| 941 | 955 |
| --- src/http_ssl.c | |
| +++ src/http_ssl.c | |
| @@ -451,11 +451,18 @@ | |
| 451 | ssl_global_init_client(); |
| 452 | if( pUrlData->useProxy ){ |
| 453 | int rc; |
| 454 | char *connStr = mprintf("%s:%d", g.url.name, pUrlData->port); |
| 455 | BIO *sBio = BIO_new_connect(connStr); |
| 456 | free(connStr); |
| 457 | if( BIO_do_connect(sBio)<=0 ){ |
| 458 | ssl_set_errmsg("SSL: cannot connect to proxy %s:%d (%s)", |
| 459 | pUrlData->name, pUrlData->port, |
| 460 | ERR_reason_error_string(ERR_get_error())); |
| 461 | ssl_close_client(); |
| @@ -503,11 +510,18 @@ | |
| 503 | #endif |
| 504 | |
| 505 | if( !pUrlData->useProxy ){ |
| 506 | char *connStr = mprintf("%s:%d", pUrlData->name, pUrlData->port); |
| 507 | BIO_set_conn_hostname(iBio, connStr); |
| 508 | free(connStr); |
| 509 | if( BIO_do_connect(iBio)<=0 ){ |
| 510 | ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)", |
| 511 | pUrlData->name, pUrlData->port, |
| 512 | ERR_reason_error_string(ERR_get_error())); |
| 513 | ssl_close_client(); |
| @@ -910,31 +924,31 @@ | |
| 910 | if( file_isdir(zPath, ExtFILE)>0 ) *pzStore = zPath; |
| 911 | } |
| 912 | #endif /* FOSSIL_ENABLE_SSL */ |
| 913 | |
| 914 | /* |
| 915 | ** COMMAND: tls-config* |
| 916 | ** COMMAND: ssl-config |
| 917 | ** |
| 918 | ** Usage: %fossil ssl-config [SUBCOMMAND] [OPTIONS...] [ARGS...] |
| 919 | ** |
| 920 | ** This command is used to view or modify the TLS (Transport Layer |
| 921 | ** Security) configuration for Fossil. TLS (formerly SSL) is the |
| 922 | ** encryption technology used for secure HTTPS transport. |
| 923 | ** |
| 924 | ** Sub-commands: |
| 925 | ** |
| 926 | ** remove-exception DOMAINS Remove TLS cert exceptions for the domains |
| 927 | ** listed. Or remove them all if the --all |
| 928 | ** option is specified. |
| 929 | ** |
| 930 | ** scrub ?--force? Remove all SSL configuration data from the |
| 931 | ** repository. Use --force to omit the |
| 932 | ** confirmation. |
| 933 | ** |
| 934 | ** show ?-v? Show the TLS configuration. Add -v to see |
| 935 | ** additional explanation |
| 936 | */ |
| 937 | void test_tlsconfig_info(void){ |
| 938 | const char *zCmd; |
| 939 | size_t nCmd; |
| 940 | int nHit = 0; |
| 941 |
| --- src/http_ssl.c | |
| +++ src/http_ssl.c | |
| @@ -451,11 +451,18 @@ | |
| 451 | ssl_global_init_client(); |
| 452 | if( pUrlData->useProxy ){ |
| 453 | int rc; |
| 454 | char *connStr = mprintf("%s:%d", g.url.name, pUrlData->port); |
| 455 | BIO *sBio = BIO_new_connect(connStr); |
| 456 | if( g.fIPv4 ){ |
| 457 | #ifdef BIO_FAMILY_IPV4 |
| 458 | BIO_set_conn_ip_family(sBio, BIO_FAMILY_IPV4); |
| 459 | #else |
| 460 | fossil_warning("The --ipv4 option is not supported in this build\n"); |
| 461 | #endif |
| 462 | } |
| 463 | fossil_free(connStr); |
| 464 | if( BIO_do_connect(sBio)<=0 ){ |
| 465 | ssl_set_errmsg("SSL: cannot connect to proxy %s:%d (%s)", |
| 466 | pUrlData->name, pUrlData->port, |
| 467 | ERR_reason_error_string(ERR_get_error())); |
| 468 | ssl_close_client(); |
| @@ -503,11 +510,18 @@ | |
| 510 | #endif |
| 511 | |
| 512 | if( !pUrlData->useProxy ){ |
| 513 | char *connStr = mprintf("%s:%d", pUrlData->name, pUrlData->port); |
| 514 | BIO_set_conn_hostname(iBio, connStr); |
| 515 | fossil_free(connStr); |
| 516 | if( g.fIPv4 ){ |
| 517 | #ifdef BIO_FAMILY_IPV4 |
| 518 | BIO_set_conn_ip_family(iBio, BIO_FAMILY_IPV4); |
| 519 | #else |
| 520 | fossil_warning("The --ipv4 option is not supported in this build\n"); |
| 521 | #endif |
| 522 | } |
| 523 | if( BIO_do_connect(iBio)<=0 ){ |
| 524 | ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)", |
| 525 | pUrlData->name, pUrlData->port, |
| 526 | ERR_reason_error_string(ERR_get_error())); |
| 527 | ssl_close_client(); |
| @@ -910,31 +924,31 @@ | |
| 924 | if( file_isdir(zPath, ExtFILE)>0 ) *pzStore = zPath; |
| 925 | } |
| 926 | #endif /* FOSSIL_ENABLE_SSL */ |
| 927 | |
| 928 | /* |
| 929 | ** COMMAND: tls-config* abbrv-subcom |
| 930 | ** COMMAND: ssl-config abbrv-subcom |
| 931 | ** |
| 932 | ** Usage: %fossil ssl-config [SUBCOMMAND] [OPTIONS...] [ARGS...] |
| 933 | ** |
| 934 | ** This command is used to view or modify the TLS (Transport Layer |
| 935 | ** Security) configuration for Fossil. TLS (formerly SSL) is the |
| 936 | ** encryption technology used for secure HTTPS transport. |
| 937 | ** |
| 938 | ** Sub-commands: |
| 939 | ** |
| 940 | ** remove-exception DOMAINS Remove TLS cert exceptions for the domains |
| 941 | ** listed. Or remove them all if the --all |
| 942 | ** option is specified. |
| 943 | ** |
| 944 | ** scrub ?--force? Remove all SSL configuration data from the |
| 945 | ** repository. Use --force to omit the |
| 946 | ** confirmation. |
| 947 | ** |
| 948 | ** show ?-v? Show the TLS configuration. Add -v to see |
| 949 | ** additional explanation |
| 950 | */ |
| 951 | void test_tlsconfig_info(void){ |
| 952 | const char *zCmd; |
| 953 | size_t nCmd; |
| 954 | int nHit = 0; |
| 955 |
+144
-112
| --- src/info.c | ||
| +++ src/info.c | ||
| @@ -257,17 +257,19 @@ | ||
| 257 | 257 | } |
| 258 | 258 | fossil_print("fossil: %z\n", file_fullexename(g.nameOfExe)); |
| 259 | 259 | fossil_print("version: %s", z); |
| 260 | 260 | blob_reset(&vx); |
| 261 | 261 | } |
| 262 | - }else{ | |
| 262 | + }else if( g.repositoryOpen ){ | |
| 263 | 263 | int rid; |
| 264 | 264 | rid = name_to_rid(g.argv[2]); |
| 265 | 265 | if( rid==0 ){ |
| 266 | 266 | fossil_fatal("no such object: %s", g.argv[2]); |
| 267 | 267 | } |
| 268 | 268 | show_common_info(rid, "hash:", 1, 1); |
| 269 | + }else{ | |
| 270 | + fossil_fatal("Could not find or open a Fossil repository"); | |
| 269 | 271 | } |
| 270 | 272 | } |
| 271 | 273 | |
| 272 | 274 | /* |
| 273 | 275 | ** Show the context graph (immediate parents and children) for |
| @@ -925,12 +927,12 @@ | ||
| 925 | 927 | " WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim", |
| 926 | 928 | rid |
| 927 | 929 | ); |
| 928 | 930 | isLeaf = !db_exists("SELECT 1 FROM plink WHERE pid=%d", rid); |
| 929 | 931 | db_prepare(&q1, |
| 930 | - "SELECT uuid, datetime(mtime,toLocal()), user, comment," | |
| 931 | - " datetime(omtime,toLocal()), mtime" | |
| 932 | + "SELECT uuid, datetime(mtime,toLocal(),'subsec'), user, comment," | |
| 933 | + " datetime(omtime,toLocal(),'subsec'), mtime" | |
| 932 | 934 | " FROM blob, event" |
| 933 | 935 | " WHERE blob.rid=%d" |
| 934 | 936 | " AND event.objid=%d", |
| 935 | 937 | rid, rid |
| 936 | 938 | ); |
| @@ -1366,72 +1368,10 @@ | ||
| 1366 | 1368 | webpage_error("Artifact %s is not a check-in.", P(zParam)); |
| 1367 | 1369 | return 0; |
| 1368 | 1370 | } |
| 1369 | 1371 | return manifest_get(rid, CFTYPE_MANIFEST, 0); |
| 1370 | 1372 | } |
| 1371 | - | |
| 1372 | -#if 0 /* not used */ | |
| 1373 | -/* | |
| 1374 | -** Output a description of a check-in | |
| 1375 | -*/ | |
| 1376 | -static void checkin_description(int rid){ | |
| 1377 | - Stmt q; | |
| 1378 | - db_prepare(&q, | |
| 1379 | - "SELECT datetime(mtime), coalesce(euser,user)," | |
| 1380 | - " coalesce(ecomment,comment), uuid," | |
| 1381 | - " (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref" | |
| 1382 | - " WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid" | |
| 1383 | - " AND tagxref.rid=blob.rid AND tagxref.tagtype>0)" | |
| 1384 | - " FROM event, blob" | |
| 1385 | - " WHERE event.objid=%d AND type='ci'" | |
| 1386 | - " AND blob.rid=%d", | |
| 1387 | - rid, rid | |
| 1388 | - ); | |
| 1389 | - while( db_step(&q)==SQLITE_ROW ){ | |
| 1390 | - const char *zDate = db_column_text(&q, 0); | |
| 1391 | - const char *zUser = db_column_text(&q, 1); | |
| 1392 | - const char *zUuid = db_column_text(&q, 3); | |
| 1393 | - const char *zTagList = db_column_text(&q, 4); | |
| 1394 | - Blob comment; | |
| 1395 | - int wikiFlags = WIKI_INLINE|WIKI_NOBADLINKS; | |
| 1396 | - if( db_get_boolean("timeline-block-markup", 0)==0 ){ | |
| 1397 | - wikiFlags |= WIKI_NOBLOCK; | |
| 1398 | - } | |
| 1399 | - hyperlink_to_version(zUuid); | |
| 1400 | - blob_zero(&comment); | |
| 1401 | - db_column_blob(&q, 2, &comment); | |
| 1402 | - wiki_convert(&comment, 0, wikiFlags); | |
| 1403 | - blob_reset(&comment); | |
| 1404 | - @ (user: | |
| 1405 | - hyperlink_to_user(zUser,zDate,","); | |
| 1406 | - if( zTagList && zTagList[0] && g.perm.Hyperlink ){ | |
| 1407 | - int i; | |
| 1408 | - const char *z = zTagList; | |
| 1409 | - Blob links; | |
| 1410 | - blob_zero(&links); | |
| 1411 | - while( z && z[0] ){ | |
| 1412 | - for(i=0; z[i] && (z[i]!=',' || z[i+1]!=' '); i++){} | |
| 1413 | - blob_appendf(&links, | |
| 1414 | - "%z%#h</a>%.2s", | |
| 1415 | - href("%R/timeline?r=%#t&nd&c=%t",i,z,zDate), i,z, &z[i] | |
| 1416 | - ); | |
| 1417 | - if( z[i]==0 ) break; | |
| 1418 | - z += i+2; | |
| 1419 | - } | |
| 1420 | - @ tags: %s(blob_str(&links)), | |
| 1421 | - blob_reset(&links); | |
| 1422 | - }else{ | |
| 1423 | - @ tags: %h(zTagList), | |
| 1424 | - } | |
| 1425 | - @ date: | |
| 1426 | - hyperlink_to_date(zDate, ")"); | |
| 1427 | - tag_private_status(rid); | |
| 1428 | - } | |
| 1429 | - db_finalize(&q); | |
| 1430 | -} | |
| 1431 | -#endif /* not used */ | |
| 1432 | - | |
| 1433 | 1373 | |
| 1434 | 1374 | /* |
| 1435 | 1375 | ** WEBPAGE: vdiff |
| 1436 | 1376 | ** URL: /vdiff?from=TAG&to=TAG |
| 1437 | 1377 | ** |
| @@ -2682,31 +2622,36 @@ | ||
| 2682 | 2622 | |
| 2683 | 2623 | /* |
| 2684 | 2624 | ** WEBPAGE: artifact |
| 2685 | 2625 | ** WEBPAGE: file |
| 2686 | 2626 | ** WEBPAGE: whatis |
| 2627 | +** WEBPAGE: docfile | |
| 2687 | 2628 | ** |
| 2688 | 2629 | ** Typical usage: |
| 2689 | 2630 | ** |
| 2690 | 2631 | ** /artifact/HASH |
| 2691 | 2632 | ** /whatis/HASH |
| 2692 | 2633 | ** /file/NAME |
| 2634 | +** /docfile/NAME | |
| 2693 | 2635 | ** |
| 2694 | 2636 | ** Additional query parameters: |
| 2695 | 2637 | ** |
| 2696 | 2638 | ** ln - show line numbers |
| 2697 | 2639 | ** ln=N - highlight line number N |
| 2698 | 2640 | ** ln=M-N - highlight lines M through N inclusive |
| 2699 | 2641 | ** ln=M-N+Y-Z - highlight lines M through N and Y through Z (inclusive) |
| 2700 | 2642 | ** verbose - show more detail in the description |
| 2643 | +** brief - show just the document, not the metadata. The | |
| 2644 | +** /docfile page is an alias for /file?brief | |
| 2701 | 2645 | ** download - redirect to the download (artifact page only) |
| 2702 | 2646 | ** name=NAME - filename or hash as a query parameter |
| 2703 | 2647 | ** filename=NAME - alternative spelling for "name=" |
| 2704 | 2648 | ** fn=NAME - alternative spelling for "name=" |
| 2705 | 2649 | ** ci=VERSION - The specific check-in to use with "name=" to |
| 2706 | 2650 | ** identify the file. |
| 2707 | 2651 | ** txt - Force display of unformatted source text |
| 2652 | +** hash - Output only the hash of the artifact | |
| 2708 | 2653 | ** |
| 2709 | 2654 | ** The /artifact page show the complete content of a file |
| 2710 | 2655 | ** identified by HASH. The /whatis page shows only a description |
| 2711 | 2656 | ** of how the artifact is used. The /file page shows the most recent |
| 2712 | 2657 | ** version of the file or directory called NAME, or a list of the |
| @@ -2725,18 +2670,21 @@ | ||
| 2725 | 2670 | void artifact_page(void){ |
| 2726 | 2671 | int rid = 0; |
| 2727 | 2672 | Blob content; |
| 2728 | 2673 | const char *zMime; |
| 2729 | 2674 | Blob downloadName; |
| 2675 | + Blob uuid; | |
| 2730 | 2676 | int renderAsWiki = 0; |
| 2731 | 2677 | int renderAsHtml = 0; |
| 2732 | 2678 | int renderAsSvg = 0; |
| 2733 | 2679 | int objType; |
| 2734 | 2680 | int asText; |
| 2735 | 2681 | const char *zUuid = 0; |
| 2736 | 2682 | u32 objdescFlags = OBJDESC_BASE; |
| 2737 | 2683 | int descOnly = fossil_strcmp(g.zPath,"whatis")==0; |
| 2684 | + int hashOnly = P("hash")!=0; | |
| 2685 | + int docOnly = P("brief")!=0; | |
| 2738 | 2686 | int isFile = fossil_strcmp(g.zPath,"file")==0; |
| 2739 | 2687 | const char *zLn = P("ln"); |
| 2740 | 2688 | const char *zName = P("name"); |
| 2741 | 2689 | const char *zCI = P("ci"); |
| 2742 | 2690 | HQuery url; |
| @@ -2747,10 +2695,14 @@ | ||
| 2747 | 2695 | |
| 2748 | 2696 | login_check_credentials(); |
| 2749 | 2697 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 2750 | 2698 | cgi_check_for_malice(); |
| 2751 | 2699 | style_set_current_feature("artifact"); |
| 2700 | + if( fossil_strcmp(g.zPath, "docfile")==0 ){ | |
| 2701 | + isFile = 1; | |
| 2702 | + docOnly = 1; | |
| 2703 | + } | |
| 2752 | 2704 | |
| 2753 | 2705 | /* Capture and normalize the name= and ci= query parameters */ |
| 2754 | 2706 | if( zName==0 ){ |
| 2755 | 2707 | zName = P("filename"); |
| 2756 | 2708 | if( zName==0 ){ |
| @@ -2849,14 +2801,23 @@ | ||
| 2849 | 2801 | url_add_parameter(&url, "verbose", "1"); |
| 2850 | 2802 | objdescFlags |= OBJDESC_DETAIL; |
| 2851 | 2803 | } |
| 2852 | 2804 | zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 2853 | 2805 | etag_check(ETAG_HASH, zUuid); |
| 2806 | + | |
| 2807 | + if( descOnly && hashOnly ){ | |
| 2808 | + blob_set(&uuid, zUuid); | |
| 2809 | + cgi_set_content_type("text/plain"); | |
| 2810 | + cgi_set_content(&uuid); | |
| 2811 | + return; | |
| 2812 | + } | |
| 2854 | 2813 | |
| 2855 | 2814 | asText = P("txt")!=0; |
| 2856 | 2815 | if( isFile ){ |
| 2857 | - if( zCI==0 || fossil_strcmp(zCI,"tip")==0 ){ | |
| 2816 | + if( docOnly ){ | |
| 2817 | + /* No header */ | |
| 2818 | + }else if( zCI==0 || fossil_strcmp(zCI,"tip")==0 ){ | |
| 2858 | 2819 | zCI = "tip"; |
| 2859 | 2820 | @ <h2>File %z(href("%R/finfo?name=%T&m&ci=tip",zName))%h(zName)</a> |
| 2860 | 2821 | @ from the %z(href("%R/info/tip"))latest check-in</a></h2> |
| 2861 | 2822 | }else{ |
| 2862 | 2823 | const char *zPath; |
| @@ -2874,17 +2835,19 @@ | ||
| 2874 | 2835 | }else{ |
| 2875 | 2836 | @ part of check-in %z(href("%R/info/%!S",zCIUuid))%S(zCIUuid)</a></h2> |
| 2876 | 2837 | } |
| 2877 | 2838 | blob_reset(&path); |
| 2878 | 2839 | } |
| 2879 | - style_submenu_element("Artifact", "%R/artifact/%S", zUuid); | |
| 2880 | 2840 | zMime = mimetype_from_name(zName); |
| 2881 | - style_submenu_element("Annotate", "%R/annotate?filename=%T&checkin=%T", | |
| 2882 | - zName, zCI); | |
| 2883 | - style_submenu_element("Blame", "%R/blame?filename=%T&checkin=%T", | |
| 2884 | - zName, zCI); | |
| 2885 | - style_submenu_element("Doc", "%R/doc/%T/%T", zCI, zName); | |
| 2841 | + if( !docOnly ){ | |
| 2842 | + style_submenu_element("Artifact", "%R/artifact/%S", zUuid); | |
| 2843 | + style_submenu_element("Annotate", "%R/annotate?filename=%T&checkin=%T", | |
| 2844 | + zName, zCI); | |
| 2845 | + style_submenu_element("Blame", "%R/blame?filename=%T&checkin=%T", | |
| 2846 | + zName, zCI); | |
| 2847 | + style_submenu_element("Doc", "%R/doc/%T/%T", zCI, zName); | |
| 2848 | + } | |
| 2886 | 2849 | blob_init(&downloadName, zName, -1); |
| 2887 | 2850 | objType = OBJTYPE_CONTENT; |
| 2888 | 2851 | }else{ |
| 2889 | 2852 | @ <h2>Artifact |
| 2890 | 2853 | style_copy_button(1, "hash-ar", 0, 2, "%s", zUuid); |
| @@ -2903,11 +2866,11 @@ | ||
| 2903 | 2866 | cgi_redirectf("%R/raw/%s?at=%T", |
| 2904 | 2867 | db_text("x", "SELECT uuid FROM blob WHERE rid=%d", rid), |
| 2905 | 2868 | file_tail(blob_str(&downloadName))); |
| 2906 | 2869 | /*NOTREACHED*/ |
| 2907 | 2870 | } |
| 2908 | - if( g.perm.Admin ){ | |
| 2871 | + if( g.perm.Admin && !docOnly ){ | |
| 2909 | 2872 | const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 2910 | 2873 | if( db_exists("SELECT 1 FROM shun WHERE uuid=%Q", zUuid) ){ |
| 2911 | 2874 | style_submenu_element("Unshun", "%R/shun?accept=%s&sub=1#accshun", zUuid); |
| 2912 | 2875 | }else{ |
| 2913 | 2876 | style_submenu_element("Shun", "%R/shun?shun=%s#addshun",zUuid); |
| @@ -2948,41 +2911,49 @@ | ||
| 2948 | 2911 | const char *zIp = db_column_text(&q,2); |
| 2949 | 2912 | @ <p>Received on %s(zDate) from %h(zUser) at %h(zIp).</p> |
| 2950 | 2913 | } |
| 2951 | 2914 | db_finalize(&q); |
| 2952 | 2915 | } |
| 2953 | - style_submenu_element("Download", "%R/raw/%s?at=%T", zUuid, file_tail(zName)); | |
| 2954 | - if( db_exists("SELECT 1 FROM mlink WHERE fid=%d", rid) ){ | |
| 2955 | - style_submenu_element("Check-ins Using", "%R/timeline?uf=%s", zUuid); | |
| 2916 | + if( !docOnly ){ | |
| 2917 | + style_submenu_element("Download", "%R/raw/%s?at=%T",zUuid,file_tail(zName)); | |
| 2918 | + if( db_exists("SELECT 1 FROM mlink WHERE fid=%d", rid) ){ | |
| 2919 | + style_submenu_element("Check-ins Using", "%R/timeline?uf=%s", zUuid); | |
| 2920 | + } | |
| 2956 | 2921 | } |
| 2957 | 2922 | if( zMime ){ |
| 2958 | 2923 | if( fossil_strcmp(zMime, "text/html")==0 ){ |
| 2959 | 2924 | if( asText ){ |
| 2960 | 2925 | style_submenu_element("Html", "%s", url_render(&url, "txt", 0, 0, 0)); |
| 2961 | 2926 | }else{ |
| 2962 | 2927 | renderAsHtml = 1; |
| 2963 | - style_submenu_element("Text", "%s", url_render(&url, "txt", "1", 0, 0)); | |
| 2928 | + if( !docOnly ){ | |
| 2929 | + style_submenu_element("Text", "%s", url_render(&url, "txt","1",0,0)); | |
| 2930 | + } | |
| 2964 | 2931 | } |
| 2965 | 2932 | }else if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 |
| 2966 | 2933 | || fossil_strcmp(zMime, "text/x-markdown")==0 |
| 2967 | 2934 | || fossil_strcmp(zMime, "text/x-pikchr")==0 ){ |
| 2968 | 2935 | if( asText ){ |
| 2969 | 2936 | style_submenu_element(zMime[7]=='p' ? "Pikchr" : "Wiki", |
| 2970 | 2937 | "%s", url_render(&url, "txt", 0, 0, 0)); |
| 2971 | 2938 | }else{ |
| 2972 | 2939 | renderAsWiki = 1; |
| 2973 | - style_submenu_element("Text", "%s", url_render(&url, "txt", "1", 0, 0)); | |
| 2940 | + if( !docOnly ){ | |
| 2941 | + style_submenu_element("Text", "%s", url_render(&url, "txt","1",0,0)); | |
| 2942 | + } | |
| 2974 | 2943 | } |
| 2975 | 2944 | }else if( fossil_strcmp(zMime, "image/svg+xml")==0 ){ |
| 2976 | 2945 | if( asText ){ |
| 2977 | 2946 | style_submenu_element("Svg", "%s", url_render(&url, "txt", 0, 0, 0)); |
| 2978 | 2947 | }else{ |
| 2979 | 2948 | renderAsSvg = 1; |
| 2980 | - style_submenu_element("Text", "%s", url_render(&url, "txt", "1", 0, 0)); | |
| 2949 | + if( !docOnly ){ | |
| 2950 | + style_submenu_element("Text", "%s", url_render(&url, "txt","1",0,0)); | |
| 2951 | + } | |
| 2981 | 2952 | } |
| 2982 | 2953 | } |
| 2983 | - if( fileedit_is_editable(zName) ){ | |
| 2954 | + if( !docOnly && fileedit_is_editable(zName) ){ | |
| 2984 | 2955 | style_submenu_element("Edit", |
| 2985 | 2956 | "%R/fileedit?filename=%T&checkin=%!S", |
| 2986 | 2957 | zName, zCI); |
| 2987 | 2958 | } |
| 2988 | 2959 | } |
| @@ -2990,11 +2961,13 @@ | ||
| 2990 | 2961 | style_submenu_element("Parsed", "%R/info/%s", zUuid); |
| 2991 | 2962 | } |
| 2992 | 2963 | if( descOnly ){ |
| 2993 | 2964 | style_submenu_element("Content", "%R/artifact/%s", zUuid); |
| 2994 | 2965 | }else{ |
| 2995 | - @ <hr> | |
| 2966 | + if( !docOnly || !isFile ){ | |
| 2967 | + @ <hr> | |
| 2968 | + } | |
| 2996 | 2969 | content_get(rid, &content); |
| 2997 | 2970 | if( renderAsWiki ){ |
| 2998 | 2971 | safe_html_context(DOCSRC_FILE); |
| 2999 | 2972 | wiki_render_by_mimetype(&content, zMime); |
| 3000 | 2973 | document_emit_js(); |
| @@ -3660,10 +3633,11 @@ | ||
| 3660 | 3633 | zNewColorFlag = P("newclr") ? " checked" : ""; |
| 3661 | 3634 | zNewTagFlag = P("newtag") ? " checked" : ""; |
| 3662 | 3635 | zNewTag = PDT("tagname",""); |
| 3663 | 3636 | zNewBrFlag = P("newbr") ? " checked" : ""; |
| 3664 | 3637 | zNewBranch = PDT("brname",""); |
| 3638 | + zBranchName = branch_of_rid(rid); | |
| 3665 | 3639 | zCloseFlag = P("close") ? " checked" : ""; |
| 3666 | 3640 | zHideFlag = P("hide") ? " checked" : ""; |
| 3667 | 3641 | if( P("apply") && cgi_csrf_safe(2) ){ |
| 3668 | 3642 | Blob ctrl; |
| 3669 | 3643 | char *zNow; |
| @@ -3707,17 +3681,25 @@ | ||
| 3707 | 3681 | zUuid[10] = 0; |
| 3708 | 3682 | style_header("Edit Check-in [%s]", zUuid); |
| 3709 | 3683 | if( P("preview") ){ |
| 3710 | 3684 | Blob suffix; |
| 3711 | 3685 | int nTag = 0; |
| 3686 | + const char *zDplyBr; /* Branch name used to determine BG color */ | |
| 3687 | + if( zNewBrFlag[0] && zNewBranch[0] ){ | |
| 3688 | + zDplyBr = zNewBranch; | |
| 3689 | + }else{ | |
| 3690 | + zDplyBr = zBranchName; | |
| 3691 | + } | |
| 3712 | 3692 | @ <b>Preview:</b> |
| 3713 | 3693 | @ <blockquote> |
| 3714 | 3694 | @ <table border=0> |
| 3715 | 3695 | if( zNewColorFlag[0] && zNewColor && zNewColor[0] ){ |
| 3716 | - @ <tr><td style="background-color: %h(zNewColor);"> | |
| 3696 | + @ <tr><td style="background-color:%h(reasonable_bg_color(zNewColor,0));"> | |
| 3717 | 3697 | }else if( zColor[0] ){ |
| 3718 | - @ <tr><td style="background-color: %h(zColor);"> | |
| 3698 | + @ <tr><td style="background-color:%h(reasonable_bg_color(zColor,0));"> | |
| 3699 | + }else if( zDplyBr && fossil_strcmp(zDplyBr,"trunk")!=0 ){ | |
| 3700 | + @ <tr><td style="background-color:%h(hash_color(zDplyBr));"> | |
| 3719 | 3701 | }else{ |
| 3720 | 3702 | @ <tr><td> |
| 3721 | 3703 | } |
| 3722 | 3704 | @ %!W(blob_str(&comment)) |
| 3723 | 3705 | blob_zero(&suffix); |
| @@ -3798,13 +3780,10 @@ | ||
| 3798 | 3780 | @ <tr><th align="right" valign="top">Tags:</th> |
| 3799 | 3781 | @ <td valign="top"> |
| 3800 | 3782 | @ <label><input type="checkbox" id="newtag" name="newtag"%s(zNewTagFlag)> |
| 3801 | 3783 | @ Add the following new tag name to this check-in:</label> |
| 3802 | 3784 | @ <input size="15" name="tagname" id="tagname" value="%h(zNewTag)"> |
| 3803 | - zBranchName = db_text(0, "SELECT value FROM tagxref, tag" | |
| 3804 | - " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid" | |
| 3805 | - " AND tagxref.tagid=%d", rid, TAG_BRANCH); | |
| 3806 | 3785 | db_prepare(&q, |
| 3807 | 3786 | "SELECT tag.tagid, tagname, tagxref.value FROM tagxref, tag" |
| 3808 | 3787 | " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid" |
| 3809 | 3788 | " ORDER BY CASE WHEN tagname GLOB 'sym-*' THEN substr(tagname,5)" |
| 3810 | 3789 | " ELSE tagname END /*sort*/", |
| @@ -3941,25 +3920,26 @@ | ||
| 3941 | 3920 | ** Usage: %fossil amend HASH OPTION ?OPTION ...? |
| 3942 | 3921 | ** |
| 3943 | 3922 | ** Amend the tags on check-in HASH to change how it displays in the timeline. |
| 3944 | 3923 | ** |
| 3945 | 3924 | ** Options: |
| 3946 | -** --author USER Make USER the author for check-in | |
| 3947 | -** -m|--comment COMMENT Make COMMENT the check-in comment | |
| 3948 | -** -M|--message-file FILE Read the amended comment from FILE | |
| 3949 | -** -e|--edit-comment Launch editor to revise comment | |
| 3950 | -** --date DATETIME Make DATETIME the check-in time | |
| 3951 | -** --bgcolor COLOR Apply COLOR to this check-in | |
| 3952 | -** --branchcolor COLOR Apply and propagate COLOR to the branch | |
| 3953 | -** --tag TAG Add new TAG to this check-in | |
| 3954 | -** --cancel TAG Cancel TAG from this check-in | |
| 3955 | -** --branch NAME Rename branch of check-in to NAME | |
| 3956 | -** --hide Hide branch starting from this check-in | |
| 3957 | -** --close Mark this "leaf" as closed | |
| 3958 | -** -n|--dry-run Print control artifact, but make no changes | |
| 3959 | -** --date-override DATETIME Set the change time on the control artifact | |
| 3960 | -** --user-override USER Set the user name on the control artifact | |
| 3925 | +** --author USER Make USER the author for check-in | |
| 3926 | +** --bgcolor COLOR Apply COLOR to this check-in | |
| 3927 | +** --branch NAME Rename branch of check-in to NAME | |
| 3928 | +** --branchcolor COLOR Apply and propagate COLOR to the branch | |
| 3929 | +** --cancel TAG Cancel TAG from this check-in | |
| 3930 | +** --close Mark this "leaf" as closed | |
| 3931 | +** --date DATETIME Make DATETIME the check-in time | |
| 3932 | +** --date-override DATETIME Set the change time on the control artifact | |
| 3933 | +** -e|--edit-comment Launch editor to revise comment | |
| 3934 | +** --hide Hide branch starting from this check-in | |
| 3935 | +** -m|--comment COMMENT Make COMMENT the check-in comment | |
| 3936 | +** -M|--message-file FILE Read the amended comment from FILE | |
| 3937 | +** -n|--dry-run Print control artifact, but make no changes | |
| 3938 | +** --no-verify-comment Do not validate the check-in comment | |
| 3939 | +** --tag TAG Add new TAG to this check-in | |
| 3940 | +** --user-override USER Set the user name on the control artifact | |
| 3961 | 3941 | ** |
| 3962 | 3942 | ** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in |
| 3963 | 3943 | ** year-month-day form, it may be truncated, the "T" may be replaced by |
| 3964 | 3944 | ** a space, and it may also name a timezone offset from UTC as "-HH:MM" |
| 3965 | 3945 | ** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z" |
| @@ -3986,19 +3966,22 @@ | ||
| 3986 | 3966 | int fNewPropagateColor = 0; /* True if color propagates after amend */ |
| 3987 | 3967 | int fHasHidden = 0; /* True if hidden tag already set */ |
| 3988 | 3968 | int fHasClosed = 0; /* True if closed tag already set */ |
| 3989 | 3969 | int fEditComment; /* True if editor to be used for comment */ |
| 3990 | 3970 | int fDryRun; /* Print control artifact, make no changes */ |
| 3971 | + int noVerifyCom = 0; /* Allow suspicious check-in comments */ | |
| 3991 | 3972 | const char *zChngTime; /* The change time on the control artifact */ |
| 3992 | 3973 | const char *zUserOvrd; /* The user name on the control artifact */ |
| 3993 | 3974 | const char *zUuid; |
| 3994 | 3975 | Blob ctrl; |
| 3995 | 3976 | Blob comment; |
| 3996 | 3977 | char *zNow; |
| 3997 | 3978 | int nTags, nCancels; |
| 3998 | 3979 | int i; |
| 3999 | 3980 | Stmt q; |
| 3981 | + int ckComFlgs; /* Flags passed to verify_comment() */ | |
| 3982 | + | |
| 4000 | 3983 | |
| 4001 | 3984 | fEditComment = find_option("edit-comment","e",0)!=0; |
| 4002 | 3985 | zNewComment = find_option("comment","m",1); |
| 4003 | 3986 | zComFile = find_option("message-file","M",1); |
| 4004 | 3987 | zNewBranch = find_option("branch",0,1); |
| @@ -4016,10 +3999,11 @@ | ||
| 4016 | 3999 | fHide = find_option("hide",0,0)!=0; |
| 4017 | 4000 | fDryRun = find_option("dry-run","n",0)!=0; |
| 4018 | 4001 | zChngTime = find_option("date-override",0,1); |
| 4019 | 4002 | if( zChngTime==0 ) zChngTime = find_option("chngtime",0,1); |
| 4020 | 4003 | zUserOvrd = find_option("user-override",0,1); |
| 4004 | + noVerifyCom = find_option("no-verify-comment",0,0)!=0; | |
| 4021 | 4005 | db_find_and_open_repository(0,0); |
| 4022 | 4006 | user_select(); |
| 4023 | 4007 | verify_all_options(); |
| 4024 | 4008 | if( g.argc<3 || g.argc>=4 ) usage(AMEND_USAGE_STMT); |
| 4025 | 4009 | rid = name_to_typed_rid(g.argv[2], "ci"); |
| @@ -4074,21 +4058,69 @@ | ||
| 4074 | 4058 | ); |
| 4075 | 4059 | } |
| 4076 | 4060 | if( (zNewColor!=0 && zNewColor[0]==0) && (zColor && zColor[0] ) ){ |
| 4077 | 4061 | cancel_color(); |
| 4078 | 4062 | } |
| 4079 | - if( fEditComment ){ | |
| 4080 | - prepare_amend_comment(&comment, zComment, zUuid); | |
| 4081 | - zNewComment = blob_str(&comment); | |
| 4082 | - }else if( zComFile ){ | |
| 4083 | - blob_zero(&comment); | |
| 4084 | - blob_read_from_file(&comment, zComFile, ExtFILE); | |
| 4085 | - blob_to_utf8_no_bom(&comment, 1); | |
| 4086 | - zNewComment = blob_str(&comment); | |
| 4087 | - } | |
| 4088 | - if( zNewComment && zNewComment[0] | |
| 4089 | - && comment_compare(zComment,zNewComment)==0 ) add_comment(zNewComment); | |
| 4063 | + if( fEditComment || zNewComment || zComFile ){ | |
| 4064 | + blob_init(&comment, 0, 0); | |
| 4065 | + | |
| 4066 | + /* Figure out how much comment verification is requested */ | |
| 4067 | + if( noVerifyCom ){ | |
| 4068 | + ckComFlgs = 0; | |
| 4069 | + }else{ | |
| 4070 | + const char *zVerComs = db_get("verify-comments","on"); | |
| 4071 | + if( is_false(zVerComs) ){ | |
| 4072 | + ckComFlgs = 0; | |
| 4073 | + }else if( strcmp(zVerComs,"preview")==0 ){ | |
| 4074 | + ckComFlgs = COMCK_PREVIEW | COMCK_MARKUP; | |
| 4075 | + }else{ | |
| 4076 | + ckComFlgs = COMCK_MARKUP; | |
| 4077 | + } | |
| 4078 | + } | |
| 4079 | + if( fEditComment ){ | |
| 4080 | + prepare_amend_comment(&comment, zComment, zUuid); | |
| 4081 | + }else if( zComFile ){ | |
| 4082 | + blob_read_from_file(&comment, zComFile, ExtFILE); | |
| 4083 | + blob_to_utf8_no_bom(&comment, 1); | |
| 4084 | + }else if( zNewComment ){ | |
| 4085 | + blob_init(&comment, zNewComment, -1); | |
| 4086 | + } | |
| 4087 | + if( blob_size(&comment)>0 | |
| 4088 | + && comment_compare(zComment, blob_str(&comment))==0 | |
| 4089 | + ){ | |
| 4090 | + int rc; | |
| 4091 | + while( (rc = verify_comment(&comment, ckComFlgs))!=0 ){ | |
| 4092 | + char cReply; | |
| 4093 | + Blob ans; | |
| 4094 | + if( !fEditComment ){ | |
| 4095 | + fossil_fatal("Amend aborted; " | |
| 4096 | + "use --no-verify-comment to override"); | |
| 4097 | + } | |
| 4098 | + if( rc==COMCK_PREVIEW ){ | |
| 4099 | + prompt_user("Continue, abort, or edit (C/a/e)? ", &ans); | |
| 4100 | + }else{ | |
| 4101 | + prompt_user("Edit, abort, or continue (E/a/c)? ", &ans); | |
| 4102 | + } | |
| 4103 | + cReply = blob_str(&ans)[0]; | |
| 4104 | + cReply = fossil_tolower(cReply); | |
| 4105 | + blob_reset(&ans); | |
| 4106 | + if( cReply=='a' ){ | |
| 4107 | + fossil_fatal("Amend aborted."); | |
| 4108 | + } | |
| 4109 | + if( cReply=='e' || (cReply!='c' && rc!=COMCK_PREVIEW) ){ | |
| 4110 | + char *zPrior = blob_materialize(&comment); | |
| 4111 | + blob_init(&comment, 0, 0); | |
| 4112 | + prepare_amend_comment(&comment, zPrior, zUuid); | |
| 4113 | + fossil_free(zPrior); | |
| 4114 | + continue; | |
| 4115 | + }else{ | |
| 4116 | + break; | |
| 4117 | + } | |
| 4118 | + } | |
| 4119 | + } | |
| 4120 | + add_comment(blob_str(&comment)); | |
| 4121 | + } | |
| 4090 | 4122 | if( zNewDate && zNewDate[0] && fossil_strcmp(zDate,zNewDate)!=0 ){ |
| 4091 | 4123 | if( is_datetime(zNewDate) ){ |
| 4092 | 4124 | add_date(zNewDate); |
| 4093 | 4125 | }else{ |
| 4094 | 4126 | fossil_fatal("Unsupported date format, use YYYY-MM-DD HH:MM:SS"); |
| 4095 | 4127 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -257,17 +257,19 @@ | |
| 257 | } |
| 258 | fossil_print("fossil: %z\n", file_fullexename(g.nameOfExe)); |
| 259 | fossil_print("version: %s", z); |
| 260 | blob_reset(&vx); |
| 261 | } |
| 262 | }else{ |
| 263 | int rid; |
| 264 | rid = name_to_rid(g.argv[2]); |
| 265 | if( rid==0 ){ |
| 266 | fossil_fatal("no such object: %s", g.argv[2]); |
| 267 | } |
| 268 | show_common_info(rid, "hash:", 1, 1); |
| 269 | } |
| 270 | } |
| 271 | |
| 272 | /* |
| 273 | ** Show the context graph (immediate parents and children) for |
| @@ -925,12 +927,12 @@ | |
| 925 | " WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim", |
| 926 | rid |
| 927 | ); |
| 928 | isLeaf = !db_exists("SELECT 1 FROM plink WHERE pid=%d", rid); |
| 929 | db_prepare(&q1, |
| 930 | "SELECT uuid, datetime(mtime,toLocal()), user, comment," |
| 931 | " datetime(omtime,toLocal()), mtime" |
| 932 | " FROM blob, event" |
| 933 | " WHERE blob.rid=%d" |
| 934 | " AND event.objid=%d", |
| 935 | rid, rid |
| 936 | ); |
| @@ -1366,72 +1368,10 @@ | |
| 1366 | webpage_error("Artifact %s is not a check-in.", P(zParam)); |
| 1367 | return 0; |
| 1368 | } |
| 1369 | return manifest_get(rid, CFTYPE_MANIFEST, 0); |
| 1370 | } |
| 1371 | |
| 1372 | #if 0 /* not used */ |
| 1373 | /* |
| 1374 | ** Output a description of a check-in |
| 1375 | */ |
| 1376 | static void checkin_description(int rid){ |
| 1377 | Stmt q; |
| 1378 | db_prepare(&q, |
| 1379 | "SELECT datetime(mtime), coalesce(euser,user)," |
| 1380 | " coalesce(ecomment,comment), uuid," |
| 1381 | " (SELECT group_concat(substr(tagname,5), ', ') FROM tag, tagxref" |
| 1382 | " WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid" |
| 1383 | " AND tagxref.rid=blob.rid AND tagxref.tagtype>0)" |
| 1384 | " FROM event, blob" |
| 1385 | " WHERE event.objid=%d AND type='ci'" |
| 1386 | " AND blob.rid=%d", |
| 1387 | rid, rid |
| 1388 | ); |
| 1389 | while( db_step(&q)==SQLITE_ROW ){ |
| 1390 | const char *zDate = db_column_text(&q, 0); |
| 1391 | const char *zUser = db_column_text(&q, 1); |
| 1392 | const char *zUuid = db_column_text(&q, 3); |
| 1393 | const char *zTagList = db_column_text(&q, 4); |
| 1394 | Blob comment; |
| 1395 | int wikiFlags = WIKI_INLINE|WIKI_NOBADLINKS; |
| 1396 | if( db_get_boolean("timeline-block-markup", 0)==0 ){ |
| 1397 | wikiFlags |= WIKI_NOBLOCK; |
| 1398 | } |
| 1399 | hyperlink_to_version(zUuid); |
| 1400 | blob_zero(&comment); |
| 1401 | db_column_blob(&q, 2, &comment); |
| 1402 | wiki_convert(&comment, 0, wikiFlags); |
| 1403 | blob_reset(&comment); |
| 1404 | @ (user: |
| 1405 | hyperlink_to_user(zUser,zDate,","); |
| 1406 | if( zTagList && zTagList[0] && g.perm.Hyperlink ){ |
| 1407 | int i; |
| 1408 | const char *z = zTagList; |
| 1409 | Blob links; |
| 1410 | blob_zero(&links); |
| 1411 | while( z && z[0] ){ |
| 1412 | for(i=0; z[i] && (z[i]!=',' || z[i+1]!=' '); i++){} |
| 1413 | blob_appendf(&links, |
| 1414 | "%z%#h</a>%.2s", |
| 1415 | href("%R/timeline?r=%#t&nd&c=%t",i,z,zDate), i,z, &z[i] |
| 1416 | ); |
| 1417 | if( z[i]==0 ) break; |
| 1418 | z += i+2; |
| 1419 | } |
| 1420 | @ tags: %s(blob_str(&links)), |
| 1421 | blob_reset(&links); |
| 1422 | }else{ |
| 1423 | @ tags: %h(zTagList), |
| 1424 | } |
| 1425 | @ date: |
| 1426 | hyperlink_to_date(zDate, ")"); |
| 1427 | tag_private_status(rid); |
| 1428 | } |
| 1429 | db_finalize(&q); |
| 1430 | } |
| 1431 | #endif /* not used */ |
| 1432 | |
| 1433 | |
| 1434 | /* |
| 1435 | ** WEBPAGE: vdiff |
| 1436 | ** URL: /vdiff?from=TAG&to=TAG |
| 1437 | ** |
| @@ -2682,31 +2622,36 @@ | |
| 2682 | |
| 2683 | /* |
| 2684 | ** WEBPAGE: artifact |
| 2685 | ** WEBPAGE: file |
| 2686 | ** WEBPAGE: whatis |
| 2687 | ** |
| 2688 | ** Typical usage: |
| 2689 | ** |
| 2690 | ** /artifact/HASH |
| 2691 | ** /whatis/HASH |
| 2692 | ** /file/NAME |
| 2693 | ** |
| 2694 | ** Additional query parameters: |
| 2695 | ** |
| 2696 | ** ln - show line numbers |
| 2697 | ** ln=N - highlight line number N |
| 2698 | ** ln=M-N - highlight lines M through N inclusive |
| 2699 | ** ln=M-N+Y-Z - highlight lines M through N and Y through Z (inclusive) |
| 2700 | ** verbose - show more detail in the description |
| 2701 | ** download - redirect to the download (artifact page only) |
| 2702 | ** name=NAME - filename or hash as a query parameter |
| 2703 | ** filename=NAME - alternative spelling for "name=" |
| 2704 | ** fn=NAME - alternative spelling for "name=" |
| 2705 | ** ci=VERSION - The specific check-in to use with "name=" to |
| 2706 | ** identify the file. |
| 2707 | ** txt - Force display of unformatted source text |
| 2708 | ** |
| 2709 | ** The /artifact page show the complete content of a file |
| 2710 | ** identified by HASH. The /whatis page shows only a description |
| 2711 | ** of how the artifact is used. The /file page shows the most recent |
| 2712 | ** version of the file or directory called NAME, or a list of the |
| @@ -2725,18 +2670,21 @@ | |
| 2725 | void artifact_page(void){ |
| 2726 | int rid = 0; |
| 2727 | Blob content; |
| 2728 | const char *zMime; |
| 2729 | Blob downloadName; |
| 2730 | int renderAsWiki = 0; |
| 2731 | int renderAsHtml = 0; |
| 2732 | int renderAsSvg = 0; |
| 2733 | int objType; |
| 2734 | int asText; |
| 2735 | const char *zUuid = 0; |
| 2736 | u32 objdescFlags = OBJDESC_BASE; |
| 2737 | int descOnly = fossil_strcmp(g.zPath,"whatis")==0; |
| 2738 | int isFile = fossil_strcmp(g.zPath,"file")==0; |
| 2739 | const char *zLn = P("ln"); |
| 2740 | const char *zName = P("name"); |
| 2741 | const char *zCI = P("ci"); |
| 2742 | HQuery url; |
| @@ -2747,10 +2695,14 @@ | |
| 2747 | |
| 2748 | login_check_credentials(); |
| 2749 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 2750 | cgi_check_for_malice(); |
| 2751 | style_set_current_feature("artifact"); |
| 2752 | |
| 2753 | /* Capture and normalize the name= and ci= query parameters */ |
| 2754 | if( zName==0 ){ |
| 2755 | zName = P("filename"); |
| 2756 | if( zName==0 ){ |
| @@ -2849,14 +2801,23 @@ | |
| 2849 | url_add_parameter(&url, "verbose", "1"); |
| 2850 | objdescFlags |= OBJDESC_DETAIL; |
| 2851 | } |
| 2852 | zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 2853 | etag_check(ETAG_HASH, zUuid); |
| 2854 | |
| 2855 | asText = P("txt")!=0; |
| 2856 | if( isFile ){ |
| 2857 | if( zCI==0 || fossil_strcmp(zCI,"tip")==0 ){ |
| 2858 | zCI = "tip"; |
| 2859 | @ <h2>File %z(href("%R/finfo?name=%T&m&ci=tip",zName))%h(zName)</a> |
| 2860 | @ from the %z(href("%R/info/tip"))latest check-in</a></h2> |
| 2861 | }else{ |
| 2862 | const char *zPath; |
| @@ -2874,17 +2835,19 @@ | |
| 2874 | }else{ |
| 2875 | @ part of check-in %z(href("%R/info/%!S",zCIUuid))%S(zCIUuid)</a></h2> |
| 2876 | } |
| 2877 | blob_reset(&path); |
| 2878 | } |
| 2879 | style_submenu_element("Artifact", "%R/artifact/%S", zUuid); |
| 2880 | zMime = mimetype_from_name(zName); |
| 2881 | style_submenu_element("Annotate", "%R/annotate?filename=%T&checkin=%T", |
| 2882 | zName, zCI); |
| 2883 | style_submenu_element("Blame", "%R/blame?filename=%T&checkin=%T", |
| 2884 | zName, zCI); |
| 2885 | style_submenu_element("Doc", "%R/doc/%T/%T", zCI, zName); |
| 2886 | blob_init(&downloadName, zName, -1); |
| 2887 | objType = OBJTYPE_CONTENT; |
| 2888 | }else{ |
| 2889 | @ <h2>Artifact |
| 2890 | style_copy_button(1, "hash-ar", 0, 2, "%s", zUuid); |
| @@ -2903,11 +2866,11 @@ | |
| 2903 | cgi_redirectf("%R/raw/%s?at=%T", |
| 2904 | db_text("x", "SELECT uuid FROM blob WHERE rid=%d", rid), |
| 2905 | file_tail(blob_str(&downloadName))); |
| 2906 | /*NOTREACHED*/ |
| 2907 | } |
| 2908 | if( g.perm.Admin ){ |
| 2909 | const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 2910 | if( db_exists("SELECT 1 FROM shun WHERE uuid=%Q", zUuid) ){ |
| 2911 | style_submenu_element("Unshun", "%R/shun?accept=%s&sub=1#accshun", zUuid); |
| 2912 | }else{ |
| 2913 | style_submenu_element("Shun", "%R/shun?shun=%s#addshun",zUuid); |
| @@ -2948,41 +2911,49 @@ | |
| 2948 | const char *zIp = db_column_text(&q,2); |
| 2949 | @ <p>Received on %s(zDate) from %h(zUser) at %h(zIp).</p> |
| 2950 | } |
| 2951 | db_finalize(&q); |
| 2952 | } |
| 2953 | style_submenu_element("Download", "%R/raw/%s?at=%T", zUuid, file_tail(zName)); |
| 2954 | if( db_exists("SELECT 1 FROM mlink WHERE fid=%d", rid) ){ |
| 2955 | style_submenu_element("Check-ins Using", "%R/timeline?uf=%s", zUuid); |
| 2956 | } |
| 2957 | if( zMime ){ |
| 2958 | if( fossil_strcmp(zMime, "text/html")==0 ){ |
| 2959 | if( asText ){ |
| 2960 | style_submenu_element("Html", "%s", url_render(&url, "txt", 0, 0, 0)); |
| 2961 | }else{ |
| 2962 | renderAsHtml = 1; |
| 2963 | style_submenu_element("Text", "%s", url_render(&url, "txt", "1", 0, 0)); |
| 2964 | } |
| 2965 | }else if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 |
| 2966 | || fossil_strcmp(zMime, "text/x-markdown")==0 |
| 2967 | || fossil_strcmp(zMime, "text/x-pikchr")==0 ){ |
| 2968 | if( asText ){ |
| 2969 | style_submenu_element(zMime[7]=='p' ? "Pikchr" : "Wiki", |
| 2970 | "%s", url_render(&url, "txt", 0, 0, 0)); |
| 2971 | }else{ |
| 2972 | renderAsWiki = 1; |
| 2973 | style_submenu_element("Text", "%s", url_render(&url, "txt", "1", 0, 0)); |
| 2974 | } |
| 2975 | }else if( fossil_strcmp(zMime, "image/svg+xml")==0 ){ |
| 2976 | if( asText ){ |
| 2977 | style_submenu_element("Svg", "%s", url_render(&url, "txt", 0, 0, 0)); |
| 2978 | }else{ |
| 2979 | renderAsSvg = 1; |
| 2980 | style_submenu_element("Text", "%s", url_render(&url, "txt", "1", 0, 0)); |
| 2981 | } |
| 2982 | } |
| 2983 | if( fileedit_is_editable(zName) ){ |
| 2984 | style_submenu_element("Edit", |
| 2985 | "%R/fileedit?filename=%T&checkin=%!S", |
| 2986 | zName, zCI); |
| 2987 | } |
| 2988 | } |
| @@ -2990,11 +2961,13 @@ | |
| 2990 | style_submenu_element("Parsed", "%R/info/%s", zUuid); |
| 2991 | } |
| 2992 | if( descOnly ){ |
| 2993 | style_submenu_element("Content", "%R/artifact/%s", zUuid); |
| 2994 | }else{ |
| 2995 | @ <hr> |
| 2996 | content_get(rid, &content); |
| 2997 | if( renderAsWiki ){ |
| 2998 | safe_html_context(DOCSRC_FILE); |
| 2999 | wiki_render_by_mimetype(&content, zMime); |
| 3000 | document_emit_js(); |
| @@ -3660,10 +3633,11 @@ | |
| 3660 | zNewColorFlag = P("newclr") ? " checked" : ""; |
| 3661 | zNewTagFlag = P("newtag") ? " checked" : ""; |
| 3662 | zNewTag = PDT("tagname",""); |
| 3663 | zNewBrFlag = P("newbr") ? " checked" : ""; |
| 3664 | zNewBranch = PDT("brname",""); |
| 3665 | zCloseFlag = P("close") ? " checked" : ""; |
| 3666 | zHideFlag = P("hide") ? " checked" : ""; |
| 3667 | if( P("apply") && cgi_csrf_safe(2) ){ |
| 3668 | Blob ctrl; |
| 3669 | char *zNow; |
| @@ -3707,17 +3681,25 @@ | |
| 3707 | zUuid[10] = 0; |
| 3708 | style_header("Edit Check-in [%s]", zUuid); |
| 3709 | if( P("preview") ){ |
| 3710 | Blob suffix; |
| 3711 | int nTag = 0; |
| 3712 | @ <b>Preview:</b> |
| 3713 | @ <blockquote> |
| 3714 | @ <table border=0> |
| 3715 | if( zNewColorFlag[0] && zNewColor && zNewColor[0] ){ |
| 3716 | @ <tr><td style="background-color: %h(zNewColor);"> |
| 3717 | }else if( zColor[0] ){ |
| 3718 | @ <tr><td style="background-color: %h(zColor);"> |
| 3719 | }else{ |
| 3720 | @ <tr><td> |
| 3721 | } |
| 3722 | @ %!W(blob_str(&comment)) |
| 3723 | blob_zero(&suffix); |
| @@ -3798,13 +3780,10 @@ | |
| 3798 | @ <tr><th align="right" valign="top">Tags:</th> |
| 3799 | @ <td valign="top"> |
| 3800 | @ <label><input type="checkbox" id="newtag" name="newtag"%s(zNewTagFlag)> |
| 3801 | @ Add the following new tag name to this check-in:</label> |
| 3802 | @ <input size="15" name="tagname" id="tagname" value="%h(zNewTag)"> |
| 3803 | zBranchName = db_text(0, "SELECT value FROM tagxref, tag" |
| 3804 | " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid" |
| 3805 | " AND tagxref.tagid=%d", rid, TAG_BRANCH); |
| 3806 | db_prepare(&q, |
| 3807 | "SELECT tag.tagid, tagname, tagxref.value FROM tagxref, tag" |
| 3808 | " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid" |
| 3809 | " ORDER BY CASE WHEN tagname GLOB 'sym-*' THEN substr(tagname,5)" |
| 3810 | " ELSE tagname END /*sort*/", |
| @@ -3941,25 +3920,26 @@ | |
| 3941 | ** Usage: %fossil amend HASH OPTION ?OPTION ...? |
| 3942 | ** |
| 3943 | ** Amend the tags on check-in HASH to change how it displays in the timeline. |
| 3944 | ** |
| 3945 | ** Options: |
| 3946 | ** --author USER Make USER the author for check-in |
| 3947 | ** -m|--comment COMMENT Make COMMENT the check-in comment |
| 3948 | ** -M|--message-file FILE Read the amended comment from FILE |
| 3949 | ** -e|--edit-comment Launch editor to revise comment |
| 3950 | ** --date DATETIME Make DATETIME the check-in time |
| 3951 | ** --bgcolor COLOR Apply COLOR to this check-in |
| 3952 | ** --branchcolor COLOR Apply and propagate COLOR to the branch |
| 3953 | ** --tag TAG Add new TAG to this check-in |
| 3954 | ** --cancel TAG Cancel TAG from this check-in |
| 3955 | ** --branch NAME Rename branch of check-in to NAME |
| 3956 | ** --hide Hide branch starting from this check-in |
| 3957 | ** --close Mark this "leaf" as closed |
| 3958 | ** -n|--dry-run Print control artifact, but make no changes |
| 3959 | ** --date-override DATETIME Set the change time on the control artifact |
| 3960 | ** --user-override USER Set the user name on the control artifact |
| 3961 | ** |
| 3962 | ** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in |
| 3963 | ** year-month-day form, it may be truncated, the "T" may be replaced by |
| 3964 | ** a space, and it may also name a timezone offset from UTC as "-HH:MM" |
| 3965 | ** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z" |
| @@ -3986,19 +3966,22 @@ | |
| 3986 | int fNewPropagateColor = 0; /* True if color propagates after amend */ |
| 3987 | int fHasHidden = 0; /* True if hidden tag already set */ |
| 3988 | int fHasClosed = 0; /* True if closed tag already set */ |
| 3989 | int fEditComment; /* True if editor to be used for comment */ |
| 3990 | int fDryRun; /* Print control artifact, make no changes */ |
| 3991 | const char *zChngTime; /* The change time on the control artifact */ |
| 3992 | const char *zUserOvrd; /* The user name on the control artifact */ |
| 3993 | const char *zUuid; |
| 3994 | Blob ctrl; |
| 3995 | Blob comment; |
| 3996 | char *zNow; |
| 3997 | int nTags, nCancels; |
| 3998 | int i; |
| 3999 | Stmt q; |
| 4000 | |
| 4001 | fEditComment = find_option("edit-comment","e",0)!=0; |
| 4002 | zNewComment = find_option("comment","m",1); |
| 4003 | zComFile = find_option("message-file","M",1); |
| 4004 | zNewBranch = find_option("branch",0,1); |
| @@ -4016,10 +3999,11 @@ | |
| 4016 | fHide = find_option("hide",0,0)!=0; |
| 4017 | fDryRun = find_option("dry-run","n",0)!=0; |
| 4018 | zChngTime = find_option("date-override",0,1); |
| 4019 | if( zChngTime==0 ) zChngTime = find_option("chngtime",0,1); |
| 4020 | zUserOvrd = find_option("user-override",0,1); |
| 4021 | db_find_and_open_repository(0,0); |
| 4022 | user_select(); |
| 4023 | verify_all_options(); |
| 4024 | if( g.argc<3 || g.argc>=4 ) usage(AMEND_USAGE_STMT); |
| 4025 | rid = name_to_typed_rid(g.argv[2], "ci"); |
| @@ -4074,21 +4058,69 @@ | |
| 4074 | ); |
| 4075 | } |
| 4076 | if( (zNewColor!=0 && zNewColor[0]==0) && (zColor && zColor[0] ) ){ |
| 4077 | cancel_color(); |
| 4078 | } |
| 4079 | if( fEditComment ){ |
| 4080 | prepare_amend_comment(&comment, zComment, zUuid); |
| 4081 | zNewComment = blob_str(&comment); |
| 4082 | }else if( zComFile ){ |
| 4083 | blob_zero(&comment); |
| 4084 | blob_read_from_file(&comment, zComFile, ExtFILE); |
| 4085 | blob_to_utf8_no_bom(&comment, 1); |
| 4086 | zNewComment = blob_str(&comment); |
| 4087 | } |
| 4088 | if( zNewComment && zNewComment[0] |
| 4089 | && comment_compare(zComment,zNewComment)==0 ) add_comment(zNewComment); |
| 4090 | if( zNewDate && zNewDate[0] && fossil_strcmp(zDate,zNewDate)!=0 ){ |
| 4091 | if( is_datetime(zNewDate) ){ |
| 4092 | add_date(zNewDate); |
| 4093 | }else{ |
| 4094 | fossil_fatal("Unsupported date format, use YYYY-MM-DD HH:MM:SS"); |
| 4095 |
| --- src/info.c | |
| +++ src/info.c | |
| @@ -257,17 +257,19 @@ | |
| 257 | } |
| 258 | fossil_print("fossil: %z\n", file_fullexename(g.nameOfExe)); |
| 259 | fossil_print("version: %s", z); |
| 260 | blob_reset(&vx); |
| 261 | } |
| 262 | }else if( g.repositoryOpen ){ |
| 263 | int rid; |
| 264 | rid = name_to_rid(g.argv[2]); |
| 265 | if( rid==0 ){ |
| 266 | fossil_fatal("no such object: %s", g.argv[2]); |
| 267 | } |
| 268 | show_common_info(rid, "hash:", 1, 1); |
| 269 | }else{ |
| 270 | fossil_fatal("Could not find or open a Fossil repository"); |
| 271 | } |
| 272 | } |
| 273 | |
| 274 | /* |
| 275 | ** Show the context graph (immediate parents and children) for |
| @@ -925,12 +927,12 @@ | |
| 927 | " WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim", |
| 928 | rid |
| 929 | ); |
| 930 | isLeaf = !db_exists("SELECT 1 FROM plink WHERE pid=%d", rid); |
| 931 | db_prepare(&q1, |
| 932 | "SELECT uuid, datetime(mtime,toLocal(),'subsec'), user, comment," |
| 933 | " datetime(omtime,toLocal(),'subsec'), mtime" |
| 934 | " FROM blob, event" |
| 935 | " WHERE blob.rid=%d" |
| 936 | " AND event.objid=%d", |
| 937 | rid, rid |
| 938 | ); |
| @@ -1366,72 +1368,10 @@ | |
| 1368 | webpage_error("Artifact %s is not a check-in.", P(zParam)); |
| 1369 | return 0; |
| 1370 | } |
| 1371 | return manifest_get(rid, CFTYPE_MANIFEST, 0); |
| 1372 | } |
| 1373 | |
| 1374 | /* |
| 1375 | ** WEBPAGE: vdiff |
| 1376 | ** URL: /vdiff?from=TAG&to=TAG |
| 1377 | ** |
| @@ -2682,31 +2622,36 @@ | |
| 2622 | |
| 2623 | /* |
| 2624 | ** WEBPAGE: artifact |
| 2625 | ** WEBPAGE: file |
| 2626 | ** WEBPAGE: whatis |
| 2627 | ** WEBPAGE: docfile |
| 2628 | ** |
| 2629 | ** Typical usage: |
| 2630 | ** |
| 2631 | ** /artifact/HASH |
| 2632 | ** /whatis/HASH |
| 2633 | ** /file/NAME |
| 2634 | ** /docfile/NAME |
| 2635 | ** |
| 2636 | ** Additional query parameters: |
| 2637 | ** |
| 2638 | ** ln - show line numbers |
| 2639 | ** ln=N - highlight line number N |
| 2640 | ** ln=M-N - highlight lines M through N inclusive |
| 2641 | ** ln=M-N+Y-Z - highlight lines M through N and Y through Z (inclusive) |
| 2642 | ** verbose - show more detail in the description |
| 2643 | ** brief - show just the document, not the metadata. The |
| 2644 | ** /docfile page is an alias for /file?brief |
| 2645 | ** download - redirect to the download (artifact page only) |
| 2646 | ** name=NAME - filename or hash as a query parameter |
| 2647 | ** filename=NAME - alternative spelling for "name=" |
| 2648 | ** fn=NAME - alternative spelling for "name=" |
| 2649 | ** ci=VERSION - The specific check-in to use with "name=" to |
| 2650 | ** identify the file. |
| 2651 | ** txt - Force display of unformatted source text |
| 2652 | ** hash - Output only the hash of the artifact |
| 2653 | ** |
| 2654 | ** The /artifact page show the complete content of a file |
| 2655 | ** identified by HASH. The /whatis page shows only a description |
| 2656 | ** of how the artifact is used. The /file page shows the most recent |
| 2657 | ** version of the file or directory called NAME, or a list of the |
| @@ -2725,18 +2670,21 @@ | |
| 2670 | void artifact_page(void){ |
| 2671 | int rid = 0; |
| 2672 | Blob content; |
| 2673 | const char *zMime; |
| 2674 | Blob downloadName; |
| 2675 | Blob uuid; |
| 2676 | int renderAsWiki = 0; |
| 2677 | int renderAsHtml = 0; |
| 2678 | int renderAsSvg = 0; |
| 2679 | int objType; |
| 2680 | int asText; |
| 2681 | const char *zUuid = 0; |
| 2682 | u32 objdescFlags = OBJDESC_BASE; |
| 2683 | int descOnly = fossil_strcmp(g.zPath,"whatis")==0; |
| 2684 | int hashOnly = P("hash")!=0; |
| 2685 | int docOnly = P("brief")!=0; |
| 2686 | int isFile = fossil_strcmp(g.zPath,"file")==0; |
| 2687 | const char *zLn = P("ln"); |
| 2688 | const char *zName = P("name"); |
| 2689 | const char *zCI = P("ci"); |
| 2690 | HQuery url; |
| @@ -2747,10 +2695,14 @@ | |
| 2695 | |
| 2696 | login_check_credentials(); |
| 2697 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 2698 | cgi_check_for_malice(); |
| 2699 | style_set_current_feature("artifact"); |
| 2700 | if( fossil_strcmp(g.zPath, "docfile")==0 ){ |
| 2701 | isFile = 1; |
| 2702 | docOnly = 1; |
| 2703 | } |
| 2704 | |
| 2705 | /* Capture and normalize the name= and ci= query parameters */ |
| 2706 | if( zName==0 ){ |
| 2707 | zName = P("filename"); |
| 2708 | if( zName==0 ){ |
| @@ -2849,14 +2801,23 @@ | |
| 2801 | url_add_parameter(&url, "verbose", "1"); |
| 2802 | objdescFlags |= OBJDESC_DETAIL; |
| 2803 | } |
| 2804 | zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 2805 | etag_check(ETAG_HASH, zUuid); |
| 2806 | |
| 2807 | if( descOnly && hashOnly ){ |
| 2808 | blob_set(&uuid, zUuid); |
| 2809 | cgi_set_content_type("text/plain"); |
| 2810 | cgi_set_content(&uuid); |
| 2811 | return; |
| 2812 | } |
| 2813 | |
| 2814 | asText = P("txt")!=0; |
| 2815 | if( isFile ){ |
| 2816 | if( docOnly ){ |
| 2817 | /* No header */ |
| 2818 | }else if( zCI==0 || fossil_strcmp(zCI,"tip")==0 ){ |
| 2819 | zCI = "tip"; |
| 2820 | @ <h2>File %z(href("%R/finfo?name=%T&m&ci=tip",zName))%h(zName)</a> |
| 2821 | @ from the %z(href("%R/info/tip"))latest check-in</a></h2> |
| 2822 | }else{ |
| 2823 | const char *zPath; |
| @@ -2874,17 +2835,19 @@ | |
| 2835 | }else{ |
| 2836 | @ part of check-in %z(href("%R/info/%!S",zCIUuid))%S(zCIUuid)</a></h2> |
| 2837 | } |
| 2838 | blob_reset(&path); |
| 2839 | } |
| 2840 | zMime = mimetype_from_name(zName); |
| 2841 | if( !docOnly ){ |
| 2842 | style_submenu_element("Artifact", "%R/artifact/%S", zUuid); |
| 2843 | style_submenu_element("Annotate", "%R/annotate?filename=%T&checkin=%T", |
| 2844 | zName, zCI); |
| 2845 | style_submenu_element("Blame", "%R/blame?filename=%T&checkin=%T", |
| 2846 | zName, zCI); |
| 2847 | style_submenu_element("Doc", "%R/doc/%T/%T", zCI, zName); |
| 2848 | } |
| 2849 | blob_init(&downloadName, zName, -1); |
| 2850 | objType = OBJTYPE_CONTENT; |
| 2851 | }else{ |
| 2852 | @ <h2>Artifact |
| 2853 | style_copy_button(1, "hash-ar", 0, 2, "%s", zUuid); |
| @@ -2903,11 +2866,11 @@ | |
| 2866 | cgi_redirectf("%R/raw/%s?at=%T", |
| 2867 | db_text("x", "SELECT uuid FROM blob WHERE rid=%d", rid), |
| 2868 | file_tail(blob_str(&downloadName))); |
| 2869 | /*NOTREACHED*/ |
| 2870 | } |
| 2871 | if( g.perm.Admin && !docOnly ){ |
| 2872 | const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 2873 | if( db_exists("SELECT 1 FROM shun WHERE uuid=%Q", zUuid) ){ |
| 2874 | style_submenu_element("Unshun", "%R/shun?accept=%s&sub=1#accshun", zUuid); |
| 2875 | }else{ |
| 2876 | style_submenu_element("Shun", "%R/shun?shun=%s#addshun",zUuid); |
| @@ -2948,41 +2911,49 @@ | |
| 2911 | const char *zIp = db_column_text(&q,2); |
| 2912 | @ <p>Received on %s(zDate) from %h(zUser) at %h(zIp).</p> |
| 2913 | } |
| 2914 | db_finalize(&q); |
| 2915 | } |
| 2916 | if( !docOnly ){ |
| 2917 | style_submenu_element("Download", "%R/raw/%s?at=%T",zUuid,file_tail(zName)); |
| 2918 | if( db_exists("SELECT 1 FROM mlink WHERE fid=%d", rid) ){ |
| 2919 | style_submenu_element("Check-ins Using", "%R/timeline?uf=%s", zUuid); |
| 2920 | } |
| 2921 | } |
| 2922 | if( zMime ){ |
| 2923 | if( fossil_strcmp(zMime, "text/html")==0 ){ |
| 2924 | if( asText ){ |
| 2925 | style_submenu_element("Html", "%s", url_render(&url, "txt", 0, 0, 0)); |
| 2926 | }else{ |
| 2927 | renderAsHtml = 1; |
| 2928 | if( !docOnly ){ |
| 2929 | style_submenu_element("Text", "%s", url_render(&url, "txt","1",0,0)); |
| 2930 | } |
| 2931 | } |
| 2932 | }else if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 |
| 2933 | || fossil_strcmp(zMime, "text/x-markdown")==0 |
| 2934 | || fossil_strcmp(zMime, "text/x-pikchr")==0 ){ |
| 2935 | if( asText ){ |
| 2936 | style_submenu_element(zMime[7]=='p' ? "Pikchr" : "Wiki", |
| 2937 | "%s", url_render(&url, "txt", 0, 0, 0)); |
| 2938 | }else{ |
| 2939 | renderAsWiki = 1; |
| 2940 | if( !docOnly ){ |
| 2941 | style_submenu_element("Text", "%s", url_render(&url, "txt","1",0,0)); |
| 2942 | } |
| 2943 | } |
| 2944 | }else if( fossil_strcmp(zMime, "image/svg+xml")==0 ){ |
| 2945 | if( asText ){ |
| 2946 | style_submenu_element("Svg", "%s", url_render(&url, "txt", 0, 0, 0)); |
| 2947 | }else{ |
| 2948 | renderAsSvg = 1; |
| 2949 | if( !docOnly ){ |
| 2950 | style_submenu_element("Text", "%s", url_render(&url, "txt","1",0,0)); |
| 2951 | } |
| 2952 | } |
| 2953 | } |
| 2954 | if( !docOnly && fileedit_is_editable(zName) ){ |
| 2955 | style_submenu_element("Edit", |
| 2956 | "%R/fileedit?filename=%T&checkin=%!S", |
| 2957 | zName, zCI); |
| 2958 | } |
| 2959 | } |
| @@ -2990,11 +2961,13 @@ | |
| 2961 | style_submenu_element("Parsed", "%R/info/%s", zUuid); |
| 2962 | } |
| 2963 | if( descOnly ){ |
| 2964 | style_submenu_element("Content", "%R/artifact/%s", zUuid); |
| 2965 | }else{ |
| 2966 | if( !docOnly || !isFile ){ |
| 2967 | @ <hr> |
| 2968 | } |
| 2969 | content_get(rid, &content); |
| 2970 | if( renderAsWiki ){ |
| 2971 | safe_html_context(DOCSRC_FILE); |
| 2972 | wiki_render_by_mimetype(&content, zMime); |
| 2973 | document_emit_js(); |
| @@ -3660,10 +3633,11 @@ | |
| 3633 | zNewColorFlag = P("newclr") ? " checked" : ""; |
| 3634 | zNewTagFlag = P("newtag") ? " checked" : ""; |
| 3635 | zNewTag = PDT("tagname",""); |
| 3636 | zNewBrFlag = P("newbr") ? " checked" : ""; |
| 3637 | zNewBranch = PDT("brname",""); |
| 3638 | zBranchName = branch_of_rid(rid); |
| 3639 | zCloseFlag = P("close") ? " checked" : ""; |
| 3640 | zHideFlag = P("hide") ? " checked" : ""; |
| 3641 | if( P("apply") && cgi_csrf_safe(2) ){ |
| 3642 | Blob ctrl; |
| 3643 | char *zNow; |
| @@ -3707,17 +3681,25 @@ | |
| 3681 | zUuid[10] = 0; |
| 3682 | style_header("Edit Check-in [%s]", zUuid); |
| 3683 | if( P("preview") ){ |
| 3684 | Blob suffix; |
| 3685 | int nTag = 0; |
| 3686 | const char *zDplyBr; /* Branch name used to determine BG color */ |
| 3687 | if( zNewBrFlag[0] && zNewBranch[0] ){ |
| 3688 | zDplyBr = zNewBranch; |
| 3689 | }else{ |
| 3690 | zDplyBr = zBranchName; |
| 3691 | } |
| 3692 | @ <b>Preview:</b> |
| 3693 | @ <blockquote> |
| 3694 | @ <table border=0> |
| 3695 | if( zNewColorFlag[0] && zNewColor && zNewColor[0] ){ |
| 3696 | @ <tr><td style="background-color:%h(reasonable_bg_color(zNewColor,0));"> |
| 3697 | }else if( zColor[0] ){ |
| 3698 | @ <tr><td style="background-color:%h(reasonable_bg_color(zColor,0));"> |
| 3699 | }else if( zDplyBr && fossil_strcmp(zDplyBr,"trunk")!=0 ){ |
| 3700 | @ <tr><td style="background-color:%h(hash_color(zDplyBr));"> |
| 3701 | }else{ |
| 3702 | @ <tr><td> |
| 3703 | } |
| 3704 | @ %!W(blob_str(&comment)) |
| 3705 | blob_zero(&suffix); |
| @@ -3798,13 +3780,10 @@ | |
| 3780 | @ <tr><th align="right" valign="top">Tags:</th> |
| 3781 | @ <td valign="top"> |
| 3782 | @ <label><input type="checkbox" id="newtag" name="newtag"%s(zNewTagFlag)> |
| 3783 | @ Add the following new tag name to this check-in:</label> |
| 3784 | @ <input size="15" name="tagname" id="tagname" value="%h(zNewTag)"> |
| 3785 | db_prepare(&q, |
| 3786 | "SELECT tag.tagid, tagname, tagxref.value FROM tagxref, tag" |
| 3787 | " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid" |
| 3788 | " ORDER BY CASE WHEN tagname GLOB 'sym-*' THEN substr(tagname,5)" |
| 3789 | " ELSE tagname END /*sort*/", |
| @@ -3941,25 +3920,26 @@ | |
| 3920 | ** Usage: %fossil amend HASH OPTION ?OPTION ...? |
| 3921 | ** |
| 3922 | ** Amend the tags on check-in HASH to change how it displays in the timeline. |
| 3923 | ** |
| 3924 | ** Options: |
| 3925 | ** --author USER Make USER the author for check-in |
| 3926 | ** --bgcolor COLOR Apply COLOR to this check-in |
| 3927 | ** --branch NAME Rename branch of check-in to NAME |
| 3928 | ** --branchcolor COLOR Apply and propagate COLOR to the branch |
| 3929 | ** --cancel TAG Cancel TAG from this check-in |
| 3930 | ** --close Mark this "leaf" as closed |
| 3931 | ** --date DATETIME Make DATETIME the check-in time |
| 3932 | ** --date-override DATETIME Set the change time on the control artifact |
| 3933 | ** -e|--edit-comment Launch editor to revise comment |
| 3934 | ** --hide Hide branch starting from this check-in |
| 3935 | ** -m|--comment COMMENT Make COMMENT the check-in comment |
| 3936 | ** -M|--message-file FILE Read the amended comment from FILE |
| 3937 | ** -n|--dry-run Print control artifact, but make no changes |
| 3938 | ** --no-verify-comment Do not validate the check-in comment |
| 3939 | ** --tag TAG Add new TAG to this check-in |
| 3940 | ** --user-override USER Set the user name on the control artifact |
| 3941 | ** |
| 3942 | ** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in |
| 3943 | ** year-month-day form, it may be truncated, the "T" may be replaced by |
| 3944 | ** a space, and it may also name a timezone offset from UTC as "-HH:MM" |
| 3945 | ** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z" |
| @@ -3986,19 +3966,22 @@ | |
| 3966 | int fNewPropagateColor = 0; /* True if color propagates after amend */ |
| 3967 | int fHasHidden = 0; /* True if hidden tag already set */ |
| 3968 | int fHasClosed = 0; /* True if closed tag already set */ |
| 3969 | int fEditComment; /* True if editor to be used for comment */ |
| 3970 | int fDryRun; /* Print control artifact, make no changes */ |
| 3971 | int noVerifyCom = 0; /* Allow suspicious check-in comments */ |
| 3972 | const char *zChngTime; /* The change time on the control artifact */ |
| 3973 | const char *zUserOvrd; /* The user name on the control artifact */ |
| 3974 | const char *zUuid; |
| 3975 | Blob ctrl; |
| 3976 | Blob comment; |
| 3977 | char *zNow; |
| 3978 | int nTags, nCancels; |
| 3979 | int i; |
| 3980 | Stmt q; |
| 3981 | int ckComFlgs; /* Flags passed to verify_comment() */ |
| 3982 | |
| 3983 | |
| 3984 | fEditComment = find_option("edit-comment","e",0)!=0; |
| 3985 | zNewComment = find_option("comment","m",1); |
| 3986 | zComFile = find_option("message-file","M",1); |
| 3987 | zNewBranch = find_option("branch",0,1); |
| @@ -4016,10 +3999,11 @@ | |
| 3999 | fHide = find_option("hide",0,0)!=0; |
| 4000 | fDryRun = find_option("dry-run","n",0)!=0; |
| 4001 | zChngTime = find_option("date-override",0,1); |
| 4002 | if( zChngTime==0 ) zChngTime = find_option("chngtime",0,1); |
| 4003 | zUserOvrd = find_option("user-override",0,1); |
| 4004 | noVerifyCom = find_option("no-verify-comment",0,0)!=0; |
| 4005 | db_find_and_open_repository(0,0); |
| 4006 | user_select(); |
| 4007 | verify_all_options(); |
| 4008 | if( g.argc<3 || g.argc>=4 ) usage(AMEND_USAGE_STMT); |
| 4009 | rid = name_to_typed_rid(g.argv[2], "ci"); |
| @@ -4074,21 +4058,69 @@ | |
| 4058 | ); |
| 4059 | } |
| 4060 | if( (zNewColor!=0 && zNewColor[0]==0) && (zColor && zColor[0] ) ){ |
| 4061 | cancel_color(); |
| 4062 | } |
| 4063 | if( fEditComment || zNewComment || zComFile ){ |
| 4064 | blob_init(&comment, 0, 0); |
| 4065 | |
| 4066 | /* Figure out how much comment verification is requested */ |
| 4067 | if( noVerifyCom ){ |
| 4068 | ckComFlgs = 0; |
| 4069 | }else{ |
| 4070 | const char *zVerComs = db_get("verify-comments","on"); |
| 4071 | if( is_false(zVerComs) ){ |
| 4072 | ckComFlgs = 0; |
| 4073 | }else if( strcmp(zVerComs,"preview")==0 ){ |
| 4074 | ckComFlgs = COMCK_PREVIEW | COMCK_MARKUP; |
| 4075 | }else{ |
| 4076 | ckComFlgs = COMCK_MARKUP; |
| 4077 | } |
| 4078 | } |
| 4079 | if( fEditComment ){ |
| 4080 | prepare_amend_comment(&comment, zComment, zUuid); |
| 4081 | }else if( zComFile ){ |
| 4082 | blob_read_from_file(&comment, zComFile, ExtFILE); |
| 4083 | blob_to_utf8_no_bom(&comment, 1); |
| 4084 | }else if( zNewComment ){ |
| 4085 | blob_init(&comment, zNewComment, -1); |
| 4086 | } |
| 4087 | if( blob_size(&comment)>0 |
| 4088 | && comment_compare(zComment, blob_str(&comment))==0 |
| 4089 | ){ |
| 4090 | int rc; |
| 4091 | while( (rc = verify_comment(&comment, ckComFlgs))!=0 ){ |
| 4092 | char cReply; |
| 4093 | Blob ans; |
| 4094 | if( !fEditComment ){ |
| 4095 | fossil_fatal("Amend aborted; " |
| 4096 | "use --no-verify-comment to override"); |
| 4097 | } |
| 4098 | if( rc==COMCK_PREVIEW ){ |
| 4099 | prompt_user("Continue, abort, or edit (C/a/e)? ", &ans); |
| 4100 | }else{ |
| 4101 | prompt_user("Edit, abort, or continue (E/a/c)? ", &ans); |
| 4102 | } |
| 4103 | cReply = blob_str(&ans)[0]; |
| 4104 | cReply = fossil_tolower(cReply); |
| 4105 | blob_reset(&ans); |
| 4106 | if( cReply=='a' ){ |
| 4107 | fossil_fatal("Amend aborted."); |
| 4108 | } |
| 4109 | if( cReply=='e' || (cReply!='c' && rc!=COMCK_PREVIEW) ){ |
| 4110 | char *zPrior = blob_materialize(&comment); |
| 4111 | blob_init(&comment, 0, 0); |
| 4112 | prepare_amend_comment(&comment, zPrior, zUuid); |
| 4113 | fossil_free(zPrior); |
| 4114 | continue; |
| 4115 | }else{ |
| 4116 | break; |
| 4117 | } |
| 4118 | } |
| 4119 | } |
| 4120 | add_comment(blob_str(&comment)); |
| 4121 | } |
| 4122 | if( zNewDate && zNewDate[0] && fossil_strcmp(zDate,zNewDate)!=0 ){ |
| 4123 | if( is_datetime(zNewDate) ){ |
| 4124 | add_date(zNewDate); |
| 4125 | }else{ |
| 4126 | fossil_fatal("Unsupported date format, use YYYY-MM-DD HH:MM:SS"); |
| 4127 |
+3
-3
| --- src/interwiki.c | ||
| +++ src/interwiki.c | ||
| @@ -161,23 +161,23 @@ | ||
| 161 | 161 | ** Usage: %fossil interwiki COMMAND ... |
| 162 | 162 | ** |
| 163 | 163 | ** Manage the "intermap" that defines the mapping from interwiki tags |
| 164 | 164 | ** to complete URLs for interwiki links. |
| 165 | 165 | ** |
| 166 | -** > fossil interwiki delete TAG ... | |
| 166 | +** > fossil interwiki delete TAG ... | |
| 167 | 167 | ** |
| 168 | 168 | ** Delete one or more interwiki maps. |
| 169 | 169 | ** |
| 170 | -** > fossil interwiki edit TAG --base URL --hash PATH --wiki PATH | |
| 170 | +** > fossil interwiki edit TAG --base URL --hash PATH --wiki PATH | |
| 171 | 171 | ** |
| 172 | 172 | ** Create an interwiki referenced call TAG. The base URL is |
| 173 | 173 | ** the --base option, which is required. The --hash and --wiki |
| 174 | 174 | ** paths are optional. The TAG must be lower-case alphanumeric |
| 175 | 175 | ** and must be unique. A new entry is created if it does not |
| 176 | 176 | ** already exit. |
| 177 | 177 | ** |
| 178 | -** > fossil interwiki list | |
| 178 | +** > fossil interwiki list | |
| 179 | 179 | ** |
| 180 | 180 | ** Show all interwiki mappings. |
| 181 | 181 | */ |
| 182 | 182 | void interwiki_cmd(void){ |
| 183 | 183 | const char *zCmd; |
| 184 | 184 |
| --- src/interwiki.c | |
| +++ src/interwiki.c | |
| @@ -161,23 +161,23 @@ | |
| 161 | ** Usage: %fossil interwiki COMMAND ... |
| 162 | ** |
| 163 | ** Manage the "intermap" that defines the mapping from interwiki tags |
| 164 | ** to complete URLs for interwiki links. |
| 165 | ** |
| 166 | ** > fossil interwiki delete TAG ... |
| 167 | ** |
| 168 | ** Delete one or more interwiki maps. |
| 169 | ** |
| 170 | ** > fossil interwiki edit TAG --base URL --hash PATH --wiki PATH |
| 171 | ** |
| 172 | ** Create an interwiki referenced call TAG. The base URL is |
| 173 | ** the --base option, which is required. The --hash and --wiki |
| 174 | ** paths are optional. The TAG must be lower-case alphanumeric |
| 175 | ** and must be unique. A new entry is created if it does not |
| 176 | ** already exit. |
| 177 | ** |
| 178 | ** > fossil interwiki list |
| 179 | ** |
| 180 | ** Show all interwiki mappings. |
| 181 | */ |
| 182 | void interwiki_cmd(void){ |
| 183 | const char *zCmd; |
| 184 |
| --- src/interwiki.c | |
| +++ src/interwiki.c | |
| @@ -161,23 +161,23 @@ | |
| 161 | ** Usage: %fossil interwiki COMMAND ... |
| 162 | ** |
| 163 | ** Manage the "intermap" that defines the mapping from interwiki tags |
| 164 | ** to complete URLs for interwiki links. |
| 165 | ** |
| 166 | ** > fossil interwiki delete TAG ... |
| 167 | ** |
| 168 | ** Delete one or more interwiki maps. |
| 169 | ** |
| 170 | ** > fossil interwiki edit TAG --base URL --hash PATH --wiki PATH |
| 171 | ** |
| 172 | ** Create an interwiki referenced call TAG. The base URL is |
| 173 | ** the --base option, which is required. The --hash and --wiki |
| 174 | ** paths are optional. The TAG must be lower-case alphanumeric |
| 175 | ** and must be unique. A new entry is created if it does not |
| 176 | ** already exit. |
| 177 | ** |
| 178 | ** > fossil interwiki list |
| 179 | ** |
| 180 | ** Show all interwiki mappings. |
| 181 | */ |
| 182 | void interwiki_cmd(void){ |
| 183 | const char *zCmd; |
| 184 |
-1
| --- src/json_config.c | ||
| +++ src/json_config.c | ||
| @@ -86,11 +86,10 @@ | ||
| 86 | 86 | { "logo-image", CONFIGSET_SKIN }, |
| 87 | 87 | { "background-mimetype", CONFIGSET_SKIN }, |
| 88 | 88 | { "background-image", CONFIGSET_SKIN }, |
| 89 | 89 | { "icon-mimetype", CONFIGSET_SKIN }, |
| 90 | 90 | { "icon-image", CONFIGSET_SKIN }, |
| 91 | -{ "timeline-block-markup", CONFIGSET_SKIN }, | |
| 92 | 91 | { "timeline-date-format", CONFIGSET_SKIN }, |
| 93 | 92 | { "timeline-default-style", CONFIGSET_SKIN }, |
| 94 | 93 | { "timeline-dwelltime", CONFIGSET_SKIN }, |
| 95 | 94 | { "timeline-closetime", CONFIGSET_SKIN }, |
| 96 | 95 | { "timeline-hard-newlines", CONFIGSET_SKIN }, |
| 97 | 96 |
| --- src/json_config.c | |
| +++ src/json_config.c | |
| @@ -86,11 +86,10 @@ | |
| 86 | { "logo-image", CONFIGSET_SKIN }, |
| 87 | { "background-mimetype", CONFIGSET_SKIN }, |
| 88 | { "background-image", CONFIGSET_SKIN }, |
| 89 | { "icon-mimetype", CONFIGSET_SKIN }, |
| 90 | { "icon-image", CONFIGSET_SKIN }, |
| 91 | { "timeline-block-markup", CONFIGSET_SKIN }, |
| 92 | { "timeline-date-format", CONFIGSET_SKIN }, |
| 93 | { "timeline-default-style", CONFIGSET_SKIN }, |
| 94 | { "timeline-dwelltime", CONFIGSET_SKIN }, |
| 95 | { "timeline-closetime", CONFIGSET_SKIN }, |
| 96 | { "timeline-hard-newlines", CONFIGSET_SKIN }, |
| 97 |
| --- src/json_config.c | |
| +++ src/json_config.c | |
| @@ -86,11 +86,10 @@ | |
| 86 | { "logo-image", CONFIGSET_SKIN }, |
| 87 | { "background-mimetype", CONFIGSET_SKIN }, |
| 88 | { "background-image", CONFIGSET_SKIN }, |
| 89 | { "icon-mimetype", CONFIGSET_SKIN }, |
| 90 | { "icon-image", CONFIGSET_SKIN }, |
| 91 | { "timeline-date-format", CONFIGSET_SKIN }, |
| 92 | { "timeline-default-style", CONFIGSET_SKIN }, |
| 93 | { "timeline-dwelltime", CONFIGSET_SKIN }, |
| 94 | { "timeline-closetime", CONFIGSET_SKIN }, |
| 95 | { "timeline-hard-newlines", CONFIGSET_SKIN }, |
| 96 |
+57
-23
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -639,14 +639,15 @@ | ||
| 639 | 639 | fossil_warning("%s", blob_str(&msg)); |
| 640 | 640 | blob_reset(&msg); |
| 641 | 641 | } |
| 642 | 642 | |
| 643 | 643 | /* |
| 644 | -** This function attempts to find command line options known to contain | |
| 645 | -** bitwise flags and initializes the associated global variables. After | |
| 646 | -** this function executes, all global variables (i.e. in the "g" struct) | |
| 647 | -** containing option-settable bitwise flag fields must be initialized. | |
| 644 | +** Initialize the g.comFmtFlags global variable. | |
| 645 | +** | |
| 646 | +** Global command-line options --comfmtflags or --comment-format can be | |
| 647 | +** used for this. However, those command-line options are undocumented | |
| 648 | +** and deprecated. They are here for backwards compatibility only. | |
| 648 | 649 | */ |
| 649 | 650 | static void fossil_init_flags_from_options(void){ |
| 650 | 651 | const char *zValue = find_option("comfmtflags", 0, 1); |
| 651 | 652 | if( zValue==0 ){ |
| 652 | 653 | zValue = find_option("comment-format", 0, 1); |
| @@ -725,14 +726,14 @@ | ||
| 725 | 726 | fossil_limit_memory(1); |
| 726 | 727 | |
| 727 | 728 | /* When updating the minimum SQLite version, change the number here, |
| 728 | 729 | ** and also MINIMUM_SQLITE_VERSION value set in ../auto.def. Take |
| 729 | 730 | ** care that both places agree! */ |
| 730 | - if( sqlite3_libversion_number()<3046000 | |
| 731 | - || strncmp(sqlite3_sourceid(),"2024-08-16",10)<0 | |
| 731 | + if( sqlite3_libversion_number()<3049000 | |
| 732 | + || strncmp(sqlite3_sourceid(),"2025-02-06",10)<0 | |
| 732 | 733 | ){ |
| 733 | - fossil_panic("Unsuitable SQLite version %s, must be at least 3.43.0", | |
| 734 | + fossil_panic("Unsuitable SQLite version %s, must be at least 3.49.0", | |
| 734 | 735 | sqlite3_libversion()); |
| 735 | 736 | } |
| 736 | 737 | |
| 737 | 738 | sqlite3_config(SQLITE_CONFIG_MULTITHREAD); |
| 738 | 739 | sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0); |
| @@ -997,15 +998,12 @@ | ||
| 997 | 998 | |
| 998 | 999 | /* |
| 999 | 1000 | ** Remove n elements from g.argv beginning with the i-th element. |
| 1000 | 1001 | */ |
| 1001 | 1002 | static void remove_from_argv(int i, int n){ |
| 1002 | - int j; | |
| 1003 | - for(j=i+n; j<g.argc; i++, j++){ | |
| 1004 | - g.argv[i] = g.argv[j]; | |
| 1005 | - } | |
| 1006 | - g.argc = i; | |
| 1003 | + memmove(&g.argv[i], &g.argv[i+n], sizeof(g.argv[i])*(g.argc-i-n)); | |
| 1004 | + g.argc -= n; | |
| 1007 | 1005 | } |
| 1008 | 1006 | |
| 1009 | 1007 | |
| 1010 | 1008 | /* |
| 1011 | 1009 | ** Look for a command-line option. If present, remove it from the |
| @@ -1064,10 +1062,19 @@ | ||
| 1064 | 1062 | break; |
| 1065 | 1063 | } |
| 1066 | 1064 | } |
| 1067 | 1065 | return zReturn; |
| 1068 | 1066 | } |
| 1067 | + | |
| 1068 | +/* | |
| 1069 | +** Restore an option previously removed by find_option(). | |
| 1070 | +*/ | |
| 1071 | +void restore_option(const char *zName, const char *zValue, int hasOpt){ | |
| 1072 | + if( zValue==0 && hasOpt ) return; | |
| 1073 | + g.argv[g.argc++] = (char*)zName; | |
| 1074 | + if( hasOpt ) g.argv[g.argc++] = (char*)zValue; | |
| 1075 | +} | |
| 1069 | 1076 | |
| 1070 | 1077 | /* Return true if zOption exists in the command-line arguments, |
| 1071 | 1078 | ** but do not remove it from the list or otherwise process it. |
| 1072 | 1079 | */ |
| 1073 | 1080 | int has_option(const char *zOption){ |
| @@ -1848,10 +1855,15 @@ | ||
| 1848 | 1855 | ** not exist. |
| 1849 | 1856 | */ |
| 1850 | 1857 | zCleanRepo = file_cleanup_fullpath(zRepo); |
| 1851 | 1858 | if( szFile==0 && sqlite3_strglob("*/.fossil",zRepo)!=0 ){ |
| 1852 | 1859 | szFile = file_size(zCleanRepo, ExtFILE); |
| 1860 | + if( szFile>0 && !file_isfile(zCleanRepo, ExtFILE) ){ | |
| 1861 | + /* Only let szFile be non-negative if zCleanRepo really is a file | |
| 1862 | + ** and not a directory or some other filesystem object. */ | |
| 1863 | + szFile = -1; | |
| 1864 | + } | |
| 1853 | 1865 | if( g.fHttpTrace ){ |
| 1854 | 1866 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", szFile); |
| 1855 | 1867 | @ <!-- file_size(%h(zCleanRepo)) is %s(zBuf) --> |
| 1856 | 1868 | fprintf(stderr, "# file_size(%s) = %s\n", zCleanRepo, zBuf); |
| 1857 | 1869 | } |
| @@ -2155,11 +2167,11 @@ | ||
| 2155 | 2167 | } |
| 2156 | 2168 | } |
| 2157 | 2169 | #endif |
| 2158 | 2170 | if( (pCmd->eCmdFlags & CMDFLAG_RAWCONTENT)==0 ){ |
| 2159 | 2171 | cgi_decode_post_parameters(); |
| 2160 | - if( !cgi_same_origin() ){ | |
| 2172 | + if( !cgi_same_origin(0) ){ | |
| 2161 | 2173 | isReadonly = 1; |
| 2162 | 2174 | db_protect(PROTECT_READONLY); |
| 2163 | 2175 | } |
| 2164 | 2176 | } |
| 2165 | 2177 | if( g.fCgiTrace ){ |
| @@ -2398,11 +2410,11 @@ | ||
| 2398 | 2410 | ** The lines are processed in the order they are read, which is most |
| 2399 | 2411 | ** significant for "errorlog:", which should be set before "repository:" |
| 2400 | 2412 | ** so that any warnings from the database when opening the repository |
| 2401 | 2413 | ** go to that log file. |
| 2402 | 2414 | ** |
| 2403 | -** See also: [[http]], [[server]], [[winsrv]] | |
| 2415 | +** See also: [[http]], [[server]], [[winsrv]] [Windows only] | |
| 2404 | 2416 | */ |
| 2405 | 2417 | void cmd_cgi(void){ |
| 2406 | 2418 | const char *zNotFound = 0; |
| 2407 | 2419 | char **azRedirect = 0; /* List of repositories to redirect to */ |
| 2408 | 2420 | int nRedirect = 0; /* Number of entries in azRedirect */ |
| @@ -2530,12 +2542,16 @@ | ||
| 2530 | 2542 | ** setenv: NAME |
| 2531 | 2543 | ** |
| 2532 | 2544 | ** Sets environment variable NAME to VALUE. If VALUE is omitted, then |
| 2533 | 2545 | ** the environment variable is unset. |
| 2534 | 2546 | */ |
| 2535 | - blob_token(&line,&value2); | |
| 2536 | - fossil_setenv(blob_str(&value), blob_str(&value2)); | |
| 2547 | + char *zValue; | |
| 2548 | + blob_tail(&line,&value2); | |
| 2549 | + blob_trim(&value2); | |
| 2550 | + zValue = blob_str(&value2); | |
| 2551 | + while( fossil_isspace(zValue[0]) ){ zValue++; } | |
| 2552 | + fossil_setenv(blob_str(&value), zValue); | |
| 2537 | 2553 | blob_reset(&value); |
| 2538 | 2554 | blob_reset(&value2); |
| 2539 | 2555 | continue; |
| 2540 | 2556 | } |
| 2541 | 2557 | if( blob_eq(&key, "errorlog:") && blob_token(&line, &value) ){ |
| @@ -2857,11 +2873,11 @@ | ||
| 2857 | 2873 | ** to force use of the current local skin config. |
| 2858 | 2874 | ** --th-trace Trace TH1 execution (for debugging purposes) |
| 2859 | 2875 | ** --usepidkey Use saved encryption key from parent process. This is |
| 2860 | 2876 | ** only necessary when using SEE on Windows or Linux. |
| 2861 | 2877 | ** |
| 2862 | -** See also: [[cgi]], [[server]], [[winsrv]] | |
| 2878 | +** See also: [[cgi]], [[server]], [[winsrv]] [Windows only] | |
| 2863 | 2879 | */ |
| 2864 | 2880 | void cmd_http(void){ |
| 2865 | 2881 | const char *zIpAddr = 0; |
| 2866 | 2882 | const char *zNotFound; |
| 2867 | 2883 | const char *zHost; |
| @@ -3188,10 +3204,13 @@ | ||
| 3188 | 3204 | ** --chroot DIR Use directory for chroot instead of repository path |
| 3189 | 3205 | ** --ckout-alias NAME Treat URIs of the form /doc/NAME/... as if they were |
| 3190 | 3206 | ** /doc/ckout/... |
| 3191 | 3207 | ** --create Create a new REPOSITORY if it does not already exist |
| 3192 | 3208 | ** --errorlog FILE Append HTTP error messages to FILE |
| 3209 | +** --extpage FILE Shortcut for "--extroot DIR --page ext/TAIL" where | |
| 3210 | +** DIR is the directory holding FILE and TAIL is the | |
| 3211 | +** filename at the end of FILE. Only works for "ui". | |
| 3193 | 3212 | ** --extroot DIR Document root for the /ext extension mechanism |
| 3194 | 3213 | ** --files GLOBLIST Comma-separated list of glob patterns for static files |
| 3195 | 3214 | ** --fossilcmd PATH The pathname of the "fossil" executable on the remote |
| 3196 | 3215 | ** system when REPOSITORY is remote. |
| 3197 | 3216 | ** --from PATH Use PATH as the diff baseline for the /ckout page |
| @@ -3239,11 +3258,11 @@ | ||
| 3239 | 3258 | ** user and group. |
| 3240 | 3259 | ** --th-trace Trace TH1 execution (for debugging purposes) |
| 3241 | 3260 | ** --usepidkey Use saved encryption key from parent process. This is |
| 3242 | 3261 | ** only necessary when using SEE on Windows or Linux. |
| 3243 | 3262 | ** |
| 3244 | -** See also: [[cgi]], [[http]], [[winsrv]] | |
| 3263 | +** See also: [[cgi]], [[http]], [[winsrv]] [Windows only] | |
| 3245 | 3264 | */ |
| 3246 | 3265 | void cmd_webserver(void){ |
| 3247 | 3266 | int iPort, mxPort; /* Range of TCP ports allowed */ |
| 3248 | 3267 | const char *zPort; /* Value of the --port option */ |
| 3249 | 3268 | const char *zBrowser; /* Name of web browser program */ |
| @@ -3266,10 +3285,11 @@ | ||
| 3266 | 3285 | int findServerArg = 2; /* argv index for find_server_repository() */ |
| 3267 | 3286 | char *zRemote = 0; /* Remote host on which to run "fossil ui" */ |
| 3268 | 3287 | const char *zJsMode; /* The --jsmode parameter */ |
| 3269 | 3288 | const char *zFossilCmd =0; /* Name of "fossil" binary on remote system */ |
| 3270 | 3289 | const char *zFrom; /* Value for --from */ |
| 3290 | + const char *zExtPage = 0; /* Argument to --extpage */ | |
| 3271 | 3291 | |
| 3272 | 3292 | |
| 3273 | 3293 | #if USE_SEE |
| 3274 | 3294 | db_setup_for_saved_encryption_key(); |
| 3275 | 3295 | #endif |
| @@ -3307,12 +3327,20 @@ | ||
| 3307 | 3327 | zFrom = find_option("from", 0, 1); |
| 3308 | 3328 | if( zFrom && zFrom==file_tail(zFrom) ){ |
| 3309 | 3329 | fossil_fatal("the argument to --from must be a pathname for" |
| 3310 | 3330 | " the \"ui\" command"); |
| 3311 | 3331 | } |
| 3312 | - zInitPage = find_option("page", "p", 1); | |
| 3313 | - if( zInitPage && zInitPage[0]=='/' ) zInitPage++; | |
| 3332 | + zExtPage = find_option("extpage",0,1); | |
| 3333 | + if( zExtPage ){ | |
| 3334 | + char *zFullPath = file_canonical_name_dup(zExtPage); | |
| 3335 | + g.zExtRoot = file_dirname(zFullPath); | |
| 3336 | + zInitPage = mprintf("ext/%s",file_tail(zFullPath)); | |
| 3337 | + fossil_free(zFullPath); | |
| 3338 | + }else{ | |
| 3339 | + zInitPage = find_option("page", "p", 1); | |
| 3340 | + if( zInitPage && zInitPage[0]=='/' ) zInitPage++; | |
| 3341 | + } | |
| 3314 | 3342 | zFossilCmd = find_option("fossilcmd", 0, 1); |
| 3315 | 3343 | if( zFrom && zInitPage==0 ){ |
| 3316 | 3344 | zInitPage = mprintf("ckout?exbase=%H", zFrom); |
| 3317 | 3345 | } |
| 3318 | 3346 | } |
| @@ -3481,11 +3509,18 @@ | ||
| 3481 | 3509 | } |
| 3482 | 3510 | blob_appendf(&ssh, " ui --nobrowser --localauth --port %d", iPort); |
| 3483 | 3511 | if( zNotFound ) blob_appendf(&ssh, " --notfound %!$", zNotFound); |
| 3484 | 3512 | if( zFileGlob ) blob_appendf(&ssh, " --files-urlenc %T", zFileGlob); |
| 3485 | 3513 | if( g.zCkoutAlias ) blob_appendf(&ssh," --ckout-alias %!$",g.zCkoutAlias); |
| 3486 | - if( g.zExtRoot ) blob_appendf(&ssh, " --extroot %$", g.zExtRoot); | |
| 3514 | + if( zExtPage ){ | |
| 3515 | + if( !file_is_absolute_path(zExtPage) ){ | |
| 3516 | + zExtPage = mprintf("%s/%s", g.argv[2], zExtPage); | |
| 3517 | + } | |
| 3518 | + blob_appendf(&ssh, " --extpage %$", zExtPage); | |
| 3519 | + }else if( g.zExtRoot ){ | |
| 3520 | + blob_appendf(&ssh, " --extroot %$", g.zExtRoot); | |
| 3521 | + } | |
| 3487 | 3522 | if( skin_in_use() ) blob_appendf(&ssh, " --skin %s", skin_in_use()); |
| 3488 | 3523 | if( zJsMode ) blob_appendf(&ssh, " --jsmode %s", zJsMode); |
| 3489 | 3524 | if( fCreate ) blob_appendf(&ssh, " --create"); |
| 3490 | 3525 | blob_appendf(&ssh, " %$", g.argv[2]); |
| 3491 | 3526 | if( isRetry ){ |
| @@ -3599,15 +3634,14 @@ | ||
| 3599 | 3634 | g.httpSSLConn = 0; |
| 3600 | 3635 | } |
| 3601 | 3636 | #endif /* FOSSIL_ENABLE_SSL */ |
| 3602 | 3637 | |
| 3603 | 3638 | #else /* WIN32 */ |
| 3604 | - find_server_repository(2, 0); | |
| 3639 | + /* Win32 implementation */ | |
| 3605 | 3640 | if( fossil_strcmp(g.zRepositoryName,"/")==0 ){ |
| 3606 | 3641 | allowRepoList = 1; |
| 3607 | 3642 | } |
| 3608 | - /* Win32 implementation */ | |
| 3609 | 3643 | if( allowRepoList ){ |
| 3610 | 3644 | flags |= HTTP_SERVER_REPOLIST; |
| 3611 | 3645 | } |
| 3612 | 3646 | if( win32_http_service(iPort, zAltBase, zNotFound, zFileGlob, flags) ){ |
| 3613 | 3647 | win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile, |
| 3614 | 3648 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -639,14 +639,15 @@ | |
| 639 | fossil_warning("%s", blob_str(&msg)); |
| 640 | blob_reset(&msg); |
| 641 | } |
| 642 | |
| 643 | /* |
| 644 | ** This function attempts to find command line options known to contain |
| 645 | ** bitwise flags and initializes the associated global variables. After |
| 646 | ** this function executes, all global variables (i.e. in the "g" struct) |
| 647 | ** containing option-settable bitwise flag fields must be initialized. |
| 648 | */ |
| 649 | static void fossil_init_flags_from_options(void){ |
| 650 | const char *zValue = find_option("comfmtflags", 0, 1); |
| 651 | if( zValue==0 ){ |
| 652 | zValue = find_option("comment-format", 0, 1); |
| @@ -725,14 +726,14 @@ | |
| 725 | fossil_limit_memory(1); |
| 726 | |
| 727 | /* When updating the minimum SQLite version, change the number here, |
| 728 | ** and also MINIMUM_SQLITE_VERSION value set in ../auto.def. Take |
| 729 | ** care that both places agree! */ |
| 730 | if( sqlite3_libversion_number()<3046000 |
| 731 | || strncmp(sqlite3_sourceid(),"2024-08-16",10)<0 |
| 732 | ){ |
| 733 | fossil_panic("Unsuitable SQLite version %s, must be at least 3.43.0", |
| 734 | sqlite3_libversion()); |
| 735 | } |
| 736 | |
| 737 | sqlite3_config(SQLITE_CONFIG_MULTITHREAD); |
| 738 | sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0); |
| @@ -997,15 +998,12 @@ | |
| 997 | |
| 998 | /* |
| 999 | ** Remove n elements from g.argv beginning with the i-th element. |
| 1000 | */ |
| 1001 | static void remove_from_argv(int i, int n){ |
| 1002 | int j; |
| 1003 | for(j=i+n; j<g.argc; i++, j++){ |
| 1004 | g.argv[i] = g.argv[j]; |
| 1005 | } |
| 1006 | g.argc = i; |
| 1007 | } |
| 1008 | |
| 1009 | |
| 1010 | /* |
| 1011 | ** Look for a command-line option. If present, remove it from the |
| @@ -1064,10 +1062,19 @@ | |
| 1064 | break; |
| 1065 | } |
| 1066 | } |
| 1067 | return zReturn; |
| 1068 | } |
| 1069 | |
| 1070 | /* Return true if zOption exists in the command-line arguments, |
| 1071 | ** but do not remove it from the list or otherwise process it. |
| 1072 | */ |
| 1073 | int has_option(const char *zOption){ |
| @@ -1848,10 +1855,15 @@ | |
| 1848 | ** not exist. |
| 1849 | */ |
| 1850 | zCleanRepo = file_cleanup_fullpath(zRepo); |
| 1851 | if( szFile==0 && sqlite3_strglob("*/.fossil",zRepo)!=0 ){ |
| 1852 | szFile = file_size(zCleanRepo, ExtFILE); |
| 1853 | if( g.fHttpTrace ){ |
| 1854 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", szFile); |
| 1855 | @ <!-- file_size(%h(zCleanRepo)) is %s(zBuf) --> |
| 1856 | fprintf(stderr, "# file_size(%s) = %s\n", zCleanRepo, zBuf); |
| 1857 | } |
| @@ -2155,11 +2167,11 @@ | |
| 2155 | } |
| 2156 | } |
| 2157 | #endif |
| 2158 | if( (pCmd->eCmdFlags & CMDFLAG_RAWCONTENT)==0 ){ |
| 2159 | cgi_decode_post_parameters(); |
| 2160 | if( !cgi_same_origin() ){ |
| 2161 | isReadonly = 1; |
| 2162 | db_protect(PROTECT_READONLY); |
| 2163 | } |
| 2164 | } |
| 2165 | if( g.fCgiTrace ){ |
| @@ -2398,11 +2410,11 @@ | |
| 2398 | ** The lines are processed in the order they are read, which is most |
| 2399 | ** significant for "errorlog:", which should be set before "repository:" |
| 2400 | ** so that any warnings from the database when opening the repository |
| 2401 | ** go to that log file. |
| 2402 | ** |
| 2403 | ** See also: [[http]], [[server]], [[winsrv]] |
| 2404 | */ |
| 2405 | void cmd_cgi(void){ |
| 2406 | const char *zNotFound = 0; |
| 2407 | char **azRedirect = 0; /* List of repositories to redirect to */ |
| 2408 | int nRedirect = 0; /* Number of entries in azRedirect */ |
| @@ -2530,12 +2542,16 @@ | |
| 2530 | ** setenv: NAME |
| 2531 | ** |
| 2532 | ** Sets environment variable NAME to VALUE. If VALUE is omitted, then |
| 2533 | ** the environment variable is unset. |
| 2534 | */ |
| 2535 | blob_token(&line,&value2); |
| 2536 | fossil_setenv(blob_str(&value), blob_str(&value2)); |
| 2537 | blob_reset(&value); |
| 2538 | blob_reset(&value2); |
| 2539 | continue; |
| 2540 | } |
| 2541 | if( blob_eq(&key, "errorlog:") && blob_token(&line, &value) ){ |
| @@ -2857,11 +2873,11 @@ | |
| 2857 | ** to force use of the current local skin config. |
| 2858 | ** --th-trace Trace TH1 execution (for debugging purposes) |
| 2859 | ** --usepidkey Use saved encryption key from parent process. This is |
| 2860 | ** only necessary when using SEE on Windows or Linux. |
| 2861 | ** |
| 2862 | ** See also: [[cgi]], [[server]], [[winsrv]] |
| 2863 | */ |
| 2864 | void cmd_http(void){ |
| 2865 | const char *zIpAddr = 0; |
| 2866 | const char *zNotFound; |
| 2867 | const char *zHost; |
| @@ -3188,10 +3204,13 @@ | |
| 3188 | ** --chroot DIR Use directory for chroot instead of repository path |
| 3189 | ** --ckout-alias NAME Treat URIs of the form /doc/NAME/... as if they were |
| 3190 | ** /doc/ckout/... |
| 3191 | ** --create Create a new REPOSITORY if it does not already exist |
| 3192 | ** --errorlog FILE Append HTTP error messages to FILE |
| 3193 | ** --extroot DIR Document root for the /ext extension mechanism |
| 3194 | ** --files GLOBLIST Comma-separated list of glob patterns for static files |
| 3195 | ** --fossilcmd PATH The pathname of the "fossil" executable on the remote |
| 3196 | ** system when REPOSITORY is remote. |
| 3197 | ** --from PATH Use PATH as the diff baseline for the /ckout page |
| @@ -3239,11 +3258,11 @@ | |
| 3239 | ** user and group. |
| 3240 | ** --th-trace Trace TH1 execution (for debugging purposes) |
| 3241 | ** --usepidkey Use saved encryption key from parent process. This is |
| 3242 | ** only necessary when using SEE on Windows or Linux. |
| 3243 | ** |
| 3244 | ** See also: [[cgi]], [[http]], [[winsrv]] |
| 3245 | */ |
| 3246 | void cmd_webserver(void){ |
| 3247 | int iPort, mxPort; /* Range of TCP ports allowed */ |
| 3248 | const char *zPort; /* Value of the --port option */ |
| 3249 | const char *zBrowser; /* Name of web browser program */ |
| @@ -3266,10 +3285,11 @@ | |
| 3266 | int findServerArg = 2; /* argv index for find_server_repository() */ |
| 3267 | char *zRemote = 0; /* Remote host on which to run "fossil ui" */ |
| 3268 | const char *zJsMode; /* The --jsmode parameter */ |
| 3269 | const char *zFossilCmd =0; /* Name of "fossil" binary on remote system */ |
| 3270 | const char *zFrom; /* Value for --from */ |
| 3271 | |
| 3272 | |
| 3273 | #if USE_SEE |
| 3274 | db_setup_for_saved_encryption_key(); |
| 3275 | #endif |
| @@ -3307,12 +3327,20 @@ | |
| 3307 | zFrom = find_option("from", 0, 1); |
| 3308 | if( zFrom && zFrom==file_tail(zFrom) ){ |
| 3309 | fossil_fatal("the argument to --from must be a pathname for" |
| 3310 | " the \"ui\" command"); |
| 3311 | } |
| 3312 | zInitPage = find_option("page", "p", 1); |
| 3313 | if( zInitPage && zInitPage[0]=='/' ) zInitPage++; |
| 3314 | zFossilCmd = find_option("fossilcmd", 0, 1); |
| 3315 | if( zFrom && zInitPage==0 ){ |
| 3316 | zInitPage = mprintf("ckout?exbase=%H", zFrom); |
| 3317 | } |
| 3318 | } |
| @@ -3481,11 +3509,18 @@ | |
| 3481 | } |
| 3482 | blob_appendf(&ssh, " ui --nobrowser --localauth --port %d", iPort); |
| 3483 | if( zNotFound ) blob_appendf(&ssh, " --notfound %!$", zNotFound); |
| 3484 | if( zFileGlob ) blob_appendf(&ssh, " --files-urlenc %T", zFileGlob); |
| 3485 | if( g.zCkoutAlias ) blob_appendf(&ssh," --ckout-alias %!$",g.zCkoutAlias); |
| 3486 | if( g.zExtRoot ) blob_appendf(&ssh, " --extroot %$", g.zExtRoot); |
| 3487 | if( skin_in_use() ) blob_appendf(&ssh, " --skin %s", skin_in_use()); |
| 3488 | if( zJsMode ) blob_appendf(&ssh, " --jsmode %s", zJsMode); |
| 3489 | if( fCreate ) blob_appendf(&ssh, " --create"); |
| 3490 | blob_appendf(&ssh, " %$", g.argv[2]); |
| 3491 | if( isRetry ){ |
| @@ -3599,15 +3634,14 @@ | |
| 3599 | g.httpSSLConn = 0; |
| 3600 | } |
| 3601 | #endif /* FOSSIL_ENABLE_SSL */ |
| 3602 | |
| 3603 | #else /* WIN32 */ |
| 3604 | find_server_repository(2, 0); |
| 3605 | if( fossil_strcmp(g.zRepositoryName,"/")==0 ){ |
| 3606 | allowRepoList = 1; |
| 3607 | } |
| 3608 | /* Win32 implementation */ |
| 3609 | if( allowRepoList ){ |
| 3610 | flags |= HTTP_SERVER_REPOLIST; |
| 3611 | } |
| 3612 | if( win32_http_service(iPort, zAltBase, zNotFound, zFileGlob, flags) ){ |
| 3613 | win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile, |
| 3614 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -639,14 +639,15 @@ | |
| 639 | fossil_warning("%s", blob_str(&msg)); |
| 640 | blob_reset(&msg); |
| 641 | } |
| 642 | |
| 643 | /* |
| 644 | ** Initialize the g.comFmtFlags global variable. |
| 645 | ** |
| 646 | ** Global command-line options --comfmtflags or --comment-format can be |
| 647 | ** used for this. However, those command-line options are undocumented |
| 648 | ** and deprecated. They are here for backwards compatibility only. |
| 649 | */ |
| 650 | static void fossil_init_flags_from_options(void){ |
| 651 | const char *zValue = find_option("comfmtflags", 0, 1); |
| 652 | if( zValue==0 ){ |
| 653 | zValue = find_option("comment-format", 0, 1); |
| @@ -725,14 +726,14 @@ | |
| 726 | fossil_limit_memory(1); |
| 727 | |
| 728 | /* When updating the minimum SQLite version, change the number here, |
| 729 | ** and also MINIMUM_SQLITE_VERSION value set in ../auto.def. Take |
| 730 | ** care that both places agree! */ |
| 731 | if( sqlite3_libversion_number()<3049000 |
| 732 | || strncmp(sqlite3_sourceid(),"2025-02-06",10)<0 |
| 733 | ){ |
| 734 | fossil_panic("Unsuitable SQLite version %s, must be at least 3.49.0", |
| 735 | sqlite3_libversion()); |
| 736 | } |
| 737 | |
| 738 | sqlite3_config(SQLITE_CONFIG_MULTITHREAD); |
| 739 | sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0); |
| @@ -997,15 +998,12 @@ | |
| 998 | |
| 999 | /* |
| 1000 | ** Remove n elements from g.argv beginning with the i-th element. |
| 1001 | */ |
| 1002 | static void remove_from_argv(int i, int n){ |
| 1003 | memmove(&g.argv[i], &g.argv[i+n], sizeof(g.argv[i])*(g.argc-i-n)); |
| 1004 | g.argc -= n; |
| 1005 | } |
| 1006 | |
| 1007 | |
| 1008 | /* |
| 1009 | ** Look for a command-line option. If present, remove it from the |
| @@ -1064,10 +1062,19 @@ | |
| 1062 | break; |
| 1063 | } |
| 1064 | } |
| 1065 | return zReturn; |
| 1066 | } |
| 1067 | |
| 1068 | /* |
| 1069 | ** Restore an option previously removed by find_option(). |
| 1070 | */ |
| 1071 | void restore_option(const char *zName, const char *zValue, int hasOpt){ |
| 1072 | if( zValue==0 && hasOpt ) return; |
| 1073 | g.argv[g.argc++] = (char*)zName; |
| 1074 | if( hasOpt ) g.argv[g.argc++] = (char*)zValue; |
| 1075 | } |
| 1076 | |
| 1077 | /* Return true if zOption exists in the command-line arguments, |
| 1078 | ** but do not remove it from the list or otherwise process it. |
| 1079 | */ |
| 1080 | int has_option(const char *zOption){ |
| @@ -1848,10 +1855,15 @@ | |
| 1855 | ** not exist. |
| 1856 | */ |
| 1857 | zCleanRepo = file_cleanup_fullpath(zRepo); |
| 1858 | if( szFile==0 && sqlite3_strglob("*/.fossil",zRepo)!=0 ){ |
| 1859 | szFile = file_size(zCleanRepo, ExtFILE); |
| 1860 | if( szFile>0 && !file_isfile(zCleanRepo, ExtFILE) ){ |
| 1861 | /* Only let szFile be non-negative if zCleanRepo really is a file |
| 1862 | ** and not a directory or some other filesystem object. */ |
| 1863 | szFile = -1; |
| 1864 | } |
| 1865 | if( g.fHttpTrace ){ |
| 1866 | sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", szFile); |
| 1867 | @ <!-- file_size(%h(zCleanRepo)) is %s(zBuf) --> |
| 1868 | fprintf(stderr, "# file_size(%s) = %s\n", zCleanRepo, zBuf); |
| 1869 | } |
| @@ -2155,11 +2167,11 @@ | |
| 2167 | } |
| 2168 | } |
| 2169 | #endif |
| 2170 | if( (pCmd->eCmdFlags & CMDFLAG_RAWCONTENT)==0 ){ |
| 2171 | cgi_decode_post_parameters(); |
| 2172 | if( !cgi_same_origin(0) ){ |
| 2173 | isReadonly = 1; |
| 2174 | db_protect(PROTECT_READONLY); |
| 2175 | } |
| 2176 | } |
| 2177 | if( g.fCgiTrace ){ |
| @@ -2398,11 +2410,11 @@ | |
| 2410 | ** The lines are processed in the order they are read, which is most |
| 2411 | ** significant for "errorlog:", which should be set before "repository:" |
| 2412 | ** so that any warnings from the database when opening the repository |
| 2413 | ** go to that log file. |
| 2414 | ** |
| 2415 | ** See also: [[http]], [[server]], [[winsrv]] [Windows only] |
| 2416 | */ |
| 2417 | void cmd_cgi(void){ |
| 2418 | const char *zNotFound = 0; |
| 2419 | char **azRedirect = 0; /* List of repositories to redirect to */ |
| 2420 | int nRedirect = 0; /* Number of entries in azRedirect */ |
| @@ -2530,12 +2542,16 @@ | |
| 2542 | ** setenv: NAME |
| 2543 | ** |
| 2544 | ** Sets environment variable NAME to VALUE. If VALUE is omitted, then |
| 2545 | ** the environment variable is unset. |
| 2546 | */ |
| 2547 | char *zValue; |
| 2548 | blob_tail(&line,&value2); |
| 2549 | blob_trim(&value2); |
| 2550 | zValue = blob_str(&value2); |
| 2551 | while( fossil_isspace(zValue[0]) ){ zValue++; } |
| 2552 | fossil_setenv(blob_str(&value), zValue); |
| 2553 | blob_reset(&value); |
| 2554 | blob_reset(&value2); |
| 2555 | continue; |
| 2556 | } |
| 2557 | if( blob_eq(&key, "errorlog:") && blob_token(&line, &value) ){ |
| @@ -2857,11 +2873,11 @@ | |
| 2873 | ** to force use of the current local skin config. |
| 2874 | ** --th-trace Trace TH1 execution (for debugging purposes) |
| 2875 | ** --usepidkey Use saved encryption key from parent process. This is |
| 2876 | ** only necessary when using SEE on Windows or Linux. |
| 2877 | ** |
| 2878 | ** See also: [[cgi]], [[server]], [[winsrv]] [Windows only] |
| 2879 | */ |
| 2880 | void cmd_http(void){ |
| 2881 | const char *zIpAddr = 0; |
| 2882 | const char *zNotFound; |
| 2883 | const char *zHost; |
| @@ -3188,10 +3204,13 @@ | |
| 3204 | ** --chroot DIR Use directory for chroot instead of repository path |
| 3205 | ** --ckout-alias NAME Treat URIs of the form /doc/NAME/... as if they were |
| 3206 | ** /doc/ckout/... |
| 3207 | ** --create Create a new REPOSITORY if it does not already exist |
| 3208 | ** --errorlog FILE Append HTTP error messages to FILE |
| 3209 | ** --extpage FILE Shortcut for "--extroot DIR --page ext/TAIL" where |
| 3210 | ** DIR is the directory holding FILE and TAIL is the |
| 3211 | ** filename at the end of FILE. Only works for "ui". |
| 3212 | ** --extroot DIR Document root for the /ext extension mechanism |
| 3213 | ** --files GLOBLIST Comma-separated list of glob patterns for static files |
| 3214 | ** --fossilcmd PATH The pathname of the "fossil" executable on the remote |
| 3215 | ** system when REPOSITORY is remote. |
| 3216 | ** --from PATH Use PATH as the diff baseline for the /ckout page |
| @@ -3239,11 +3258,11 @@ | |
| 3258 | ** user and group. |
| 3259 | ** --th-trace Trace TH1 execution (for debugging purposes) |
| 3260 | ** --usepidkey Use saved encryption key from parent process. This is |
| 3261 | ** only necessary when using SEE on Windows or Linux. |
| 3262 | ** |
| 3263 | ** See also: [[cgi]], [[http]], [[winsrv]] [Windows only] |
| 3264 | */ |
| 3265 | void cmd_webserver(void){ |
| 3266 | int iPort, mxPort; /* Range of TCP ports allowed */ |
| 3267 | const char *zPort; /* Value of the --port option */ |
| 3268 | const char *zBrowser; /* Name of web browser program */ |
| @@ -3266,10 +3285,11 @@ | |
| 3285 | int findServerArg = 2; /* argv index for find_server_repository() */ |
| 3286 | char *zRemote = 0; /* Remote host on which to run "fossil ui" */ |
| 3287 | const char *zJsMode; /* The --jsmode parameter */ |
| 3288 | const char *zFossilCmd =0; /* Name of "fossil" binary on remote system */ |
| 3289 | const char *zFrom; /* Value for --from */ |
| 3290 | const char *zExtPage = 0; /* Argument to --extpage */ |
| 3291 | |
| 3292 | |
| 3293 | #if USE_SEE |
| 3294 | db_setup_for_saved_encryption_key(); |
| 3295 | #endif |
| @@ -3307,12 +3327,20 @@ | |
| 3327 | zFrom = find_option("from", 0, 1); |
| 3328 | if( zFrom && zFrom==file_tail(zFrom) ){ |
| 3329 | fossil_fatal("the argument to --from must be a pathname for" |
| 3330 | " the \"ui\" command"); |
| 3331 | } |
| 3332 | zExtPage = find_option("extpage",0,1); |
| 3333 | if( zExtPage ){ |
| 3334 | char *zFullPath = file_canonical_name_dup(zExtPage); |
| 3335 | g.zExtRoot = file_dirname(zFullPath); |
| 3336 | zInitPage = mprintf("ext/%s",file_tail(zFullPath)); |
| 3337 | fossil_free(zFullPath); |
| 3338 | }else{ |
| 3339 | zInitPage = find_option("page", "p", 1); |
| 3340 | if( zInitPage && zInitPage[0]=='/' ) zInitPage++; |
| 3341 | } |
| 3342 | zFossilCmd = find_option("fossilcmd", 0, 1); |
| 3343 | if( zFrom && zInitPage==0 ){ |
| 3344 | zInitPage = mprintf("ckout?exbase=%H", zFrom); |
| 3345 | } |
| 3346 | } |
| @@ -3481,11 +3509,18 @@ | |
| 3509 | } |
| 3510 | blob_appendf(&ssh, " ui --nobrowser --localauth --port %d", iPort); |
| 3511 | if( zNotFound ) blob_appendf(&ssh, " --notfound %!$", zNotFound); |
| 3512 | if( zFileGlob ) blob_appendf(&ssh, " --files-urlenc %T", zFileGlob); |
| 3513 | if( g.zCkoutAlias ) blob_appendf(&ssh," --ckout-alias %!$",g.zCkoutAlias); |
| 3514 | if( zExtPage ){ |
| 3515 | if( !file_is_absolute_path(zExtPage) ){ |
| 3516 | zExtPage = mprintf("%s/%s", g.argv[2], zExtPage); |
| 3517 | } |
| 3518 | blob_appendf(&ssh, " --extpage %$", zExtPage); |
| 3519 | }else if( g.zExtRoot ){ |
| 3520 | blob_appendf(&ssh, " --extroot %$", g.zExtRoot); |
| 3521 | } |
| 3522 | if( skin_in_use() ) blob_appendf(&ssh, " --skin %s", skin_in_use()); |
| 3523 | if( zJsMode ) blob_appendf(&ssh, " --jsmode %s", zJsMode); |
| 3524 | if( fCreate ) blob_appendf(&ssh, " --create"); |
| 3525 | blob_appendf(&ssh, " %$", g.argv[2]); |
| 3526 | if( isRetry ){ |
| @@ -3599,15 +3634,14 @@ | |
| 3634 | g.httpSSLConn = 0; |
| 3635 | } |
| 3636 | #endif /* FOSSIL_ENABLE_SSL */ |
| 3637 | |
| 3638 | #else /* WIN32 */ |
| 3639 | /* Win32 implementation */ |
| 3640 | if( fossil_strcmp(g.zRepositoryName,"/")==0 ){ |
| 3641 | allowRepoList = 1; |
| 3642 | } |
| 3643 | if( allowRepoList ){ |
| 3644 | flags |= HTTP_SERVER_REPOLIST; |
| 3645 | } |
| 3646 | if( win32_http_service(iPort, zAltBase, zNotFound, zFileGlob, flags) ){ |
| 3647 | win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile, |
| 3648 |
+4
-3
| --- src/main.mk | ||
| +++ src/main.mk | ||
| @@ -2134,18 +2134,19 @@ | ||
| 2134 | 2134 | $(XTCC) $(PIKCHR_OPTIONS) -c $(SRCDIR_extsrc)/pikchr.c -o $@ |
| 2135 | 2135 | |
| 2136 | 2136 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c |
| 2137 | 2137 | $(XTCC) -c $(SRCDIR_extsrc)/cson_amalgamation.c -o $@ |
| 2138 | 2138 | |
| 2139 | -$(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c | |
| 2139 | +$(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c $(MAKEFILE_LIST) | |
| 2140 | 2140 | $(EMCC_WRAPPER) -o $@ $(EMCC_OPT) --no-entry \ |
| 2141 | - -sEXPORTED_RUNTIME_METHODS=cwrap,setValue,getValue,stackSave,stackRestore \ | |
| 2142 | - -sEXPORTED_FUNCTIONS=_pikchr $(SRCDIR_extsrc)/pikchr.c \ | |
| 2141 | + -sEXPORTED_RUNTIME_METHODS=cwrap,ccall,setValue,getValue,stackSave,stackAlloc,stackRestore \ | |
| 2142 | + -sEXPORTED_FUNCTIONS=_pikchr,_pikchr_version $(SRCDIR_extsrc)/pikchr.c \ | |
| 2143 | 2143 | -sENVIRONMENT=web \ |
| 2144 | 2144 | -sMODULARIZE \ |
| 2145 | 2145 | -sEXPORT_NAME=initPikchrModule \ |
| 2146 | 2146 | --minify 0 |
| 2147 | + $(TCLSH) $(TOPDIR)/tools/randomize-js-names.tcl $(SRCDIR_extsrc) | |
| 2147 | 2148 | @chmod -x $(SRCDIR_extsrc)/pikchr.wasm |
| 2148 | 2149 | wasm: $(SRCDIR_extsrc)/pikchr.js |
| 2149 | 2150 | |
| 2150 | 2151 | # |
| 2151 | 2152 | # compile_commands.json support... |
| 2152 | 2153 |
| --- src/main.mk | |
| +++ src/main.mk | |
| @@ -2134,18 +2134,19 @@ | |
| 2134 | $(XTCC) $(PIKCHR_OPTIONS) -c $(SRCDIR_extsrc)/pikchr.c -o $@ |
| 2135 | |
| 2136 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c |
| 2137 | $(XTCC) -c $(SRCDIR_extsrc)/cson_amalgamation.c -o $@ |
| 2138 | |
| 2139 | $(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c |
| 2140 | $(EMCC_WRAPPER) -o $@ $(EMCC_OPT) --no-entry \ |
| 2141 | -sEXPORTED_RUNTIME_METHODS=cwrap,setValue,getValue,stackSave,stackRestore \ |
| 2142 | -sEXPORTED_FUNCTIONS=_pikchr $(SRCDIR_extsrc)/pikchr.c \ |
| 2143 | -sENVIRONMENT=web \ |
| 2144 | -sMODULARIZE \ |
| 2145 | -sEXPORT_NAME=initPikchrModule \ |
| 2146 | --minify 0 |
| 2147 | @chmod -x $(SRCDIR_extsrc)/pikchr.wasm |
| 2148 | wasm: $(SRCDIR_extsrc)/pikchr.js |
| 2149 | |
| 2150 | # |
| 2151 | # compile_commands.json support... |
| 2152 |
| --- src/main.mk | |
| +++ src/main.mk | |
| @@ -2134,18 +2134,19 @@ | |
| 2134 | $(XTCC) $(PIKCHR_OPTIONS) -c $(SRCDIR_extsrc)/pikchr.c -o $@ |
| 2135 | |
| 2136 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c |
| 2137 | $(XTCC) -c $(SRCDIR_extsrc)/cson_amalgamation.c -o $@ |
| 2138 | |
| 2139 | $(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c $(MAKEFILE_LIST) |
| 2140 | $(EMCC_WRAPPER) -o $@ $(EMCC_OPT) --no-entry \ |
| 2141 | -sEXPORTED_RUNTIME_METHODS=cwrap,ccall,setValue,getValue,stackSave,stackAlloc,stackRestore \ |
| 2142 | -sEXPORTED_FUNCTIONS=_pikchr,_pikchr_version $(SRCDIR_extsrc)/pikchr.c \ |
| 2143 | -sENVIRONMENT=web \ |
| 2144 | -sMODULARIZE \ |
| 2145 | -sEXPORT_NAME=initPikchrModule \ |
| 2146 | --minify 0 |
| 2147 | $(TCLSH) $(TOPDIR)/tools/randomize-js-names.tcl $(SRCDIR_extsrc) |
| 2148 | @chmod -x $(SRCDIR_extsrc)/pikchr.wasm |
| 2149 | wasm: $(SRCDIR_extsrc)/pikchr.js |
| 2150 | |
| 2151 | # |
| 2152 | # compile_commands.json support... |
| 2153 |
+295
| --- src/manifest.c | ||
| +++ src/manifest.c | ||
| @@ -2911,5 +2911,300 @@ | ||
| 2911 | 2911 | if( g.argc!=3 ) usage("RECORDID"); |
| 2912 | 2912 | rid = name_to_rid(g.argv[2]); |
| 2913 | 2913 | content_get(rid, &content); |
| 2914 | 2914 | manifest_crosslink(rid, &content, MC_NONE); |
| 2915 | 2915 | } |
| 2916 | + | |
| 2917 | +/* | |
| 2918 | +** For a given CATYPE_... value, returns a human-friendly name, or | |
| 2919 | +** NULL if typeId is unknown or is CFTYPE_ANY. The names returned by | |
| 2920 | +** this function are geared towards use with artifact_to_json(), and | |
| 2921 | +** may differ from some historical uses. e.g. CFTYPE_CONTROL artifacts | |
| 2922 | +** are called "tag" artifacts by this function. | |
| 2923 | +*/ | |
| 2924 | +const char * artifact_type_to_name(int typeId){ | |
| 2925 | + switch(typeId){ | |
| 2926 | + case CFTYPE_MANIFEST: return "checkin"; | |
| 2927 | + case CFTYPE_CLUSTER: return "cluster"; | |
| 2928 | + case CFTYPE_CONTROL: return "tag"; | |
| 2929 | + case CFTYPE_WIKI: return "wiki"; | |
| 2930 | + case CFTYPE_TICKET: return "ticket"; | |
| 2931 | + case CFTYPE_ATTACHMENT: return "attachment"; | |
| 2932 | + case CFTYPE_EVENT: return "technote"; | |
| 2933 | + case CFTYPE_FORUM: return "forumpost"; | |
| 2934 | + } | |
| 2935 | + return NULL; | |
| 2936 | +} | |
| 2937 | + | |
| 2938 | +/* | |
| 2939 | +** Creates a JSON representation of p, appending it to b. | |
| 2940 | +** | |
| 2941 | +** b is not cleared before rendering, so the caller needs to do that | |
| 2942 | +** if it's important for their use case. | |
| 2943 | +** | |
| 2944 | +** Pedantic note: this routine traverses p->aFile directly, rather | |
| 2945 | +** than using manifest_file_next(), so that delta manifests are | |
| 2946 | +** rendered as-is instead of containing their derived F-cards. If that | |
| 2947 | +** policy is ever changed, p will need to be non-const. | |
| 2948 | +*/ | |
| 2949 | +void artifact_to_json(Manifest const *p, Blob *b){ | |
| 2950 | + int i; | |
| 2951 | + | |
| 2952 | + blob_append_literal(b, "{"); | |
| 2953 | + blob_appendf(b, "\"uuid\":\"%z\"", rid_to_uuid(p->rid)); | |
| 2954 | + /*blob_appendf(b, ", \"rid\": %d", p->rid); not portable across repos*/ | |
| 2955 | + blob_appendf(b, ",\"type\":%!j", artifact_type_to_name(p->type)); | |
| 2956 | +#define ISA(TYPE) if( p->type==TYPE ) | |
| 2957 | +#define CARD_LETTER(LETTER) \ | |
| 2958 | + blob_append_literal(b, ",\"" #LETTER "\":") | |
| 2959 | +#define CARD_STR(LETTER, VAL) \ | |
| 2960 | + assert( VAL ); CARD_LETTER(LETTER); blob_appendf(b, "%!j", VAL) | |
| 2961 | +#define CARD_STR2(LETTER, VAL) \ | |
| 2962 | + if( VAL ) { CARD_STR(LETTER, VAL); } (void)0 | |
| 2963 | +#define STR_OR_NULL(VAL) \ | |
| 2964 | + if( VAL ) blob_appendf(b, "%!j", VAL); \ | |
| 2965 | + else blob_append(b, "null", 4) | |
| 2966 | +#define KVP_STR(ADDCOMMA, KEY,VAL) \ | |
| 2967 | + if(ADDCOMMA) blob_append_char(b, ','); \ | |
| 2968 | + blob_appendf(b, "%!j:", #KEY); \ | |
| 2969 | + STR_OR_NULL(VAL) | |
| 2970 | + | |
| 2971 | + ISA( CFTYPE_ATTACHMENT ){ | |
| 2972 | + CARD_LETTER(A); | |
| 2973 | + blob_append_char(b, '{'); | |
| 2974 | + KVP_STR(0, filename, p->zAttachName); | |
| 2975 | + KVP_STR(1, target, p->zAttachTarget); | |
| 2976 | + KVP_STR(1, source, p->zAttachSrc); | |
| 2977 | + blob_append_char(b, '}'); | |
| 2978 | + } | |
| 2979 | + CARD_STR2(B, p->zBaseline); | |
| 2980 | + CARD_STR2(C, p->zComment); | |
| 2981 | + CARD_LETTER(D); blob_appendf(b, "%f", p->rDate); | |
| 2982 | + ISA( CFTYPE_EVENT ){ | |
| 2983 | + blob_appendf(b, ", \"E\":{\"time\":%f,\"id\":%!j}", | |
| 2984 | + p->rEventDate, p->zEventId); | |
| 2985 | + } | |
| 2986 | + ISA( CFTYPE_MANIFEST ){ | |
| 2987 | + CARD_LETTER(F); | |
| 2988 | + blob_append_char(b, '['); | |
| 2989 | + for( i = 0; i < p->nFile; ++i ){ | |
| 2990 | + ManifestFile const * const pF = &p->aFile[i]; | |
| 2991 | + if( i>0 ) blob_append_char(b, ','); | |
| 2992 | + blob_append_char(b, '{'); | |
| 2993 | + KVP_STR(0, name, pF->zName); | |
| 2994 | + KVP_STR(1, uuid, pF->zUuid); | |
| 2995 | + KVP_STR(1, perm, pF->zPerm); | |
| 2996 | + KVP_STR(1, rename, pF->zPrior); | |
| 2997 | + blob_append_char(b, '}'); | |
| 2998 | + } | |
| 2999 | + /* Special case: model checkins with no F-card as having an empty | |
| 3000 | + ** array, rather than no F-cards, to hypothetically simplify | |
| 3001 | + ** handling in JSON queries. */ | |
| 3002 | + blob_append_char(b, ']'); | |
| 3003 | + } | |
| 3004 | + CARD_STR2(G, p->zThreadRoot); | |
| 3005 | + ISA( CFTYPE_FORUM ){ | |
| 3006 | + CARD_LETTER(H); | |
| 3007 | + STR_OR_NULL( (p->zThreadTitle && *p->zThreadTitle) ? p->zThreadTitle : NULL); | |
| 3008 | + CARD_STR2(I, p->zInReplyTo); | |
| 3009 | + } | |
| 3010 | + if( p->nField ){ | |
| 3011 | + CARD_LETTER(J); | |
| 3012 | + blob_append_char(b, '['); | |
| 3013 | + for( i = 0; i < p->nField; ++i ){ | |
| 3014 | + const char * zName = p->aField[i].zName; | |
| 3015 | + if( i>0 ) blob_append_char(b, ','); | |
| 3016 | + blob_append_char(b, '{'); | |
| 3017 | + KVP_STR(0, name, '+'==*zName ? &zName[1] : zName); | |
| 3018 | + KVP_STR(1, value, p->aField[i].zValue); | |
| 3019 | + blob_appendf(b, ",\"append\":%s", '+'==*zName ? "true" : "false"); | |
| 3020 | + blob_append_char(b, '}'); | |
| 3021 | + } | |
| 3022 | + blob_append_char(b, ']'); | |
| 3023 | + } | |
| 3024 | + CARD_STR2(K, p->zTicketUuid); | |
| 3025 | + CARD_STR2(L, p->zWikiTitle); | |
| 3026 | + ISA( CFTYPE_CLUSTER ){ | |
| 3027 | + CARD_LETTER(M); | |
| 3028 | + blob_append_char(b, '['); | |
| 3029 | + for( int i = 0; i < p->nCChild; ++i ){ | |
| 3030 | + if( i>0 ) blob_append_char(b, ','); | |
| 3031 | + blob_appendf(b, "%!j", p->azCChild[i]); | |
| 3032 | + } | |
| 3033 | + blob_append_char(b, ']'); | |
| 3034 | + } | |
| 3035 | + CARD_STR2(N, p->zMimetype); | |
| 3036 | + ISA( CFTYPE_MANIFEST || p->nParent>0 ){ | |
| 3037 | + CARD_LETTER(P); | |
| 3038 | + blob_append_char(b, '['); | |
| 3039 | + for( i = 0; i < p->nParent; ++i ){ | |
| 3040 | + if( i>0 ) blob_append_char(b, ','); | |
| 3041 | + blob_appendf(b, "%!j", p->azParent[i]); | |
| 3042 | + } | |
| 3043 | + /* Special case: model checkins with no P-card as having an empty | |
| 3044 | + ** array, as per F-cards. */ | |
| 3045 | + blob_append_char(b, ']'); | |
| 3046 | + } | |
| 3047 | + if( p->nCherrypick ){ | |
| 3048 | + CARD_LETTER(Q); | |
| 3049 | + blob_append_char(b, '['); | |
| 3050 | + for( i = 0; i < p->nCherrypick; ++i ){ | |
| 3051 | + if( i>0 ) blob_append_char(b, ','); | |
| 3052 | + blob_append_char(b, '{'); | |
| 3053 | + blob_appendf(b, "\"type\":\"%c\"", p->aCherrypick[i].zCPTarget[0]); | |
| 3054 | + KVP_STR(1, target, &p->aCherrypick[i].zCPTarget[1]); | |
| 3055 | + KVP_STR(1, base, p->aCherrypick[i].zCPBase); | |
| 3056 | + blob_append_char(b, '}'); | |
| 3057 | + } | |
| 3058 | + blob_append_char(b, ']'); | |
| 3059 | + } | |
| 3060 | + CARD_STR2(R, p->zRepoCksum); | |
| 3061 | + if( p->nTag ){ | |
| 3062 | + CARD_LETTER(T); | |
| 3063 | + blob_append_char(b, '['); | |
| 3064 | + for( int i = 0; i < p->nTag; ++i ){ | |
| 3065 | + const char *zName = p->aTag[i].zName; | |
| 3066 | + if( i>0 ) blob_append_char(b, ','); | |
| 3067 | + blob_append_char(b, '{'); | |
| 3068 | + blob_appendf(b, "\"type\":\"%c\"", *zName); | |
| 3069 | + KVP_STR(1, name, &zName[1]); | |
| 3070 | + KVP_STR(1, target, p->aTag[i].zUuid ? p->aTag[i].zUuid : "*") | |
| 3071 | + /* We could arguably resolve the "*" as null or p's uuid. */; | |
| 3072 | + KVP_STR(1, value, p->aTag[i].zValue); | |
| 3073 | + blob_append_char(b, '}'); | |
| 3074 | + } | |
| 3075 | + blob_append_char(b, ']'); | |
| 3076 | + } | |
| 3077 | + CARD_STR2(U, p->zUser); | |
| 3078 | + if( p->zWiki || CFTYPE_WIKI==p->type || CFTYPE_FORUM==p->type | |
| 3079 | + || CFTYPE_EVENT==p->type ){ | |
| 3080 | + CARD_LETTER(W); | |
| 3081 | + STR_OR_NULL((p->zWiki && *p->zWiki) ? p->zWiki : NULL); | |
| 3082 | + } | |
| 3083 | + blob_append_literal(b, "}"); | |
| 3084 | +#undef CARD_FMT | |
| 3085 | +#undef CARD_LETTER | |
| 3086 | +#undef CARD_STR | |
| 3087 | +#undef CARD_STR2 | |
| 3088 | +#undef ISA | |
| 3089 | +#undef KVP_STR | |
| 3090 | +#undef STR_OR_NULL | |
| 3091 | +} | |
| 3092 | + | |
| 3093 | +/* | |
| 3094 | +** Convenience wrapper around artifact_to_json() which expects rid to | |
| 3095 | +** be the blob.rid of any artifact type. If it can load a Manifest | |
| 3096 | +** with that rid, it returns rid, else it returns 0. | |
| 3097 | +*/ | |
| 3098 | +int artifact_to_json_by_rid(int rid, Blob *pOut){ | |
| 3099 | + Manifest * const p = manifest_get(rid, CFTYPE_ANY, 0); | |
| 3100 | + if( p ){ | |
| 3101 | + artifact_to_json(p, pOut); | |
| 3102 | + manifest_destroy(p); | |
| 3103 | + }else{ | |
| 3104 | + rid = 0; | |
| 3105 | + } | |
| 3106 | + return rid; | |
| 3107 | +} | |
| 3108 | + | |
| 3109 | +/* | |
| 3110 | +** Convenience wrapper around artifact_to_json() which accepts any | |
| 3111 | +** artifact name which is legal for symbolic_name_to_rid(). On success | |
| 3112 | +** it returns the rid of the artifact. Returns 0 if no such artifact | |
| 3113 | +** exists and a negative value if the name is ambiguous. | |
| 3114 | +** | |
| 3115 | +** pOut is not cleared before rendering, so the caller needs to do | |
| 3116 | +** that if it's important for their use case. | |
| 3117 | +*/ | |
| 3118 | +int artifact_to_json_by_name(const char *zName, Blob *pOut){ | |
| 3119 | + const int rid = symbolic_name_to_rid(zName, 0); | |
| 3120 | + return rid>0 | |
| 3121 | + ? artifact_to_json_by_rid(rid, pOut) | |
| 3122 | + : rid; | |
| 3123 | +} | |
| 3124 | + | |
| 3125 | +/* | |
| 3126 | +** SQLite UDF for artifact_to_json(). Its single argument should be | |
| 3127 | +** either an INTEGER (blob.rid value) or a TEXT symbolic artifact | |
| 3128 | +** name, as per symbolic_name_to_rid(). If an artifact is found then | |
| 3129 | +** the result of the UDF is that JSON as a string, else it evaluates | |
| 3130 | +** to NULL. | |
| 3131 | +*/ | |
| 3132 | +void artifact_to_json_sql_func( | |
| 3133 | + sqlite3_context *context, | |
| 3134 | + int argc, | |
| 3135 | + sqlite3_value **argv | |
| 3136 | +){ | |
| 3137 | + int rid = 0; | |
| 3138 | + Blob b = empty_blob; | |
| 3139 | + | |
| 3140 | + if(1 != argc){ | |
| 3141 | + goto error_usage; | |
| 3142 | + } | |
| 3143 | + switch( sqlite3_value_type(argv[0]) ){ | |
| 3144 | + case SQLITE_INTEGER: | |
| 3145 | + rid = artifact_to_json_by_rid(sqlite3_value_int(argv[0]), &b); | |
| 3146 | + break; | |
| 3147 | + case SQLITE_TEXT:{ | |
| 3148 | + const char * z = (const char *)sqlite3_value_text(argv[0]); | |
| 3149 | + if( z ){ | |
| 3150 | + rid = artifact_to_json_by_name(z, &b); | |
| 3151 | + } | |
| 3152 | + break; | |
| 3153 | + } | |
| 3154 | + default: | |
| 3155 | + goto error_usage; | |
| 3156 | + } | |
| 3157 | + if( rid>0 ){ | |
| 3158 | + sqlite3_result_text(context, blob_str(&b), blob_size(&b), | |
| 3159 | + SQLITE_TRANSIENT); | |
| 3160 | + blob_reset(&b); | |
| 3161 | + }else{ | |
| 3162 | + /* We should arguably error out if rid<0 (ambiguous name) */ | |
| 3163 | + sqlite3_result_null(context); | |
| 3164 | + } | |
| 3165 | + return; | |
| 3166 | +error_usage: | |
| 3167 | + sqlite3_result_error(context, "Expecting one argument: blob.rid or " | |
| 3168 | + "artifact symbolic name", -1); | |
| 3169 | +} | |
| 3170 | + | |
| 3171 | + | |
| 3172 | + | |
| 3173 | +/* | |
| 3174 | +** COMMAND: test-artifact-to-json | |
| 3175 | +** | |
| 3176 | +** Usage: %fossil test-artifact-to-json ?-pretty|-p? symbolic-name [...names] | |
| 3177 | +** | |
| 3178 | +** Tests the artifact_to_json() and artifact_to_json_by_name() APIs. | |
| 3179 | +*/ | |
| 3180 | +void test_manifest_to_json(void){ | |
| 3181 | + int i; | |
| 3182 | + Blob b = empty_blob; | |
| 3183 | + Stmt q; | |
| 3184 | + const int bPretty = find_option("pretty","p",0)!=0; | |
| 3185 | + int nErr = 0; | |
| 3186 | + | |
| 3187 | + db_find_and_open_repository(0,0); | |
| 3188 | + db_prepare(&q, "select json_pretty(:json)"); | |
| 3189 | + for( i=2; i<g.argc; ++i ){ | |
| 3190 | + char const *zName = g.argv[i]; | |
| 3191 | + const int rc = artifact_to_json_by_name(zName, &b); | |
| 3192 | + if( rc<=0 ){ | |
| 3193 | + ++nErr; | |
| 3194 | + fossil_warning("Error reading artifact %Q", zName); | |
| 3195 | + continue; | |
| 3196 | + }else if( bPretty ){ | |
| 3197 | + db_bind_blob(&q, ":json", &b); | |
| 3198 | + b.nUsed = 0; | |
| 3199 | + db_step(&q); | |
| 3200 | + db_column_blob(&q, 0, &b); | |
| 3201 | + db_reset(&q); | |
| 3202 | + } | |
| 3203 | + fossil_print("%b\n", &b); | |
| 3204 | + blob_reset(&b); | |
| 3205 | + } | |
| 3206 | + db_finalize(&q); | |
| 3207 | + if( nErr ){ | |
| 3208 | + fossil_warning("Error count: %d", nErr); | |
| 3209 | + } | |
| 3210 | +} | |
| 2916 | 3211 |
| --- src/manifest.c | |
| +++ src/manifest.c | |
| @@ -2911,5 +2911,300 @@ | |
| 2911 | if( g.argc!=3 ) usage("RECORDID"); |
| 2912 | rid = name_to_rid(g.argv[2]); |
| 2913 | content_get(rid, &content); |
| 2914 | manifest_crosslink(rid, &content, MC_NONE); |
| 2915 | } |
| 2916 |
| --- src/manifest.c | |
| +++ src/manifest.c | |
| @@ -2911,5 +2911,300 @@ | |
| 2911 | if( g.argc!=3 ) usage("RECORDID"); |
| 2912 | rid = name_to_rid(g.argv[2]); |
| 2913 | content_get(rid, &content); |
| 2914 | manifest_crosslink(rid, &content, MC_NONE); |
| 2915 | } |
| 2916 | |
| 2917 | /* |
| 2918 | ** For a given CATYPE_... value, returns a human-friendly name, or |
| 2919 | ** NULL if typeId is unknown or is CFTYPE_ANY. The names returned by |
| 2920 | ** this function are geared towards use with artifact_to_json(), and |
| 2921 | ** may differ from some historical uses. e.g. CFTYPE_CONTROL artifacts |
| 2922 | ** are called "tag" artifacts by this function. |
| 2923 | */ |
| 2924 | const char * artifact_type_to_name(int typeId){ |
| 2925 | switch(typeId){ |
| 2926 | case CFTYPE_MANIFEST: return "checkin"; |
| 2927 | case CFTYPE_CLUSTER: return "cluster"; |
| 2928 | case CFTYPE_CONTROL: return "tag"; |
| 2929 | case CFTYPE_WIKI: return "wiki"; |
| 2930 | case CFTYPE_TICKET: return "ticket"; |
| 2931 | case CFTYPE_ATTACHMENT: return "attachment"; |
| 2932 | case CFTYPE_EVENT: return "technote"; |
| 2933 | case CFTYPE_FORUM: return "forumpost"; |
| 2934 | } |
| 2935 | return NULL; |
| 2936 | } |
| 2937 | |
| 2938 | /* |
| 2939 | ** Creates a JSON representation of p, appending it to b. |
| 2940 | ** |
| 2941 | ** b is not cleared before rendering, so the caller needs to do that |
| 2942 | ** if it's important for their use case. |
| 2943 | ** |
| 2944 | ** Pedantic note: this routine traverses p->aFile directly, rather |
| 2945 | ** than using manifest_file_next(), so that delta manifests are |
| 2946 | ** rendered as-is instead of containing their derived F-cards. If that |
| 2947 | ** policy is ever changed, p will need to be non-const. |
| 2948 | */ |
| 2949 | void artifact_to_json(Manifest const *p, Blob *b){ |
| 2950 | int i; |
| 2951 | |
| 2952 | blob_append_literal(b, "{"); |
| 2953 | blob_appendf(b, "\"uuid\":\"%z\"", rid_to_uuid(p->rid)); |
| 2954 | /*blob_appendf(b, ", \"rid\": %d", p->rid); not portable across repos*/ |
| 2955 | blob_appendf(b, ",\"type\":%!j", artifact_type_to_name(p->type)); |
| 2956 | #define ISA(TYPE) if( p->type==TYPE ) |
| 2957 | #define CARD_LETTER(LETTER) \ |
| 2958 | blob_append_literal(b, ",\"" #LETTER "\":") |
| 2959 | #define CARD_STR(LETTER, VAL) \ |
| 2960 | assert( VAL ); CARD_LETTER(LETTER); blob_appendf(b, "%!j", VAL) |
| 2961 | #define CARD_STR2(LETTER, VAL) \ |
| 2962 | if( VAL ) { CARD_STR(LETTER, VAL); } (void)0 |
| 2963 | #define STR_OR_NULL(VAL) \ |
| 2964 | if( VAL ) blob_appendf(b, "%!j", VAL); \ |
| 2965 | else blob_append(b, "null", 4) |
| 2966 | #define KVP_STR(ADDCOMMA, KEY,VAL) \ |
| 2967 | if(ADDCOMMA) blob_append_char(b, ','); \ |
| 2968 | blob_appendf(b, "%!j:", #KEY); \ |
| 2969 | STR_OR_NULL(VAL) |
| 2970 | |
| 2971 | ISA( CFTYPE_ATTACHMENT ){ |
| 2972 | CARD_LETTER(A); |
| 2973 | blob_append_char(b, '{'); |
| 2974 | KVP_STR(0, filename, p->zAttachName); |
| 2975 | KVP_STR(1, target, p->zAttachTarget); |
| 2976 | KVP_STR(1, source, p->zAttachSrc); |
| 2977 | blob_append_char(b, '}'); |
| 2978 | } |
| 2979 | CARD_STR2(B, p->zBaseline); |
| 2980 | CARD_STR2(C, p->zComment); |
| 2981 | CARD_LETTER(D); blob_appendf(b, "%f", p->rDate); |
| 2982 | ISA( CFTYPE_EVENT ){ |
| 2983 | blob_appendf(b, ", \"E\":{\"time\":%f,\"id\":%!j}", |
| 2984 | p->rEventDate, p->zEventId); |
| 2985 | } |
| 2986 | ISA( CFTYPE_MANIFEST ){ |
| 2987 | CARD_LETTER(F); |
| 2988 | blob_append_char(b, '['); |
| 2989 | for( i = 0; i < p->nFile; ++i ){ |
| 2990 | ManifestFile const * const pF = &p->aFile[i]; |
| 2991 | if( i>0 ) blob_append_char(b, ','); |
| 2992 | blob_append_char(b, '{'); |
| 2993 | KVP_STR(0, name, pF->zName); |
| 2994 | KVP_STR(1, uuid, pF->zUuid); |
| 2995 | KVP_STR(1, perm, pF->zPerm); |
| 2996 | KVP_STR(1, rename, pF->zPrior); |
| 2997 | blob_append_char(b, '}'); |
| 2998 | } |
| 2999 | /* Special case: model checkins with no F-card as having an empty |
| 3000 | ** array, rather than no F-cards, to hypothetically simplify |
| 3001 | ** handling in JSON queries. */ |
| 3002 | blob_append_char(b, ']'); |
| 3003 | } |
| 3004 | CARD_STR2(G, p->zThreadRoot); |
| 3005 | ISA( CFTYPE_FORUM ){ |
| 3006 | CARD_LETTER(H); |
| 3007 | STR_OR_NULL( (p->zThreadTitle && *p->zThreadTitle) ? p->zThreadTitle : NULL); |
| 3008 | CARD_STR2(I, p->zInReplyTo); |
| 3009 | } |
| 3010 | if( p->nField ){ |
| 3011 | CARD_LETTER(J); |
| 3012 | blob_append_char(b, '['); |
| 3013 | for( i = 0; i < p->nField; ++i ){ |
| 3014 | const char * zName = p->aField[i].zName; |
| 3015 | if( i>0 ) blob_append_char(b, ','); |
| 3016 | blob_append_char(b, '{'); |
| 3017 | KVP_STR(0, name, '+'==*zName ? &zName[1] : zName); |
| 3018 | KVP_STR(1, value, p->aField[i].zValue); |
| 3019 | blob_appendf(b, ",\"append\":%s", '+'==*zName ? "true" : "false"); |
| 3020 | blob_append_char(b, '}'); |
| 3021 | } |
| 3022 | blob_append_char(b, ']'); |
| 3023 | } |
| 3024 | CARD_STR2(K, p->zTicketUuid); |
| 3025 | CARD_STR2(L, p->zWikiTitle); |
| 3026 | ISA( CFTYPE_CLUSTER ){ |
| 3027 | CARD_LETTER(M); |
| 3028 | blob_append_char(b, '['); |
| 3029 | for( int i = 0; i < p->nCChild; ++i ){ |
| 3030 | if( i>0 ) blob_append_char(b, ','); |
| 3031 | blob_appendf(b, "%!j", p->azCChild[i]); |
| 3032 | } |
| 3033 | blob_append_char(b, ']'); |
| 3034 | } |
| 3035 | CARD_STR2(N, p->zMimetype); |
| 3036 | ISA( CFTYPE_MANIFEST || p->nParent>0 ){ |
| 3037 | CARD_LETTER(P); |
| 3038 | blob_append_char(b, '['); |
| 3039 | for( i = 0; i < p->nParent; ++i ){ |
| 3040 | if( i>0 ) blob_append_char(b, ','); |
| 3041 | blob_appendf(b, "%!j", p->azParent[i]); |
| 3042 | } |
| 3043 | /* Special case: model checkins with no P-card as having an empty |
| 3044 | ** array, as per F-cards. */ |
| 3045 | blob_append_char(b, ']'); |
| 3046 | } |
| 3047 | if( p->nCherrypick ){ |
| 3048 | CARD_LETTER(Q); |
| 3049 | blob_append_char(b, '['); |
| 3050 | for( i = 0; i < p->nCherrypick; ++i ){ |
| 3051 | if( i>0 ) blob_append_char(b, ','); |
| 3052 | blob_append_char(b, '{'); |
| 3053 | blob_appendf(b, "\"type\":\"%c\"", p->aCherrypick[i].zCPTarget[0]); |
| 3054 | KVP_STR(1, target, &p->aCherrypick[i].zCPTarget[1]); |
| 3055 | KVP_STR(1, base, p->aCherrypick[i].zCPBase); |
| 3056 | blob_append_char(b, '}'); |
| 3057 | } |
| 3058 | blob_append_char(b, ']'); |
| 3059 | } |
| 3060 | CARD_STR2(R, p->zRepoCksum); |
| 3061 | if( p->nTag ){ |
| 3062 | CARD_LETTER(T); |
| 3063 | blob_append_char(b, '['); |
| 3064 | for( int i = 0; i < p->nTag; ++i ){ |
| 3065 | const char *zName = p->aTag[i].zName; |
| 3066 | if( i>0 ) blob_append_char(b, ','); |
| 3067 | blob_append_char(b, '{'); |
| 3068 | blob_appendf(b, "\"type\":\"%c\"", *zName); |
| 3069 | KVP_STR(1, name, &zName[1]); |
| 3070 | KVP_STR(1, target, p->aTag[i].zUuid ? p->aTag[i].zUuid : "*") |
| 3071 | /* We could arguably resolve the "*" as null or p's uuid. */; |
| 3072 | KVP_STR(1, value, p->aTag[i].zValue); |
| 3073 | blob_append_char(b, '}'); |
| 3074 | } |
| 3075 | blob_append_char(b, ']'); |
| 3076 | } |
| 3077 | CARD_STR2(U, p->zUser); |
| 3078 | if( p->zWiki || CFTYPE_WIKI==p->type || CFTYPE_FORUM==p->type |
| 3079 | || CFTYPE_EVENT==p->type ){ |
| 3080 | CARD_LETTER(W); |
| 3081 | STR_OR_NULL((p->zWiki && *p->zWiki) ? p->zWiki : NULL); |
| 3082 | } |
| 3083 | blob_append_literal(b, "}"); |
| 3084 | #undef CARD_FMT |
| 3085 | #undef CARD_LETTER |
| 3086 | #undef CARD_STR |
| 3087 | #undef CARD_STR2 |
| 3088 | #undef ISA |
| 3089 | #undef KVP_STR |
| 3090 | #undef STR_OR_NULL |
| 3091 | } |
| 3092 | |
| 3093 | /* |
| 3094 | ** Convenience wrapper around artifact_to_json() which expects rid to |
| 3095 | ** be the blob.rid of any artifact type. If it can load a Manifest |
| 3096 | ** with that rid, it returns rid, else it returns 0. |
| 3097 | */ |
| 3098 | int artifact_to_json_by_rid(int rid, Blob *pOut){ |
| 3099 | Manifest * const p = manifest_get(rid, CFTYPE_ANY, 0); |
| 3100 | if( p ){ |
| 3101 | artifact_to_json(p, pOut); |
| 3102 | manifest_destroy(p); |
| 3103 | }else{ |
| 3104 | rid = 0; |
| 3105 | } |
| 3106 | return rid; |
| 3107 | } |
| 3108 | |
| 3109 | /* |
| 3110 | ** Convenience wrapper around artifact_to_json() which accepts any |
| 3111 | ** artifact name which is legal for symbolic_name_to_rid(). On success |
| 3112 | ** it returns the rid of the artifact. Returns 0 if no such artifact |
| 3113 | ** exists and a negative value if the name is ambiguous. |
| 3114 | ** |
| 3115 | ** pOut is not cleared before rendering, so the caller needs to do |
| 3116 | ** that if it's important for their use case. |
| 3117 | */ |
| 3118 | int artifact_to_json_by_name(const char *zName, Blob *pOut){ |
| 3119 | const int rid = symbolic_name_to_rid(zName, 0); |
| 3120 | return rid>0 |
| 3121 | ? artifact_to_json_by_rid(rid, pOut) |
| 3122 | : rid; |
| 3123 | } |
| 3124 | |
| 3125 | /* |
| 3126 | ** SQLite UDF for artifact_to_json(). Its single argument should be |
| 3127 | ** either an INTEGER (blob.rid value) or a TEXT symbolic artifact |
| 3128 | ** name, as per symbolic_name_to_rid(). If an artifact is found then |
| 3129 | ** the result of the UDF is that JSON as a string, else it evaluates |
| 3130 | ** to NULL. |
| 3131 | */ |
| 3132 | void artifact_to_json_sql_func( |
| 3133 | sqlite3_context *context, |
| 3134 | int argc, |
| 3135 | sqlite3_value **argv |
| 3136 | ){ |
| 3137 | int rid = 0; |
| 3138 | Blob b = empty_blob; |
| 3139 | |
| 3140 | if(1 != argc){ |
| 3141 | goto error_usage; |
| 3142 | } |
| 3143 | switch( sqlite3_value_type(argv[0]) ){ |
| 3144 | case SQLITE_INTEGER: |
| 3145 | rid = artifact_to_json_by_rid(sqlite3_value_int(argv[0]), &b); |
| 3146 | break; |
| 3147 | case SQLITE_TEXT:{ |
| 3148 | const char * z = (const char *)sqlite3_value_text(argv[0]); |
| 3149 | if( z ){ |
| 3150 | rid = artifact_to_json_by_name(z, &b); |
| 3151 | } |
| 3152 | break; |
| 3153 | } |
| 3154 | default: |
| 3155 | goto error_usage; |
| 3156 | } |
| 3157 | if( rid>0 ){ |
| 3158 | sqlite3_result_text(context, blob_str(&b), blob_size(&b), |
| 3159 | SQLITE_TRANSIENT); |
| 3160 | blob_reset(&b); |
| 3161 | }else{ |
| 3162 | /* We should arguably error out if rid<0 (ambiguous name) */ |
| 3163 | sqlite3_result_null(context); |
| 3164 | } |
| 3165 | return; |
| 3166 | error_usage: |
| 3167 | sqlite3_result_error(context, "Expecting one argument: blob.rid or " |
| 3168 | "artifact symbolic name", -1); |
| 3169 | } |
| 3170 | |
| 3171 | |
| 3172 | |
| 3173 | /* |
| 3174 | ** COMMAND: test-artifact-to-json |
| 3175 | ** |
| 3176 | ** Usage: %fossil test-artifact-to-json ?-pretty|-p? symbolic-name [...names] |
| 3177 | ** |
| 3178 | ** Tests the artifact_to_json() and artifact_to_json_by_name() APIs. |
| 3179 | */ |
| 3180 | void test_manifest_to_json(void){ |
| 3181 | int i; |
| 3182 | Blob b = empty_blob; |
| 3183 | Stmt q; |
| 3184 | const int bPretty = find_option("pretty","p",0)!=0; |
| 3185 | int nErr = 0; |
| 3186 | |
| 3187 | db_find_and_open_repository(0,0); |
| 3188 | db_prepare(&q, "select json_pretty(:json)"); |
| 3189 | for( i=2; i<g.argc; ++i ){ |
| 3190 | char const *zName = g.argv[i]; |
| 3191 | const int rc = artifact_to_json_by_name(zName, &b); |
| 3192 | if( rc<=0 ){ |
| 3193 | ++nErr; |
| 3194 | fossil_warning("Error reading artifact %Q", zName); |
| 3195 | continue; |
| 3196 | }else if( bPretty ){ |
| 3197 | db_bind_blob(&q, ":json", &b); |
| 3198 | b.nUsed = 0; |
| 3199 | db_step(&q); |
| 3200 | db_column_blob(&q, 0, &b); |
| 3201 | db_reset(&q); |
| 3202 | } |
| 3203 | fossil_print("%b\n", &b); |
| 3204 | blob_reset(&b); |
| 3205 | } |
| 3206 | db_finalize(&q); |
| 3207 | if( nErr ){ |
| 3208 | fossil_warning("Error count: %d", nErr); |
| 3209 | } |
| 3210 | } |
| 3211 |
+57
| --- src/markdown_html.c | ||
| +++ src/markdown_html.c | ||
| @@ -911,5 +911,62 @@ | ||
| 911 | 911 | html_renderer.opaque = &context; |
| 912 | 912 | if( output_title ) blob_reset(output_title); |
| 913 | 913 | blob_reset(output_body); |
| 914 | 914 | markdown(output_body, input_markdown, &html_renderer); |
| 915 | 915 | } |
| 916 | + | |
| 917 | +/* | |
| 918 | +** Undo HTML escapes in Blob p. In other words convert: | |
| 919 | +** | |
| 920 | +** & -> & | |
| 921 | +** < -> < | |
| 922 | +** > -> > | |
| 923 | +** " -> " | |
| 924 | +** &#NNN; -> ascii character NNN | |
| 925 | +*/ | |
| 926 | +void markdown_dehtmlize_blob(Blob *p){ | |
| 927 | + char *z; | |
| 928 | + unsigned int j, k; | |
| 929 | + | |
| 930 | + z = p->aData; | |
| 931 | + for(j=k=0; j<p->nUsed; j++){ | |
| 932 | + char c = z[j]; | |
| 933 | + if( c=='&' ){ | |
| 934 | + if( z[j+1]=='#' && fossil_isdigit(z[j+2]) ){ | |
| 935 | + int n = 3; | |
| 936 | + int x = z[j+2] - '0'; | |
| 937 | + if( fossil_isdigit(z[j+3]) ){ | |
| 938 | + x = x*10 + z[j+3] - '0'; | |
| 939 | + n++; | |
| 940 | + if( fossil_isdigit(z[j+4]) ){ | |
| 941 | + x = x*10 + z[j+4] - '0'; | |
| 942 | + n++; | |
| 943 | + } | |
| 944 | + } | |
| 945 | + if( z[j+n]==';' ){ | |
| 946 | + z[k++] = (char)x; | |
| 947 | + j += n; | |
| 948 | + }else{ | |
| 949 | + z[k++] = c; | |
| 950 | + } | |
| 951 | + }else if( memcmp(&z[j],"<",4)==0 ){ | |
| 952 | + z[k++] = '<'; | |
| 953 | + j += 3; | |
| 954 | + }else if( memcmp(&z[j],">",4)==0 ){ | |
| 955 | + z[k++] = '>'; | |
| 956 | + j += 3; | |
| 957 | + }else if( memcmp(&z[j],""",6)==0 ){ | |
| 958 | + z[k++] = '"'; | |
| 959 | + j += 5; | |
| 960 | + }else if( memcmp(&z[j],"&",5)==0 ){ | |
| 961 | + z[k++] = '&'; | |
| 962 | + j += 4; | |
| 963 | + }else{ | |
| 964 | + z[k++] = c; | |
| 965 | + } | |
| 966 | + }else{ | |
| 967 | + z[k++] = c; | |
| 968 | + } | |
| 969 | + } | |
| 970 | + z[k] = 0; | |
| 971 | + p->nUsed = k; | |
| 972 | +} | |
| 916 | 973 |
| --- src/markdown_html.c | |
| +++ src/markdown_html.c | |
| @@ -911,5 +911,62 @@ | |
| 911 | html_renderer.opaque = &context; |
| 912 | if( output_title ) blob_reset(output_title); |
| 913 | blob_reset(output_body); |
| 914 | markdown(output_body, input_markdown, &html_renderer); |
| 915 | } |
| 916 |
| --- src/markdown_html.c | |
| +++ src/markdown_html.c | |
| @@ -911,5 +911,62 @@ | |
| 911 | html_renderer.opaque = &context; |
| 912 | if( output_title ) blob_reset(output_title); |
| 913 | blob_reset(output_body); |
| 914 | markdown(output_body, input_markdown, &html_renderer); |
| 915 | } |
| 916 | |
| 917 | /* |
| 918 | ** Undo HTML escapes in Blob p. In other words convert: |
| 919 | ** |
| 920 | ** & -> & |
| 921 | ** < -> < |
| 922 | ** > -> > |
| 923 | ** " -> " |
| 924 | ** &#NNN; -> ascii character NNN |
| 925 | */ |
| 926 | void markdown_dehtmlize_blob(Blob *p){ |
| 927 | char *z; |
| 928 | unsigned int j, k; |
| 929 | |
| 930 | z = p->aData; |
| 931 | for(j=k=0; j<p->nUsed; j++){ |
| 932 | char c = z[j]; |
| 933 | if( c=='&' ){ |
| 934 | if( z[j+1]=='#' && fossil_isdigit(z[j+2]) ){ |
| 935 | int n = 3; |
| 936 | int x = z[j+2] - '0'; |
| 937 | if( fossil_isdigit(z[j+3]) ){ |
| 938 | x = x*10 + z[j+3] - '0'; |
| 939 | n++; |
| 940 | if( fossil_isdigit(z[j+4]) ){ |
| 941 | x = x*10 + z[j+4] - '0'; |
| 942 | n++; |
| 943 | } |
| 944 | } |
| 945 | if( z[j+n]==';' ){ |
| 946 | z[k++] = (char)x; |
| 947 | j += n; |
| 948 | }else{ |
| 949 | z[k++] = c; |
| 950 | } |
| 951 | }else if( memcmp(&z[j],"<",4)==0 ){ |
| 952 | z[k++] = '<'; |
| 953 | j += 3; |
| 954 | }else if( memcmp(&z[j],">",4)==0 ){ |
| 955 | z[k++] = '>'; |
| 956 | j += 3; |
| 957 | }else if( memcmp(&z[j],""",6)==0 ){ |
| 958 | z[k++] = '"'; |
| 959 | j += 5; |
| 960 | }else if( memcmp(&z[j],"&",5)==0 ){ |
| 961 | z[k++] = '&'; |
| 962 | j += 4; |
| 963 | }else{ |
| 964 | z[k++] = c; |
| 965 | } |
| 966 | }else{ |
| 967 | z[k++] = c; |
| 968 | } |
| 969 | } |
| 970 | z[k] = 0; |
| 971 | p->nUsed = k; |
| 972 | } |
| 973 |
+107
-18
| --- src/name.c | ||
| +++ src/name.c | ||
| @@ -58,29 +58,42 @@ | ||
| 58 | 58 | ** Check to see if the string might be a compact date/time that omits |
| 59 | 59 | ** the punctuation. Example: "20190327084549" instead of |
| 60 | 60 | ** "2019-03-27 08:45:49". If the string is of the appropriate form, |
| 61 | 61 | ** then return an alternative string (in static space) that is the same |
| 62 | 62 | ** string with punctuation inserted. |
| 63 | +** | |
| 64 | +** If the bRoundUp parameter is true, then round the resulting date-time | |
| 65 | +** up to the largest date/time that is consistent with the input value. | |
| 66 | +** This is because the result will be used for an mtime<=julianday($DATE) | |
| 67 | +** comparison. In other words: | |
| 68 | +** | |
| 69 | +** 20250317123421 -> 2025-03-17 12:34:21.999 | |
| 70 | +** ^^^^--- Added | |
| 71 | +** | |
| 72 | +** 202503171234 -> 2025-03-17 12:34:59.999 | |
| 73 | +** ^^^^^^^--- Added | |
| 74 | +** 20250317 -> 2025-03-17 23:59:59.999 | |
| 75 | +** ^^^^^^^^^^^^^--- Added | |
| 63 | 76 | ** |
| 64 | 77 | ** If the bVerifyNotAHash flag is true, then a check is made to see if |
| 65 | -** the string is a hash prefix and NULL is returned if it is. If the | |
| 78 | +** the input string is a hash prefix and NULL is returned if it is. If the | |
| 66 | 79 | ** bVerifyNotAHash flag is false, then the result is determined by syntax |
| 67 | 80 | ** of the input string only, without reference to the artifact table. |
| 68 | 81 | */ |
| 69 | -char *fossil_expand_datetime(const char *zIn, int bVerifyNotAHash){ | |
| 82 | +char *fossil_expand_datetime(const char *zIn,int bVerifyNotAHash,int bRoundUp){ | |
| 70 | 83 | static char zEDate[24]; |
| 71 | 84 | static const char aPunct[] = { 0, 0, '-', '-', ' ', ':', ':' }; |
| 72 | 85 | int n = (int)strlen(zIn); |
| 73 | 86 | int i, j; |
| 74 | 87 | int addZulu = 0; |
| 75 | 88 | |
| 76 | 89 | /* These forms are allowed: |
| 77 | 90 | ** |
| 78 | - ** 123456789 1234 123456789 123456789 | |
| 79 | - ** (1) YYYYMMDD => YYYY-MM-DD | |
| 80 | - ** (2) YYYYMMDDHHMM => YYYY-MM-DD HH:MM | |
| 81 | - ** (3) YYYYMMDDHHMMSS => YYYY-MM-DD HH:MM:SS | |
| 91 | + ** 123456789 1234 123456789 123456789 1234 | |
| 92 | + ** (1) YYYYMMDD => YYYY-MM-DD 23:59:59.999 | |
| 93 | + ** (2) YYYYMMDDHHMM => YYYY-MM-DD HH:MM:59.999 | |
| 94 | + ** (3) YYYYMMDDHHMMSS => YYYY-MM-DD HH:MM:SS.999 | |
| 82 | 95 | ** |
| 83 | 96 | ** An optional "Z" zulu timezone designator is allowed at the end. |
| 84 | 97 | */ |
| 85 | 98 | if( n>0 && (zIn[n-1]=='Z' || zIn[n-1]=='z') ){ |
| 86 | 99 | n--; |
| @@ -99,15 +112,23 @@ | ||
| 99 | 112 | if( i>=4 && (i%2)==0 ){ |
| 100 | 113 | zEDate[j++] = aPunct[i/2]; |
| 101 | 114 | } |
| 102 | 115 | zEDate[j++] = zIn[i]; |
| 103 | 116 | } |
| 104 | - if( addZulu ){ | |
| 117 | + if( bRoundUp ){ | |
| 105 | 118 | if( j==10 ){ |
| 106 | - memcpy(&zEDate[10]," 00:00", 6); | |
| 107 | - j += 6; | |
| 119 | + memcpy(&zEDate[10], " 23:59:59.999", 13); | |
| 120 | + j += 13; | |
| 121 | + }else if( j==16 ){ | |
| 122 | + memcpy(&zEDate[16], ":59.999",7); | |
| 123 | + j += 7; | |
| 124 | + }else if( j==19 ){ | |
| 125 | + memcpy(&zEDate[19], ".999", 4); | |
| 126 | + j += 4; | |
| 108 | 127 | } |
| 128 | + } | |
| 129 | + if( addZulu ){ | |
| 109 | 130 | zEDate[j++] = 'Z'; |
| 110 | 131 | } |
| 111 | 132 | zEDate[j] = 0; |
| 112 | 133 | |
| 113 | 134 | /* Check for reasonable date values. |
| @@ -147,27 +168,40 @@ | ||
| 147 | 168 | ** comparison. So add in missing factional seconds or seconds or time. |
| 148 | 169 | ** |
| 149 | 170 | ** The returned string is held in a static buffer that is overwritten |
| 150 | 171 | ** with each call, or else is just a copy of its input if there are |
| 151 | 172 | ** no changes. |
| 173 | +** | |
| 174 | +** For reference: | |
| 175 | +** | |
| 176 | +** 0123456789 123456789 1234 | |
| 177 | +** YYYY-MM-DD HH:MM:SS.SSSz | |
| 152 | 178 | */ |
| 153 | 179 | const char *fossil_roundup_date(const char *zDate){ |
| 154 | - static char zUp[24]; | |
| 180 | + static char zUp[28]; | |
| 155 | 181 | int n = (int)strlen(zDate); |
| 182 | + int addZ = 0; | |
| 183 | + if( n>10 && (zDate[n-1]=='z' || zDate[n-1]=='Z') ){ | |
| 184 | + n--; | |
| 185 | + addZ = 1; | |
| 186 | + } | |
| 156 | 187 | if( n==19 ){ /* YYYY-MM-DD HH:MM:SS */ |
| 157 | 188 | memcpy(zUp, zDate, 19); |
| 158 | - memcpy(zUp+19, ".999", 5); | |
| 189 | + memcpy(zUp+19, ".999z", 6); | |
| 190 | + if( !addZ ) zUp[23] = 0; | |
| 159 | 191 | return zUp; |
| 160 | 192 | } |
| 161 | 193 | if( n==16 ){ /* YYYY-MM-DD HH:MM */ |
| 162 | 194 | memcpy(zUp, zDate, 16); |
| 163 | - memcpy(zUp+16, ":59.999", 8); | |
| 195 | + memcpy(zUp+16, ":59.999z", 8); | |
| 196 | + if( !addZ ) zUp[23] = 0; | |
| 164 | 197 | return zUp; |
| 165 | 198 | } |
| 166 | 199 | if( n==10 ){ /* YYYY-MM-DD */ |
| 167 | 200 | memcpy(zUp, zDate, 10); |
| 168 | - memcpy(zUp+10, " 23:59:59.999", 14); | |
| 201 | + memcpy(zUp+10, " 23:59:59.999z", 14); | |
| 202 | + if( !addZ ) zUp[23] = 0; | |
| 169 | 203 | return zUp; |
| 170 | 204 | } |
| 171 | 205 | return zDate; |
| 172 | 206 | } |
| 173 | 207 | |
| @@ -231,11 +265,11 @@ | ||
| 231 | 265 | ** This is a tricky query to do efficiently. |
| 232 | 266 | ** If the tag is very common (ex: "trunk") then |
| 233 | 267 | ** we want to use the query identified below as Q1 - which searches |
| 234 | 268 | ** the most recent EVENT table entries for the most recent with the tag. |
| 235 | 269 | ** But if the tag is relatively scarce (anything other than "trunk", basically) |
| 236 | -** then we want to do the indexed search show below as Q2. | |
| 270 | +** then we want to do the indexed search shown below as Q2. | |
| 237 | 271 | */ |
| 238 | 272 | static int most_recent_event_with_tag(const char *zTag, const char *zType){ |
| 239 | 273 | return db_int(0, |
| 240 | 274 | "SELECT objid FROM (" |
| 241 | 275 | /* Q1: Begin by looking for the tag in the 30 most recent events */ |
| @@ -479,11 +513,11 @@ | ||
| 479 | 513 | if( rid ) return rid; |
| 480 | 514 | } |
| 481 | 515 | |
| 482 | 516 | /* Date and times */ |
| 483 | 517 | if( memcmp(zTag, "date:", 5)==0 ){ |
| 484 | - zDate = fossil_expand_datetime(&zTag[5],0); | |
| 518 | + zDate = fossil_expand_datetime(&zTag[5],0,1); | |
| 485 | 519 | if( zDate==0 ) zDate = &zTag[5]; |
| 486 | 520 | rid = db_int(0, |
| 487 | 521 | "SELECT objid FROM event" |
| 488 | 522 | " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'" |
| 489 | 523 | " ORDER BY mtime DESC LIMIT 1", |
| @@ -545,21 +579,21 @@ | ||
| 545 | 579 | |
| 546 | 580 | /* symbolic-name ":" date-time */ |
| 547 | 581 | nTag = strlen(zTag); |
| 548 | 582 | for(i=0; i<nTag-8 && zTag[i]!=':'; i++){} |
| 549 | 583 | if( zTag[i]==':' |
| 550 | - && (fossil_isdate(&zTag[i+1]) || fossil_expand_datetime(&zTag[i+1],0)!=0) | |
| 584 | + && (fossil_isdate(&zTag[i+1]) || fossil_expand_datetime(&zTag[i+1],0,0)!=0) | |
| 551 | 585 | ){ |
| 552 | 586 | char *zDate = mprintf("%s", &zTag[i+1]); |
| 553 | 587 | char *zTagBase = mprintf("%.*s", i, zTag); |
| 554 | 588 | char *zXDate; |
| 555 | 589 | int nDate = strlen(zDate); |
| 556 | 590 | if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){ |
| 557 | 591 | zDate[nDate-3] = 'z'; |
| 558 | 592 | zDate[nDate-2] = 0; |
| 559 | 593 | } |
| 560 | - zXDate = fossil_expand_datetime(zDate,0); | |
| 594 | + zXDate = fossil_expand_datetime(zDate,0,1); | |
| 561 | 595 | if( zXDate==0 ) zXDate = zDate; |
| 562 | 596 | rid = db_int(0, |
| 563 | 597 | "SELECT event.objid, max(event.mtime)" |
| 564 | 598 | " FROM tag, tagxref, event" |
| 565 | 599 | " WHERE tag.tagname='sym-%q' " |
| @@ -631,11 +665,11 @@ | ||
| 631 | 665 | if( startOfBranch ) rid = start_of_branch(rid,1); |
| 632 | 666 | return rid; |
| 633 | 667 | } |
| 634 | 668 | |
| 635 | 669 | /* Pure numeric date/time */ |
| 636 | - zDate = fossil_expand_datetime(zTag, 0); | |
| 670 | + zDate = fossil_expand_datetime(zTag, 0,1); | |
| 637 | 671 | if( zDate ){ |
| 638 | 672 | rid = db_int(0, |
| 639 | 673 | "SELECT objid FROM event" |
| 640 | 674 | " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'" |
| 641 | 675 | " ORDER BY mtime DESC LIMIT 1", |
| @@ -684,10 +718,65 @@ | ||
| 684 | 718 | rid = symbolic_name_to_rid(zNew,zType); |
| 685 | 719 | fossil_free(zNew); |
| 686 | 720 | } |
| 687 | 721 | return rid; |
| 688 | 722 | } |
| 723 | + | |
| 724 | +/* | |
| 725 | +** Convert a symbolic name used as an argument to the a=, b=, or c= | |
| 726 | +** query parameters of timeline into a julianday mtime value. | |
| 727 | +** | |
| 728 | +** If pzDisplay is not null, then display text for the symbolic name might | |
| 729 | +** be written into *pzDisplay. But that is not guaranteed. | |
| 730 | +** | |
| 731 | +** If bRoundUp is true and the symbolic name is a timestamp with less | |
| 732 | +** than millisecond resolution, then the timestamp is rounding up to the | |
| 733 | +** largest millisecond consistent with that timestamp. If bRoundUp is | |
| 734 | +** false, then the resulting time is obtained by extending the timestamp | |
| 735 | +** with zeros (hence rounding down). Use bRoundUp==1 if the result | |
| 736 | +** will be used in mtime<=$RESULT and use bRoundUp==0 if the result | |
| 737 | +** will be used in mtime>=$RESULT. | |
| 738 | +*/ | |
| 739 | +double symbolic_name_to_mtime( | |
| 740 | + const char *z, /* Input symbolic name */ | |
| 741 | + const char **pzDisplay, /* Perhaps write display text here, if not NULL */ | |
| 742 | + int bRoundUp /* Round up if true */ | |
| 743 | +){ | |
| 744 | + double mtime; | |
| 745 | + int rid; | |
| 746 | + const char *zDate; | |
| 747 | + if( z==0 ) return -1.0; | |
| 748 | + if( fossil_isdate(z) ){ | |
| 749 | + mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", z); | |
| 750 | + if( mtime>0.0 ) return mtime; | |
| 751 | + } | |
| 752 | + zDate = fossil_expand_datetime(z, 1, bRoundUp); | |
| 753 | + if( zDate!=0 ){ | |
| 754 | + mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", | |
| 755 | + bRoundUp ? fossil_roundup_date(zDate) : zDate); | |
| 756 | + if( mtime>0.0 ){ | |
| 757 | + if( pzDisplay ){ | |
| 758 | + zDate = fossil_expand_datetime(z,0,0); | |
| 759 | + *pzDisplay = fossil_strdup(zDate); | |
| 760 | + } | |
| 761 | + return mtime; | |
| 762 | + } | |
| 763 | + } | |
| 764 | + rid = symbolic_name_to_rid(z, "*"); | |
| 765 | + if( rid ){ | |
| 766 | + mtime = mtime_of_rid(rid, 0.0); | |
| 767 | + }else{ | |
| 768 | + mtime = db_double(-1.0, | |
| 769 | + "SELECT max(event.mtime) FROM event, tag, tagxref" | |
| 770 | + " WHERE tag.tagname GLOB 'event-%q*'" | |
| 771 | + " AND tagxref.tagid=tag.tagid AND tagxref.tagtype" | |
| 772 | + " AND event.objid=tagxref.rid", | |
| 773 | + z | |
| 774 | + ); | |
| 775 | + } | |
| 776 | + return mtime; | |
| 777 | +} | |
| 689 | 778 | |
| 690 | 779 | /* |
| 691 | 780 | ** This routine takes a user-entered string and tries to convert it to |
| 692 | 781 | ** an artifact hash. |
| 693 | 782 | ** |
| 694 | 783 |
| --- src/name.c | |
| +++ src/name.c | |
| @@ -58,29 +58,42 @@ | |
| 58 | ** Check to see if the string might be a compact date/time that omits |
| 59 | ** the punctuation. Example: "20190327084549" instead of |
| 60 | ** "2019-03-27 08:45:49". If the string is of the appropriate form, |
| 61 | ** then return an alternative string (in static space) that is the same |
| 62 | ** string with punctuation inserted. |
| 63 | ** |
| 64 | ** If the bVerifyNotAHash flag is true, then a check is made to see if |
| 65 | ** the string is a hash prefix and NULL is returned if it is. If the |
| 66 | ** bVerifyNotAHash flag is false, then the result is determined by syntax |
| 67 | ** of the input string only, without reference to the artifact table. |
| 68 | */ |
| 69 | char *fossil_expand_datetime(const char *zIn, int bVerifyNotAHash){ |
| 70 | static char zEDate[24]; |
| 71 | static const char aPunct[] = { 0, 0, '-', '-', ' ', ':', ':' }; |
| 72 | int n = (int)strlen(zIn); |
| 73 | int i, j; |
| 74 | int addZulu = 0; |
| 75 | |
| 76 | /* These forms are allowed: |
| 77 | ** |
| 78 | ** 123456789 1234 123456789 123456789 |
| 79 | ** (1) YYYYMMDD => YYYY-MM-DD |
| 80 | ** (2) YYYYMMDDHHMM => YYYY-MM-DD HH:MM |
| 81 | ** (3) YYYYMMDDHHMMSS => YYYY-MM-DD HH:MM:SS |
| 82 | ** |
| 83 | ** An optional "Z" zulu timezone designator is allowed at the end. |
| 84 | */ |
| 85 | if( n>0 && (zIn[n-1]=='Z' || zIn[n-1]=='z') ){ |
| 86 | n--; |
| @@ -99,15 +112,23 @@ | |
| 99 | if( i>=4 && (i%2)==0 ){ |
| 100 | zEDate[j++] = aPunct[i/2]; |
| 101 | } |
| 102 | zEDate[j++] = zIn[i]; |
| 103 | } |
| 104 | if( addZulu ){ |
| 105 | if( j==10 ){ |
| 106 | memcpy(&zEDate[10]," 00:00", 6); |
| 107 | j += 6; |
| 108 | } |
| 109 | zEDate[j++] = 'Z'; |
| 110 | } |
| 111 | zEDate[j] = 0; |
| 112 | |
| 113 | /* Check for reasonable date values. |
| @@ -147,27 +168,40 @@ | |
| 147 | ** comparison. So add in missing factional seconds or seconds or time. |
| 148 | ** |
| 149 | ** The returned string is held in a static buffer that is overwritten |
| 150 | ** with each call, or else is just a copy of its input if there are |
| 151 | ** no changes. |
| 152 | */ |
| 153 | const char *fossil_roundup_date(const char *zDate){ |
| 154 | static char zUp[24]; |
| 155 | int n = (int)strlen(zDate); |
| 156 | if( n==19 ){ /* YYYY-MM-DD HH:MM:SS */ |
| 157 | memcpy(zUp, zDate, 19); |
| 158 | memcpy(zUp+19, ".999", 5); |
| 159 | return zUp; |
| 160 | } |
| 161 | if( n==16 ){ /* YYYY-MM-DD HH:MM */ |
| 162 | memcpy(zUp, zDate, 16); |
| 163 | memcpy(zUp+16, ":59.999", 8); |
| 164 | return zUp; |
| 165 | } |
| 166 | if( n==10 ){ /* YYYY-MM-DD */ |
| 167 | memcpy(zUp, zDate, 10); |
| 168 | memcpy(zUp+10, " 23:59:59.999", 14); |
| 169 | return zUp; |
| 170 | } |
| 171 | return zDate; |
| 172 | } |
| 173 | |
| @@ -231,11 +265,11 @@ | |
| 231 | ** This is a tricky query to do efficiently. |
| 232 | ** If the tag is very common (ex: "trunk") then |
| 233 | ** we want to use the query identified below as Q1 - which searches |
| 234 | ** the most recent EVENT table entries for the most recent with the tag. |
| 235 | ** But if the tag is relatively scarce (anything other than "trunk", basically) |
| 236 | ** then we want to do the indexed search show below as Q2. |
| 237 | */ |
| 238 | static int most_recent_event_with_tag(const char *zTag, const char *zType){ |
| 239 | return db_int(0, |
| 240 | "SELECT objid FROM (" |
| 241 | /* Q1: Begin by looking for the tag in the 30 most recent events */ |
| @@ -479,11 +513,11 @@ | |
| 479 | if( rid ) return rid; |
| 480 | } |
| 481 | |
| 482 | /* Date and times */ |
| 483 | if( memcmp(zTag, "date:", 5)==0 ){ |
| 484 | zDate = fossil_expand_datetime(&zTag[5],0); |
| 485 | if( zDate==0 ) zDate = &zTag[5]; |
| 486 | rid = db_int(0, |
| 487 | "SELECT objid FROM event" |
| 488 | " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'" |
| 489 | " ORDER BY mtime DESC LIMIT 1", |
| @@ -545,21 +579,21 @@ | |
| 545 | |
| 546 | /* symbolic-name ":" date-time */ |
| 547 | nTag = strlen(zTag); |
| 548 | for(i=0; i<nTag-8 && zTag[i]!=':'; i++){} |
| 549 | if( zTag[i]==':' |
| 550 | && (fossil_isdate(&zTag[i+1]) || fossil_expand_datetime(&zTag[i+1],0)!=0) |
| 551 | ){ |
| 552 | char *zDate = mprintf("%s", &zTag[i+1]); |
| 553 | char *zTagBase = mprintf("%.*s", i, zTag); |
| 554 | char *zXDate; |
| 555 | int nDate = strlen(zDate); |
| 556 | if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){ |
| 557 | zDate[nDate-3] = 'z'; |
| 558 | zDate[nDate-2] = 0; |
| 559 | } |
| 560 | zXDate = fossil_expand_datetime(zDate,0); |
| 561 | if( zXDate==0 ) zXDate = zDate; |
| 562 | rid = db_int(0, |
| 563 | "SELECT event.objid, max(event.mtime)" |
| 564 | " FROM tag, tagxref, event" |
| 565 | " WHERE tag.tagname='sym-%q' " |
| @@ -631,11 +665,11 @@ | |
| 631 | if( startOfBranch ) rid = start_of_branch(rid,1); |
| 632 | return rid; |
| 633 | } |
| 634 | |
| 635 | /* Pure numeric date/time */ |
| 636 | zDate = fossil_expand_datetime(zTag, 0); |
| 637 | if( zDate ){ |
| 638 | rid = db_int(0, |
| 639 | "SELECT objid FROM event" |
| 640 | " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'" |
| 641 | " ORDER BY mtime DESC LIMIT 1", |
| @@ -684,10 +718,65 @@ | |
| 684 | rid = symbolic_name_to_rid(zNew,zType); |
| 685 | fossil_free(zNew); |
| 686 | } |
| 687 | return rid; |
| 688 | } |
| 689 | |
| 690 | /* |
| 691 | ** This routine takes a user-entered string and tries to convert it to |
| 692 | ** an artifact hash. |
| 693 | ** |
| 694 |
| --- src/name.c | |
| +++ src/name.c | |
| @@ -58,29 +58,42 @@ | |
| 58 | ** Check to see if the string might be a compact date/time that omits |
| 59 | ** the punctuation. Example: "20190327084549" instead of |
| 60 | ** "2019-03-27 08:45:49". If the string is of the appropriate form, |
| 61 | ** then return an alternative string (in static space) that is the same |
| 62 | ** string with punctuation inserted. |
| 63 | ** |
| 64 | ** If the bRoundUp parameter is true, then round the resulting date-time |
| 65 | ** up to the largest date/time that is consistent with the input value. |
| 66 | ** This is because the result will be used for an mtime<=julianday($DATE) |
| 67 | ** comparison. In other words: |
| 68 | ** |
| 69 | ** 20250317123421 -> 2025-03-17 12:34:21.999 |
| 70 | ** ^^^^--- Added |
| 71 | ** |
| 72 | ** 202503171234 -> 2025-03-17 12:34:59.999 |
| 73 | ** ^^^^^^^--- Added |
| 74 | ** 20250317 -> 2025-03-17 23:59:59.999 |
| 75 | ** ^^^^^^^^^^^^^--- Added |
| 76 | ** |
| 77 | ** If the bVerifyNotAHash flag is true, then a check is made to see if |
| 78 | ** the input string is a hash prefix and NULL is returned if it is. If the |
| 79 | ** bVerifyNotAHash flag is false, then the result is determined by syntax |
| 80 | ** of the input string only, without reference to the artifact table. |
| 81 | */ |
| 82 | char *fossil_expand_datetime(const char *zIn,int bVerifyNotAHash,int bRoundUp){ |
| 83 | static char zEDate[24]; |
| 84 | static const char aPunct[] = { 0, 0, '-', '-', ' ', ':', ':' }; |
| 85 | int n = (int)strlen(zIn); |
| 86 | int i, j; |
| 87 | int addZulu = 0; |
| 88 | |
| 89 | /* These forms are allowed: |
| 90 | ** |
| 91 | ** 123456789 1234 123456789 123456789 1234 |
| 92 | ** (1) YYYYMMDD => YYYY-MM-DD 23:59:59.999 |
| 93 | ** (2) YYYYMMDDHHMM => YYYY-MM-DD HH:MM:59.999 |
| 94 | ** (3) YYYYMMDDHHMMSS => YYYY-MM-DD HH:MM:SS.999 |
| 95 | ** |
| 96 | ** An optional "Z" zulu timezone designator is allowed at the end. |
| 97 | */ |
| 98 | if( n>0 && (zIn[n-1]=='Z' || zIn[n-1]=='z') ){ |
| 99 | n--; |
| @@ -99,15 +112,23 @@ | |
| 112 | if( i>=4 && (i%2)==0 ){ |
| 113 | zEDate[j++] = aPunct[i/2]; |
| 114 | } |
| 115 | zEDate[j++] = zIn[i]; |
| 116 | } |
| 117 | if( bRoundUp ){ |
| 118 | if( j==10 ){ |
| 119 | memcpy(&zEDate[10], " 23:59:59.999", 13); |
| 120 | j += 13; |
| 121 | }else if( j==16 ){ |
| 122 | memcpy(&zEDate[16], ":59.999",7); |
| 123 | j += 7; |
| 124 | }else if( j==19 ){ |
| 125 | memcpy(&zEDate[19], ".999", 4); |
| 126 | j += 4; |
| 127 | } |
| 128 | } |
| 129 | if( addZulu ){ |
| 130 | zEDate[j++] = 'Z'; |
| 131 | } |
| 132 | zEDate[j] = 0; |
| 133 | |
| 134 | /* Check for reasonable date values. |
| @@ -147,27 +168,40 @@ | |
| 168 | ** comparison. So add in missing factional seconds or seconds or time. |
| 169 | ** |
| 170 | ** The returned string is held in a static buffer that is overwritten |
| 171 | ** with each call, or else is just a copy of its input if there are |
| 172 | ** no changes. |
| 173 | ** |
| 174 | ** For reference: |
| 175 | ** |
| 176 | ** 0123456789 123456789 1234 |
| 177 | ** YYYY-MM-DD HH:MM:SS.SSSz |
| 178 | */ |
| 179 | const char *fossil_roundup_date(const char *zDate){ |
| 180 | static char zUp[28]; |
| 181 | int n = (int)strlen(zDate); |
| 182 | int addZ = 0; |
| 183 | if( n>10 && (zDate[n-1]=='z' || zDate[n-1]=='Z') ){ |
| 184 | n--; |
| 185 | addZ = 1; |
| 186 | } |
| 187 | if( n==19 ){ /* YYYY-MM-DD HH:MM:SS */ |
| 188 | memcpy(zUp, zDate, 19); |
| 189 | memcpy(zUp+19, ".999z", 6); |
| 190 | if( !addZ ) zUp[23] = 0; |
| 191 | return zUp; |
| 192 | } |
| 193 | if( n==16 ){ /* YYYY-MM-DD HH:MM */ |
| 194 | memcpy(zUp, zDate, 16); |
| 195 | memcpy(zUp+16, ":59.999z", 8); |
| 196 | if( !addZ ) zUp[23] = 0; |
| 197 | return zUp; |
| 198 | } |
| 199 | if( n==10 ){ /* YYYY-MM-DD */ |
| 200 | memcpy(zUp, zDate, 10); |
| 201 | memcpy(zUp+10, " 23:59:59.999z", 14); |
| 202 | if( !addZ ) zUp[23] = 0; |
| 203 | return zUp; |
| 204 | } |
| 205 | return zDate; |
| 206 | } |
| 207 | |
| @@ -231,11 +265,11 @@ | |
| 265 | ** This is a tricky query to do efficiently. |
| 266 | ** If the tag is very common (ex: "trunk") then |
| 267 | ** we want to use the query identified below as Q1 - which searches |
| 268 | ** the most recent EVENT table entries for the most recent with the tag. |
| 269 | ** But if the tag is relatively scarce (anything other than "trunk", basically) |
| 270 | ** then we want to do the indexed search shown below as Q2. |
| 271 | */ |
| 272 | static int most_recent_event_with_tag(const char *zTag, const char *zType){ |
| 273 | return db_int(0, |
| 274 | "SELECT objid FROM (" |
| 275 | /* Q1: Begin by looking for the tag in the 30 most recent events */ |
| @@ -479,11 +513,11 @@ | |
| 513 | if( rid ) return rid; |
| 514 | } |
| 515 | |
| 516 | /* Date and times */ |
| 517 | if( memcmp(zTag, "date:", 5)==0 ){ |
| 518 | zDate = fossil_expand_datetime(&zTag[5],0,1); |
| 519 | if( zDate==0 ) zDate = &zTag[5]; |
| 520 | rid = db_int(0, |
| 521 | "SELECT objid FROM event" |
| 522 | " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'" |
| 523 | " ORDER BY mtime DESC LIMIT 1", |
| @@ -545,21 +579,21 @@ | |
| 579 | |
| 580 | /* symbolic-name ":" date-time */ |
| 581 | nTag = strlen(zTag); |
| 582 | for(i=0; i<nTag-8 && zTag[i]!=':'; i++){} |
| 583 | if( zTag[i]==':' |
| 584 | && (fossil_isdate(&zTag[i+1]) || fossil_expand_datetime(&zTag[i+1],0,0)!=0) |
| 585 | ){ |
| 586 | char *zDate = mprintf("%s", &zTag[i+1]); |
| 587 | char *zTagBase = mprintf("%.*s", i, zTag); |
| 588 | char *zXDate; |
| 589 | int nDate = strlen(zDate); |
| 590 | if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){ |
| 591 | zDate[nDate-3] = 'z'; |
| 592 | zDate[nDate-2] = 0; |
| 593 | } |
| 594 | zXDate = fossil_expand_datetime(zDate,0,1); |
| 595 | if( zXDate==0 ) zXDate = zDate; |
| 596 | rid = db_int(0, |
| 597 | "SELECT event.objid, max(event.mtime)" |
| 598 | " FROM tag, tagxref, event" |
| 599 | " WHERE tag.tagname='sym-%q' " |
| @@ -631,11 +665,11 @@ | |
| 665 | if( startOfBranch ) rid = start_of_branch(rid,1); |
| 666 | return rid; |
| 667 | } |
| 668 | |
| 669 | /* Pure numeric date/time */ |
| 670 | zDate = fossil_expand_datetime(zTag, 0,1); |
| 671 | if( zDate ){ |
| 672 | rid = db_int(0, |
| 673 | "SELECT objid FROM event" |
| 674 | " WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'" |
| 675 | " ORDER BY mtime DESC LIMIT 1", |
| @@ -684,10 +718,65 @@ | |
| 718 | rid = symbolic_name_to_rid(zNew,zType); |
| 719 | fossil_free(zNew); |
| 720 | } |
| 721 | return rid; |
| 722 | } |
| 723 | |
| 724 | /* |
| 725 | ** Convert a symbolic name used as an argument to the a=, b=, or c= |
| 726 | ** query parameters of timeline into a julianday mtime value. |
| 727 | ** |
| 728 | ** If pzDisplay is not null, then display text for the symbolic name might |
| 729 | ** be written into *pzDisplay. But that is not guaranteed. |
| 730 | ** |
| 731 | ** If bRoundUp is true and the symbolic name is a timestamp with less |
| 732 | ** than millisecond resolution, then the timestamp is rounding up to the |
| 733 | ** largest millisecond consistent with that timestamp. If bRoundUp is |
| 734 | ** false, then the resulting time is obtained by extending the timestamp |
| 735 | ** with zeros (hence rounding down). Use bRoundUp==1 if the result |
| 736 | ** will be used in mtime<=$RESULT and use bRoundUp==0 if the result |
| 737 | ** will be used in mtime>=$RESULT. |
| 738 | */ |
| 739 | double symbolic_name_to_mtime( |
| 740 | const char *z, /* Input symbolic name */ |
| 741 | const char **pzDisplay, /* Perhaps write display text here, if not NULL */ |
| 742 | int bRoundUp /* Round up if true */ |
| 743 | ){ |
| 744 | double mtime; |
| 745 | int rid; |
| 746 | const char *zDate; |
| 747 | if( z==0 ) return -1.0; |
| 748 | if( fossil_isdate(z) ){ |
| 749 | mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", z); |
| 750 | if( mtime>0.0 ) return mtime; |
| 751 | } |
| 752 | zDate = fossil_expand_datetime(z, 1, bRoundUp); |
| 753 | if( zDate!=0 ){ |
| 754 | mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", |
| 755 | bRoundUp ? fossil_roundup_date(zDate) : zDate); |
| 756 | if( mtime>0.0 ){ |
| 757 | if( pzDisplay ){ |
| 758 | zDate = fossil_expand_datetime(z,0,0); |
| 759 | *pzDisplay = fossil_strdup(zDate); |
| 760 | } |
| 761 | return mtime; |
| 762 | } |
| 763 | } |
| 764 | rid = symbolic_name_to_rid(z, "*"); |
| 765 | if( rid ){ |
| 766 | mtime = mtime_of_rid(rid, 0.0); |
| 767 | }else{ |
| 768 | mtime = db_double(-1.0, |
| 769 | "SELECT max(event.mtime) FROM event, tag, tagxref" |
| 770 | " WHERE tag.tagname GLOB 'event-%q*'" |
| 771 | " AND tagxref.tagid=tag.tagid AND tagxref.tagtype" |
| 772 | " AND event.objid=tagxref.rid", |
| 773 | z |
| 774 | ); |
| 775 | } |
| 776 | return mtime; |
| 777 | } |
| 778 | |
| 779 | /* |
| 780 | ** This routine takes a user-entered string and tries to convert it to |
| 781 | ** an artifact hash. |
| 782 | ** |
| 783 |
+98
-5
| --- src/patch.c | ||
| +++ src/patch.c | ||
| @@ -698,10 +698,26 @@ | ||
| 698 | 698 | fossil_fatal("cannot change to directory \"%s\"", zDir); |
| 699 | 699 | } |
| 700 | 700 | fossil_free(zToFree); |
| 701 | 701 | return zPatchFile; |
| 702 | 702 | } |
| 703 | + | |
| 704 | +/* | |
| 705 | +** Resolves a patch-command remote system name, accounting for patch | |
| 706 | +** aliases. | |
| 707 | +** | |
| 708 | +** If a CONFIG table entry matching name='patch-alias:$zKey' is found, | |
| 709 | +** the corresponding value is returned, else a fossil_strdup() of zKey | |
| 710 | +** is returned. The caller is responsible for passing the resulting | |
| 711 | +** string to fossil_free(). | |
| 712 | +*/ | |
| 713 | +static char *patch_resolve_remote(const char *zKey){ | |
| 714 | + char *zAlias = db_text(0, "SELECT value FROM config " | |
| 715 | + "WHERE name = 'patch-alias:%q'", | |
| 716 | + zKey); | |
| 717 | + return zAlias ? zAlias : fossil_strdup(zKey); | |
| 718 | +} | |
| 703 | 719 | |
| 704 | 720 | /* |
| 705 | 721 | ** Create a FILE* that will execute the remote side of a push or pull |
| 706 | 722 | ** using ssh (probably) or fossil for local pushes and pulls. Return |
| 707 | 723 | ** a FILE* obtained from popen() into which we write the patch, or from |
| @@ -729,11 +745,11 @@ | ||
| 729 | 745 | if( mFlags & PATCH_DRYRUN ) blob_appendf(&flgs, " -n"); |
| 730 | 746 | zForce = blob_size(&flgs)>0 ? blob_str(&flgs) : ""; |
| 731 | 747 | if( g.argc!=4 ){ |
| 732 | 748 | usage(mprintf("%s [USER@]HOST:DIRECTORY", zThisCmd)); |
| 733 | 749 | } |
| 734 | - zRemote = fossil_strdup(g.argv[3]); | |
| 750 | + zRemote = patch_resolve_remote(g.argv[3]); | |
| 735 | 751 | zDir = (char*)file_skip_userhost(zRemote); |
| 736 | 752 | if( zDir==0 ){ |
| 737 | 753 | if( isRetry ) goto remote_command_error; |
| 738 | 754 | zDir = zRemote; |
| 739 | 755 | blob_append_escaped_arg(&cmd, g.nameOfExe, 1); |
| @@ -778,11 +794,11 @@ | ||
| 778 | 794 | /* |
| 779 | 795 | ** Toggle the use-path-for-ssh setting for the remote host defined |
| 780 | 796 | ** by g.argv[3]. |
| 781 | 797 | */ |
| 782 | 798 | static void patch_toggle_ssh_needs_path(void){ |
| 783 | - char *zRemote = fossil_strdup(g.argv[3]); | |
| 799 | + char *zRemote = patch_resolve_remote(g.argv[3]); | |
| 784 | 800 | char *zDir = (char*)file_skip_userhost(zRemote); |
| 785 | 801 | if( zDir ){ |
| 786 | 802 | *(char*)(zDir - 1) = 0; |
| 787 | 803 | ssh_needs_path_argument(zRemote, 99); |
| 788 | 804 | } |
| @@ -905,11 +921,10 @@ | ||
| 905 | 921 | db_finalize(&q); |
| 906 | 922 | diff_end(pCfg, nErr); |
| 907 | 923 | if( nErr ) fossil_fatal("abort due to prior errors"); |
| 908 | 924 | } |
| 909 | 925 | |
| 910 | - | |
| 911 | 926 | /* |
| 912 | 927 | ** COMMAND: patch |
| 913 | 928 | ** |
| 914 | 929 | ** Usage: %fossil patch SUBCOMMAND ?ARGS ..? |
| 915 | 930 | ** |
| @@ -916,10 +931,22 @@ | ||
| 916 | 931 | ** This command is used to create, view, and apply Fossil binary patches. |
| 917 | 932 | ** A Fossil binary patch is a single (binary) file that captures all of the |
| 918 | 933 | ** uncommitted changes of a check-out. Use Fossil binary patches to transfer |
| 919 | 934 | ** proposed or incomplete changes between machines for testing or analysis. |
| 920 | 935 | ** |
| 936 | +** > fossil patch alias add|rm|ls|list ?ARGS? | |
| 937 | +** | |
| 938 | +** Manage remote-name aliases, which act as short-form | |
| 939 | +** equivalents to REMOTE-CHECKOUT strings. Aliases are local to | |
| 940 | +** a given repository and do not sync. Subcommands: | |
| 941 | +** | |
| 942 | +** ... add ALIAS REMOTE-CHECKOUT Add ALIAS as an alias | |
| 943 | +** for REMOTE-CHECKOUT. | |
| 944 | +** ... ls|list List all local aliases. | |
| 945 | +** ... rm ALIAS [ALIAS...] Remove named aliases | |
| 946 | +** ... rm --all Remove all aliases | |
| 947 | +** | |
| 921 | 948 | ** > fossil patch create [DIRECTORY] PATCHFILE |
| 922 | 949 | ** |
| 923 | 950 | ** Create a new binary patch in PATCHFILE that captures all uncommitted |
| 924 | 951 | ** changes in the check-out at DIRECTORY, or the current directory if |
| 925 | 952 | ** DIRECTORY is omitted. If PATCHFILE is "-" then the binary patch |
| @@ -996,14 +1023,76 @@ | ||
| 996 | 1023 | void patch_cmd(void){ |
| 997 | 1024 | const char *zCmd; |
| 998 | 1025 | size_t n; |
| 999 | 1026 | if( g.argc<3 ){ |
| 1000 | 1027 | patch_usage: |
| 1001 | - usage("apply|create|diff|gdiff|pull|push|view"); | |
| 1028 | + usage("alias|apply|create|diff|gdiff|pull|push|view"); | |
| 1002 | 1029 | } |
| 1003 | 1030 | zCmd = g.argv[2]; |
| 1004 | 1031 | n = strlen(zCmd); |
| 1032 | + if( strncmp(zCmd, "alias", n)==0 ){ | |
| 1033 | + const char * zArg = g.argc>3 ? g.argv[3] : 0; | |
| 1034 | + db_must_be_within_tree(); | |
| 1035 | + if( 0==zArg ){ | |
| 1036 | + goto usage_patch_alias; | |
| 1037 | + }else if( 0==strcmp("ls",zArg) || 0==strcmp("list",zArg) ){ | |
| 1038 | + /* alias ls|list */ | |
| 1039 | + Stmt q; | |
| 1040 | + int nAlias = 0; | |
| 1041 | + | |
| 1042 | + verify_all_options(); | |
| 1043 | + db_prepare(&q, "SELECT substr(name,13), value FROM config " | |
| 1044 | + "WHERE name GLOB 'patch-alias:*' ORDER BY name"); | |
| 1045 | + while( SQLITE_ROW==db_step(&q) ){ | |
| 1046 | + const char *zName = db_column_text(&q, 0); | |
| 1047 | + const char *zVal = db_column_text(&q, 1); | |
| 1048 | + ++nAlias; | |
| 1049 | + fossil_print("%s = %s\n", zName, zVal); | |
| 1050 | + } | |
| 1051 | + db_finalize(&q); | |
| 1052 | + if( 0==nAlias ){ | |
| 1053 | + fossil_print("No patch aliases defined\n"); | |
| 1054 | + } | |
| 1055 | + }else if( 0==strcmp("add", zArg) ){ | |
| 1056 | + /* alias add localName remote */ | |
| 1057 | + verify_all_options(); | |
| 1058 | + if( 6!=g.argc ){ | |
| 1059 | + usage("alias add localName remote"); | |
| 1060 | + } | |
| 1061 | + db_unprotect(PROTECT_CONFIG); | |
| 1062 | + db_multi_exec("REPLACE INTO config (name, value, mtime) " | |
| 1063 | + "VALUES ('patch-alias:%q', %Q, unixepoch())", | |
| 1064 | + g.argv[4], g.argv[5]); | |
| 1065 | + db_protect_pop(); | |
| 1066 | + }else if( 0==strcmp("rm", zArg) ){ | |
| 1067 | + /* alias rm */ | |
| 1068 | + const int fAll = 0!=find_option("all", 0, 0); | |
| 1069 | + if( fAll ? g.argc<4 : g.argc<5 ){ | |
| 1070 | + usage("alias rm [-all] [aliasGlob [...aliasGlobN]]"); | |
| 1071 | + } | |
| 1072 | + verify_all_options(); | |
| 1073 | + db_unprotect(PROTECT_CONFIG); | |
| 1074 | + if( 0!=fAll ){ | |
| 1075 | + db_multi_exec("DELETE FROM config WHERE name GLOB 'patch-alias:*'"); | |
| 1076 | + }else{ | |
| 1077 | + Stmt q; | |
| 1078 | + int i; | |
| 1079 | + db_prepare(&q, "DELETE FROM config WHERE name " | |
| 1080 | + "GLOB 'patch-alias:' || :pattern"); | |
| 1081 | + for(i = 4; i < g.argc; ++i){ | |
| 1082 | + db_bind_text(&q, ":pattern", g.argv[i]); | |
| 1083 | + db_step(&q); | |
| 1084 | + db_reset(&q); | |
| 1085 | + } | |
| 1086 | + db_finalize(&q); | |
| 1087 | + } | |
| 1088 | + db_protect_pop(); | |
| 1089 | + }else{ | |
| 1090 | + usage_patch_alias: | |
| 1091 | + usage("alias ls|list|add|rm ..."); | |
| 1092 | + } | |
| 1093 | + }else | |
| 1005 | 1094 | if( strncmp(zCmd, "apply", n)==0 ){ |
| 1006 | 1095 | char *zIn; |
| 1007 | 1096 | unsigned flags = 0; |
| 1008 | 1097 | if( find_option("dry-run","n",0) ) flags |= PATCH_DRYRUN; |
| 1009 | 1098 | if( find_option("verbose","v",0) ) flags |= PATCH_VERBOSE; |
| @@ -1033,14 +1122,18 @@ | ||
| 1033 | 1122 | db_close(0); |
| 1034 | 1123 | diff_tk("patch diff", 3); |
| 1035 | 1124 | return; |
| 1036 | 1125 | } |
| 1037 | 1126 | db_find_and_open_repository(0, 0); |
| 1127 | + if( gdiff_using_tk(zCmd[0]=='g') ){ | |
| 1128 | + diff_tk("patch diff", 3); | |
| 1129 | + return; | |
| 1130 | + } | |
| 1038 | 1131 | if( find_option("force","f",0) ) flags |= PATCH_FORCE; |
| 1039 | 1132 | diff_options(&DCfg, zCmd[0]=='g', 0); |
| 1040 | 1133 | verify_all_options(); |
| 1041 | - zIn = patch_find_patch_filename("apply"); | |
| 1134 | + zIn = patch_find_patch_filename("diff"); | |
| 1042 | 1135 | patch_attach(zIn, stdin, 0); |
| 1043 | 1136 | patch_diff(flags, &DCfg); |
| 1044 | 1137 | fossil_free(zIn); |
| 1045 | 1138 | }else |
| 1046 | 1139 | if( strncmp(zCmd, "pull", n)==0 ){ |
| 1047 | 1140 |
| --- src/patch.c | |
| +++ src/patch.c | |
| @@ -698,10 +698,26 @@ | |
| 698 | fossil_fatal("cannot change to directory \"%s\"", zDir); |
| 699 | } |
| 700 | fossil_free(zToFree); |
| 701 | return zPatchFile; |
| 702 | } |
| 703 | |
| 704 | /* |
| 705 | ** Create a FILE* that will execute the remote side of a push or pull |
| 706 | ** using ssh (probably) or fossil for local pushes and pulls. Return |
| 707 | ** a FILE* obtained from popen() into which we write the patch, or from |
| @@ -729,11 +745,11 @@ | |
| 729 | if( mFlags & PATCH_DRYRUN ) blob_appendf(&flgs, " -n"); |
| 730 | zForce = blob_size(&flgs)>0 ? blob_str(&flgs) : ""; |
| 731 | if( g.argc!=4 ){ |
| 732 | usage(mprintf("%s [USER@]HOST:DIRECTORY", zThisCmd)); |
| 733 | } |
| 734 | zRemote = fossil_strdup(g.argv[3]); |
| 735 | zDir = (char*)file_skip_userhost(zRemote); |
| 736 | if( zDir==0 ){ |
| 737 | if( isRetry ) goto remote_command_error; |
| 738 | zDir = zRemote; |
| 739 | blob_append_escaped_arg(&cmd, g.nameOfExe, 1); |
| @@ -778,11 +794,11 @@ | |
| 778 | /* |
| 779 | ** Toggle the use-path-for-ssh setting for the remote host defined |
| 780 | ** by g.argv[3]. |
| 781 | */ |
| 782 | static void patch_toggle_ssh_needs_path(void){ |
| 783 | char *zRemote = fossil_strdup(g.argv[3]); |
| 784 | char *zDir = (char*)file_skip_userhost(zRemote); |
| 785 | if( zDir ){ |
| 786 | *(char*)(zDir - 1) = 0; |
| 787 | ssh_needs_path_argument(zRemote, 99); |
| 788 | } |
| @@ -905,11 +921,10 @@ | |
| 905 | db_finalize(&q); |
| 906 | diff_end(pCfg, nErr); |
| 907 | if( nErr ) fossil_fatal("abort due to prior errors"); |
| 908 | } |
| 909 | |
| 910 | |
| 911 | /* |
| 912 | ** COMMAND: patch |
| 913 | ** |
| 914 | ** Usage: %fossil patch SUBCOMMAND ?ARGS ..? |
| 915 | ** |
| @@ -916,10 +931,22 @@ | |
| 916 | ** This command is used to create, view, and apply Fossil binary patches. |
| 917 | ** A Fossil binary patch is a single (binary) file that captures all of the |
| 918 | ** uncommitted changes of a check-out. Use Fossil binary patches to transfer |
| 919 | ** proposed or incomplete changes between machines for testing or analysis. |
| 920 | ** |
| 921 | ** > fossil patch create [DIRECTORY] PATCHFILE |
| 922 | ** |
| 923 | ** Create a new binary patch in PATCHFILE that captures all uncommitted |
| 924 | ** changes in the check-out at DIRECTORY, or the current directory if |
| 925 | ** DIRECTORY is omitted. If PATCHFILE is "-" then the binary patch |
| @@ -996,14 +1023,76 @@ | |
| 996 | void patch_cmd(void){ |
| 997 | const char *zCmd; |
| 998 | size_t n; |
| 999 | if( g.argc<3 ){ |
| 1000 | patch_usage: |
| 1001 | usage("apply|create|diff|gdiff|pull|push|view"); |
| 1002 | } |
| 1003 | zCmd = g.argv[2]; |
| 1004 | n = strlen(zCmd); |
| 1005 | if( strncmp(zCmd, "apply", n)==0 ){ |
| 1006 | char *zIn; |
| 1007 | unsigned flags = 0; |
| 1008 | if( find_option("dry-run","n",0) ) flags |= PATCH_DRYRUN; |
| 1009 | if( find_option("verbose","v",0) ) flags |= PATCH_VERBOSE; |
| @@ -1033,14 +1122,18 @@ | |
| 1033 | db_close(0); |
| 1034 | diff_tk("patch diff", 3); |
| 1035 | return; |
| 1036 | } |
| 1037 | db_find_and_open_repository(0, 0); |
| 1038 | if( find_option("force","f",0) ) flags |= PATCH_FORCE; |
| 1039 | diff_options(&DCfg, zCmd[0]=='g', 0); |
| 1040 | verify_all_options(); |
| 1041 | zIn = patch_find_patch_filename("apply"); |
| 1042 | patch_attach(zIn, stdin, 0); |
| 1043 | patch_diff(flags, &DCfg); |
| 1044 | fossil_free(zIn); |
| 1045 | }else |
| 1046 | if( strncmp(zCmd, "pull", n)==0 ){ |
| 1047 |
| --- src/patch.c | |
| +++ src/patch.c | |
| @@ -698,10 +698,26 @@ | |
| 698 | fossil_fatal("cannot change to directory \"%s\"", zDir); |
| 699 | } |
| 700 | fossil_free(zToFree); |
| 701 | return zPatchFile; |
| 702 | } |
| 703 | |
| 704 | /* |
| 705 | ** Resolves a patch-command remote system name, accounting for patch |
| 706 | ** aliases. |
| 707 | ** |
| 708 | ** If a CONFIG table entry matching name='patch-alias:$zKey' is found, |
| 709 | ** the corresponding value is returned, else a fossil_strdup() of zKey |
| 710 | ** is returned. The caller is responsible for passing the resulting |
| 711 | ** string to fossil_free(). |
| 712 | */ |
| 713 | static char *patch_resolve_remote(const char *zKey){ |
| 714 | char *zAlias = db_text(0, "SELECT value FROM config " |
| 715 | "WHERE name = 'patch-alias:%q'", |
| 716 | zKey); |
| 717 | return zAlias ? zAlias : fossil_strdup(zKey); |
| 718 | } |
| 719 | |
| 720 | /* |
| 721 | ** Create a FILE* that will execute the remote side of a push or pull |
| 722 | ** using ssh (probably) or fossil for local pushes and pulls. Return |
| 723 | ** a FILE* obtained from popen() into which we write the patch, or from |
| @@ -729,11 +745,11 @@ | |
| 745 | if( mFlags & PATCH_DRYRUN ) blob_appendf(&flgs, " -n"); |
| 746 | zForce = blob_size(&flgs)>0 ? blob_str(&flgs) : ""; |
| 747 | if( g.argc!=4 ){ |
| 748 | usage(mprintf("%s [USER@]HOST:DIRECTORY", zThisCmd)); |
| 749 | } |
| 750 | zRemote = patch_resolve_remote(g.argv[3]); |
| 751 | zDir = (char*)file_skip_userhost(zRemote); |
| 752 | if( zDir==0 ){ |
| 753 | if( isRetry ) goto remote_command_error; |
| 754 | zDir = zRemote; |
| 755 | blob_append_escaped_arg(&cmd, g.nameOfExe, 1); |
| @@ -778,11 +794,11 @@ | |
| 794 | /* |
| 795 | ** Toggle the use-path-for-ssh setting for the remote host defined |
| 796 | ** by g.argv[3]. |
| 797 | */ |
| 798 | static void patch_toggle_ssh_needs_path(void){ |
| 799 | char *zRemote = patch_resolve_remote(g.argv[3]); |
| 800 | char *zDir = (char*)file_skip_userhost(zRemote); |
| 801 | if( zDir ){ |
| 802 | *(char*)(zDir - 1) = 0; |
| 803 | ssh_needs_path_argument(zRemote, 99); |
| 804 | } |
| @@ -905,11 +921,10 @@ | |
| 921 | db_finalize(&q); |
| 922 | diff_end(pCfg, nErr); |
| 923 | if( nErr ) fossil_fatal("abort due to prior errors"); |
| 924 | } |
| 925 | |
| 926 | /* |
| 927 | ** COMMAND: patch |
| 928 | ** |
| 929 | ** Usage: %fossil patch SUBCOMMAND ?ARGS ..? |
| 930 | ** |
| @@ -916,10 +931,22 @@ | |
| 931 | ** This command is used to create, view, and apply Fossil binary patches. |
| 932 | ** A Fossil binary patch is a single (binary) file that captures all of the |
| 933 | ** uncommitted changes of a check-out. Use Fossil binary patches to transfer |
| 934 | ** proposed or incomplete changes between machines for testing or analysis. |
| 935 | ** |
| 936 | ** > fossil patch alias add|rm|ls|list ?ARGS? |
| 937 | ** |
| 938 | ** Manage remote-name aliases, which act as short-form |
| 939 | ** equivalents to REMOTE-CHECKOUT strings. Aliases are local to |
| 940 | ** a given repository and do not sync. Subcommands: |
| 941 | ** |
| 942 | ** ... add ALIAS REMOTE-CHECKOUT Add ALIAS as an alias |
| 943 | ** for REMOTE-CHECKOUT. |
| 944 | ** ... ls|list List all local aliases. |
| 945 | ** ... rm ALIAS [ALIAS...] Remove named aliases |
| 946 | ** ... rm --all Remove all aliases |
| 947 | ** |
| 948 | ** > fossil patch create [DIRECTORY] PATCHFILE |
| 949 | ** |
| 950 | ** Create a new binary patch in PATCHFILE that captures all uncommitted |
| 951 | ** changes in the check-out at DIRECTORY, or the current directory if |
| 952 | ** DIRECTORY is omitted. If PATCHFILE is "-" then the binary patch |
| @@ -996,14 +1023,76 @@ | |
| 1023 | void patch_cmd(void){ |
| 1024 | const char *zCmd; |
| 1025 | size_t n; |
| 1026 | if( g.argc<3 ){ |
| 1027 | patch_usage: |
| 1028 | usage("alias|apply|create|diff|gdiff|pull|push|view"); |
| 1029 | } |
| 1030 | zCmd = g.argv[2]; |
| 1031 | n = strlen(zCmd); |
| 1032 | if( strncmp(zCmd, "alias", n)==0 ){ |
| 1033 | const char * zArg = g.argc>3 ? g.argv[3] : 0; |
| 1034 | db_must_be_within_tree(); |
| 1035 | if( 0==zArg ){ |
| 1036 | goto usage_patch_alias; |
| 1037 | }else if( 0==strcmp("ls",zArg) || 0==strcmp("list",zArg) ){ |
| 1038 | /* alias ls|list */ |
| 1039 | Stmt q; |
| 1040 | int nAlias = 0; |
| 1041 | |
| 1042 | verify_all_options(); |
| 1043 | db_prepare(&q, "SELECT substr(name,13), value FROM config " |
| 1044 | "WHERE name GLOB 'patch-alias:*' ORDER BY name"); |
| 1045 | while( SQLITE_ROW==db_step(&q) ){ |
| 1046 | const char *zName = db_column_text(&q, 0); |
| 1047 | const char *zVal = db_column_text(&q, 1); |
| 1048 | ++nAlias; |
| 1049 | fossil_print("%s = %s\n", zName, zVal); |
| 1050 | } |
| 1051 | db_finalize(&q); |
| 1052 | if( 0==nAlias ){ |
| 1053 | fossil_print("No patch aliases defined\n"); |
| 1054 | } |
| 1055 | }else if( 0==strcmp("add", zArg) ){ |
| 1056 | /* alias add localName remote */ |
| 1057 | verify_all_options(); |
| 1058 | if( 6!=g.argc ){ |
| 1059 | usage("alias add localName remote"); |
| 1060 | } |
| 1061 | db_unprotect(PROTECT_CONFIG); |
| 1062 | db_multi_exec("REPLACE INTO config (name, value, mtime) " |
| 1063 | "VALUES ('patch-alias:%q', %Q, unixepoch())", |
| 1064 | g.argv[4], g.argv[5]); |
| 1065 | db_protect_pop(); |
| 1066 | }else if( 0==strcmp("rm", zArg) ){ |
| 1067 | /* alias rm */ |
| 1068 | const int fAll = 0!=find_option("all", 0, 0); |
| 1069 | if( fAll ? g.argc<4 : g.argc<5 ){ |
| 1070 | usage("alias rm [-all] [aliasGlob [...aliasGlobN]]"); |
| 1071 | } |
| 1072 | verify_all_options(); |
| 1073 | db_unprotect(PROTECT_CONFIG); |
| 1074 | if( 0!=fAll ){ |
| 1075 | db_multi_exec("DELETE FROM config WHERE name GLOB 'patch-alias:*'"); |
| 1076 | }else{ |
| 1077 | Stmt q; |
| 1078 | int i; |
| 1079 | db_prepare(&q, "DELETE FROM config WHERE name " |
| 1080 | "GLOB 'patch-alias:' || :pattern"); |
| 1081 | for(i = 4; i < g.argc; ++i){ |
| 1082 | db_bind_text(&q, ":pattern", g.argv[i]); |
| 1083 | db_step(&q); |
| 1084 | db_reset(&q); |
| 1085 | } |
| 1086 | db_finalize(&q); |
| 1087 | } |
| 1088 | db_protect_pop(); |
| 1089 | }else{ |
| 1090 | usage_patch_alias: |
| 1091 | usage("alias ls|list|add|rm ..."); |
| 1092 | } |
| 1093 | }else |
| 1094 | if( strncmp(zCmd, "apply", n)==0 ){ |
| 1095 | char *zIn; |
| 1096 | unsigned flags = 0; |
| 1097 | if( find_option("dry-run","n",0) ) flags |= PATCH_DRYRUN; |
| 1098 | if( find_option("verbose","v",0) ) flags |= PATCH_VERBOSE; |
| @@ -1033,14 +1122,18 @@ | |
| 1122 | db_close(0); |
| 1123 | diff_tk("patch diff", 3); |
| 1124 | return; |
| 1125 | } |
| 1126 | db_find_and_open_repository(0, 0); |
| 1127 | if( gdiff_using_tk(zCmd[0]=='g') ){ |
| 1128 | diff_tk("patch diff", 3); |
| 1129 | return; |
| 1130 | } |
| 1131 | if( find_option("force","f",0) ) flags |= PATCH_FORCE; |
| 1132 | diff_options(&DCfg, zCmd[0]=='g', 0); |
| 1133 | verify_all_options(); |
| 1134 | zIn = patch_find_patch_filename("diff"); |
| 1135 | patch_attach(zIn, stdin, 0); |
| 1136 | patch_diff(flags, &DCfg); |
| 1137 | fossil_free(zIn); |
| 1138 | }else |
| 1139 | if( strncmp(zCmd, "pull", n)==0 ){ |
| 1140 |
+157
-97
| --- src/path.c | ||
| +++ src/path.c | ||
| @@ -18,40 +18,45 @@ | ||
| 18 | 18 | ** directed acyclic graph (DAG) of check-ins. |
| 19 | 19 | */ |
| 20 | 20 | #include "config.h" |
| 21 | 21 | #include "path.h" |
| 22 | 22 | #include <assert.h> |
| 23 | +#include <math.h> | |
| 23 | 24 | |
| 24 | 25 | #if INTERFACE |
| 25 | 26 | /* Nodes for the paths through the DAG. |
| 26 | 27 | */ |
| 27 | 28 | struct PathNode { |
| 28 | 29 | int rid; /* ID for this node */ |
| 29 | 30 | u8 fromIsParent; /* True if pFrom is the parent of rid */ |
| 30 | 31 | u8 isPrim; /* True if primary side of common ancestor */ |
| 31 | 32 | u8 isHidden; /* Abbreviate output in "fossil bisect ls" */ |
| 33 | + char *zBranch; /* Branch name for this node. Might be NULL */ | |
| 34 | + double mtime; /* Date/time of this check-in */ | |
| 32 | 35 | PathNode *pFrom; /* Node we came from */ |
| 33 | 36 | union { |
| 34 | - PathNode *pPeer; /* List of nodes of the same generation */ | |
| 37 | + double rCost; /* Cost of getting to this node from pStart */ | |
| 35 | 38 | PathNode *pTo; /* Next on path from beginning to end */ |
| 36 | 39 | } u; |
| 37 | - PathNode *pAll; /* List of all nodes */ | |
| 40 | + PathNode *pAll; /* List of all nodes */ | |
| 38 | 41 | }; |
| 39 | 42 | #endif |
| 40 | 43 | |
| 41 | 44 | /* |
| 42 | 45 | ** Local variables for this module |
| 43 | 46 | */ |
| 44 | 47 | static struct { |
| 45 | - PathNode *pCurrent; /* Current generation of nodes */ | |
| 48 | + PQueue pending; /* Nodes pending review for inclusion in the graph */ | |
| 46 | 49 | PathNode *pAll; /* All nodes */ |
| 47 | - Bag seen; /* Nodes seen before */ | |
| 48 | 50 | int nStep; /* Number of steps from first to last */ |
| 49 | 51 | int nNotHidden; /* Number of steps not counting hidden nodes */ |
| 52 | + int brCost; /* Extra cost for moving to a different branch */ | |
| 53 | + int revCost; /* Extra cost for changing directions */ | |
| 50 | 54 | PathNode *pStart; /* Earliest node */ |
| 51 | 55 | PathNode *pEnd; /* Most recent */ |
| 52 | 56 | } path; |
| 57 | +static int path_debug = 0; /* Flag to enable debugging */ | |
| 53 | 58 | |
| 54 | 59 | /* |
| 55 | 60 | ** Return the first (last) element of the computed path. |
| 56 | 61 | */ |
| 57 | 62 | PathNode *path_first(void){ return path.pStart; } |
| @@ -66,25 +71,71 @@ | ||
| 66 | 71 | ** Return the number of non-hidden steps in the computed path. |
| 67 | 72 | */ |
| 68 | 73 | int path_length_not_hidden(void){ return path.nNotHidden; } |
| 69 | 74 | |
| 70 | 75 | /* |
| 71 | -** Create a new node | |
| 76 | +** Used for debugging only. | |
| 77 | +** | |
| 78 | +** Given a RID, return the ISO date/time string and branch for the | |
| 79 | +** corresponding check-in. Memory is held locally and is overwritten | |
| 80 | +** with each call. | |
| 81 | +*/ | |
| 82 | +char *path_rid_desc(int rid){ | |
| 83 | + static Stmt q; | |
| 84 | + static char *zDesc = 0; | |
| 85 | + db_static_prepare(&q, | |
| 86 | + "SELECT concat(strftime('%%Y%%m%%d%%H%%M',event.mtime),'/',value)" | |
| 87 | + " FROM event, tagxref" | |
| 88 | + " WHERE event.objid=:rid" | |
| 89 | + " AND tagxref.rid=:rid" | |
| 90 | + " AND tagxref.tagid=%d" | |
| 91 | + " AND tagxref.tagtype>0", | |
| 92 | + TAG_BRANCH | |
| 93 | + ); | |
| 94 | + fossil_free(zDesc); | |
| 95 | + db_bind_int(&q, ":rid", rid); | |
| 96 | + if( db_step(&q)==SQLITE_ROW ){ | |
| 97 | + zDesc = fossil_strdup(db_column_text(&q,0)); | |
| 98 | + } | |
| 99 | + db_reset(&q); | |
| 100 | + return zDesc ? zDesc : "???"; | |
| 101 | +} | |
| 102 | + | |
| 103 | +/* | |
| 104 | +** Create a new node and insert it into the path.pending queue. | |
| 72 | 105 | */ |
| 73 | 106 | static PathNode *path_new_node(int rid, PathNode *pFrom, int isParent){ |
| 74 | 107 | PathNode *p; |
| 75 | 108 | |
| 76 | 109 | p = fossil_malloc( sizeof(*p) ); |
| 77 | 110 | memset(p, 0, sizeof(*p)); |
| 111 | + p->pAll = path.pAll; | |
| 112 | + path.pAll = p; | |
| 78 | 113 | p->rid = rid; |
| 79 | 114 | p->fromIsParent = isParent; |
| 80 | 115 | p->pFrom = pFrom; |
| 81 | - p->u.pPeer = path.pCurrent; | |
| 82 | - path.pCurrent = p; | |
| 83 | - p->pAll = path.pAll; | |
| 84 | - path.pAll = p; | |
| 85 | - bag_insert(&path.seen, rid); | |
| 116 | + p->u.rCost = pFrom ? pFrom->u.rCost : 0.0; | |
| 117 | + if( path.brCost ){ | |
| 118 | + p->zBranch = branch_of_rid(rid); | |
| 119 | + p->mtime = mtime_of_rid(rid, 0.0); | |
| 120 | + if( pFrom ){ | |
| 121 | + p->u.rCost += fabs(pFrom->mtime - p->mtime); | |
| 122 | + if( fossil_strcmp(p->zBranch, pFrom->zBranch)!=0 ){ | |
| 123 | + p->u.rCost += path.brCost; | |
| 124 | + } | |
| 125 | + } | |
| 126 | + }else{ | |
| 127 | + /* When brCost==0, we try to minimize the number of nodes | |
| 128 | + ** along the path. The cost is just the number of nodes back | |
| 129 | + ** to the start. We do not need to know the branch name nor | |
| 130 | + ** the mtime */ | |
| 131 | + p->u.rCost += 1.0; | |
| 132 | + } | |
| 133 | + if( path_debug ){ | |
| 134 | + fossil_print("PUSH %-50s cost = %g\n", path_rid_desc(p->rid), p->u.rCost); | |
| 135 | + } | |
| 136 | + pqueuex_insert_ptr(&path.pending, (void*)p, p->u.rCost); | |
| 86 | 137 | return p; |
| 87 | 138 | } |
| 88 | 139 | |
| 89 | 140 | /* |
| 90 | 141 | ** Reset memory used by the shortest path algorithm. |
| @@ -92,13 +143,14 @@ | ||
| 92 | 143 | void path_reset(void){ |
| 93 | 144 | PathNode *p; |
| 94 | 145 | while( path.pAll ){ |
| 95 | 146 | p = path.pAll; |
| 96 | 147 | path.pAll = p->pAll; |
| 148 | + fossil_free(p->zBranch); | |
| 97 | 149 | fossil_free(p); |
| 98 | 150 | } |
| 99 | - bag_clear(&path.seen); | |
| 151 | + pqueuex_clear(&path.pending); | |
| 100 | 152 | memset(&path, 0, sizeof(path)); |
| 101 | 153 | } |
| 102 | 154 | |
| 103 | 155 | /* |
| 104 | 156 | ** Construct the path from path.pStart to path.pEnd in the u.pTo fields. |
| @@ -128,17 +180,19 @@ | ||
| 128 | 180 | PathNode *path_shortest( |
| 129 | 181 | int iFrom, /* Path starts here */ |
| 130 | 182 | int iTo, /* Path ends here */ |
| 131 | 183 | int directOnly, /* No merge links if true */ |
| 132 | 184 | int oneWayOnly, /* Parent->child only if true */ |
| 133 | - Bag *pHidden /* Hidden nodes */ | |
| 185 | + Bag *pHidden, /* Hidden nodes */ | |
| 186 | + int branchCost /* Add extra cost to changing branches */ | |
| 134 | 187 | ){ |
| 135 | 188 | Stmt s; |
| 136 | - PathNode *pPrev; | |
| 189 | + Bag seen; | |
| 137 | 190 | PathNode *p; |
| 138 | 191 | |
| 139 | 192 | path_reset(); |
| 193 | + path.brCost = branchCost; | |
| 140 | 194 | path.pStart = path_new_node(iFrom, 0, 0); |
| 141 | 195 | if( iTo==iFrom ){ |
| 142 | 196 | path.pEnd = path.pStart; |
| 143 | 197 | return path.pStart; |
| 144 | 198 | } |
| @@ -152,44 +206,46 @@ | ||
| 152 | 206 | ); |
| 153 | 207 | }else if( directOnly ){ |
| 154 | 208 | db_prepare(&s, |
| 155 | 209 | "SELECT cid, 1 FROM plink WHERE pid=:pid AND isprim " |
| 156 | 210 | "UNION ALL " |
| 157 | - "SELECT pid, 0 FROM plink WHERE cid=:pid AND isprim" | |
| 211 | + "SELECT pid, 0 FROM plink WHERE :back AND cid=:pid AND isprim" | |
| 158 | 212 | ); |
| 159 | 213 | }else{ |
| 160 | 214 | db_prepare(&s, |
| 161 | 215 | "SELECT cid, 1 FROM plink WHERE pid=:pid " |
| 162 | 216 | "UNION ALL " |
| 163 | - "SELECT pid, 0 FROM plink WHERE cid=:pid" | |
| 217 | + "SELECT pid, 0 FROM plink WHERE :back AND cid=:pid" | |
| 164 | 218 | ); |
| 165 | 219 | } |
| 166 | - while( path.pCurrent ){ | |
| 167 | - path.nStep++; | |
| 168 | - pPrev = path.pCurrent; | |
| 169 | - path.pCurrent = 0; | |
| 170 | - while( pPrev ){ | |
| 171 | - db_bind_int(&s, ":pid", pPrev->rid); | |
| 172 | - while( db_step(&s)==SQLITE_ROW ){ | |
| 173 | - int cid = db_column_int(&s, 0); | |
| 174 | - int isParent = db_column_int(&s, 1); | |
| 175 | - if( bag_find(&path.seen, cid) ) continue; | |
| 176 | - p = path_new_node(cid, pPrev, isParent); | |
| 177 | - if( pHidden && bag_find(pHidden,cid) ) p->isHidden = 1; | |
| 178 | - if( cid==iTo ){ | |
| 179 | - db_finalize(&s); | |
| 180 | - path.pEnd = p; | |
| 181 | - path_reverse_path(); | |
| 182 | - for(p=path.pStart->u.pTo; p; p=p->u.pTo ){ | |
| 183 | - if( !p->isHidden ) path.nNotHidden++; | |
| 184 | - } | |
| 185 | - return path.pStart; | |
| 186 | - } | |
| 187 | - } | |
| 188 | - db_reset(&s); | |
| 189 | - pPrev = pPrev->u.pPeer; | |
| 190 | - } | |
| 220 | + bag_init(&seen); | |
| 221 | + while( (p = pqueuex_extract_ptr(&path.pending))!=0 ){ | |
| 222 | + if( path_debug ){ | |
| 223 | + printf("PULL %s %g\n", path_rid_desc(p->rid), p->u.rCost); | |
| 224 | + } | |
| 225 | + if( p->rid==iTo ){ | |
| 226 | + db_finalize(&s); | |
| 227 | + path.pEnd = p; | |
| 228 | + path_reverse_path(); | |
| 229 | + for(p=path.pStart->u.pTo; p; p=p->u.pTo ){ | |
| 230 | + if( !p->isHidden ) path.nNotHidden++; | |
| 231 | + } | |
| 232 | + return path.pStart; | |
| 233 | + } | |
| 234 | + if( bag_find(&seen, p->rid) ) continue; | |
| 235 | + bag_insert(&seen, p->rid); | |
| 236 | + db_bind_int(&s, ":pid", p->rid); | |
| 237 | + if( !oneWayOnly ) db_bind_int(&s, ":back", !p->fromIsParent); | |
| 238 | + while( db_step(&s)==SQLITE_ROW ){ | |
| 239 | + int cid = db_column_int(&s, 0); | |
| 240 | + int isParent = db_column_int(&s, 1); | |
| 241 | + PathNode *pNew; | |
| 242 | + if( bag_find(&seen, cid) ) continue; | |
| 243 | + pNew = path_new_node(cid, p, isParent); | |
| 244 | + if( pHidden && bag_find(pHidden,cid) ) pNew->isHidden = 1; | |
| 245 | + } | |
| 246 | + db_reset(&s); | |
| 191 | 247 | } |
| 192 | 248 | db_finalize(&s); |
| 193 | 249 | path_reset(); |
| 194 | 250 | return 0; |
| 195 | 251 | } |
| @@ -215,10 +271,22 @@ | ||
| 215 | 271 | PathNode *p; |
| 216 | 272 | p = path.pStart; |
| 217 | 273 | if( p ) p = p->u.pTo; |
| 218 | 274 | return p; |
| 219 | 275 | } |
| 276 | + | |
| 277 | +/* | |
| 278 | +** Return the branch for a path node. | |
| 279 | +** | |
| 280 | +** Storage space is managed by the path subsystem. The returned value | |
| 281 | +** is valid until the path is reset. | |
| 282 | +*/ | |
| 283 | +const char *path_branch(PathNode *p){ | |
| 284 | + if( p==0 ) return 0; | |
| 285 | + if( p->zBranch==0 ) p->zBranch = branch_of_rid(p->rid); | |
| 286 | + return p->zBranch; | |
| 287 | +} | |
| 220 | 288 | |
| 221 | 289 | /* |
| 222 | 290 | ** Return an estimate of the number of comparisons remaining in order |
| 223 | 291 | ** to bisect path. This is based on the log2() of path.nStep. |
| 224 | 292 | */ |
| @@ -238,11 +306,11 @@ | ||
| 238 | 306 | int cid /* RID for check-in at the end of the path */ |
| 239 | 307 | ){ |
| 240 | 308 | PathNode *pPath; |
| 241 | 309 | int gen = 0; |
| 242 | 310 | Stmt ins; |
| 243 | - pPath = path_shortest(cid, origid, 1, 0, 0); | |
| 311 | + pPath = path_shortest(cid, origid, 1, 0, 0, 0); | |
| 244 | 312 | db_multi_exec( |
| 245 | 313 | "CREATE TEMP TABLE IF NOT EXISTS ancestor(" |
| 246 | 314 | " rid INT UNIQUE," |
| 247 | 315 | " generation INTEGER PRIMARY KEY" |
| 248 | 316 | ");" |
| @@ -261,58 +329,55 @@ | ||
| 261 | 329 | } |
| 262 | 330 | |
| 263 | 331 | /* |
| 264 | 332 | ** COMMAND: test-shortest-path |
| 265 | 333 | ** |
| 266 | -** Usage: %fossil test-shortest-path ?--no-merge? VERSION1 VERSION2 | |
| 334 | +** Usage: %fossil test-shortest-path [OPTIONS] VERSION1 VERSION2 | |
| 335 | +** | |
| 336 | +** Report the shortest path between two check-ins. Options: | |
| 267 | 337 | ** |
| 268 | -** Report the shortest path between two check-ins. If the --no-merge flag | |
| 269 | -** is used, follow only direct parent-child paths and omit merge links. | |
| 338 | +** --branch-cost N Additional cost N for changing branches | |
| 339 | +** --debug Show debugging output | |
| 340 | +** --one-way One-way forwards in time, parent->child only | |
| 341 | +** --no-merge Follow only direct parent-child paths and omit | |
| 342 | +** merge links. | |
| 270 | 343 | */ |
| 271 | 344 | void shortest_path_test_cmd(void){ |
| 272 | 345 | int iFrom; |
| 273 | 346 | int iTo; |
| 274 | 347 | PathNode *p; |
| 275 | 348 | int n; |
| 276 | 349 | int directOnly; |
| 277 | 350 | int oneWay; |
| 351 | + const char *zBrCost; | |
| 278 | 352 | |
| 279 | 353 | db_find_and_open_repository(0,0); |
| 280 | 354 | directOnly = find_option("no-merge",0,0)!=0; |
| 281 | 355 | oneWay = find_option("one-way",0,0)!=0; |
| 356 | + zBrCost = find_option("branch-cost",0,1); | |
| 357 | + if( find_option("debug",0,0)!=0 ) path_debug = 1; | |
| 282 | 358 | if( g.argc!=4 ) usage("VERSION1 VERSION2"); |
| 283 | 359 | iFrom = name_to_rid(g.argv[2]); |
| 284 | 360 | iTo = name_to_rid(g.argv[3]); |
| 285 | - p = path_shortest(iFrom, iTo, directOnly, oneWay, 0); | |
| 361 | + p = path_shortest(iFrom, iTo, directOnly, oneWay, 0, | |
| 362 | + zBrCost ? atoi(zBrCost) : 0); | |
| 286 | 363 | if( p==0 ){ |
| 287 | 364 | fossil_fatal("no path from %s to %s", g.argv[1], g.argv[2]); |
| 288 | 365 | } |
| 289 | 366 | for(n=1, p=path.pStart; p; p=p->u.pTo, n++){ |
| 290 | - char *z; | |
| 291 | - z = db_text(0, | |
| 292 | - "SELECT substr(uuid,1,12) || ' ' || datetime(mtime)" | |
| 293 | - " FROM blob, event" | |
| 294 | - " WHERE blob.rid=%d AND event.objid=%d AND event.type='ci'", | |
| 295 | - p->rid, p->rid); | |
| 296 | - fossil_print("%4d: %5d %s", n, p->rid, z); | |
| 297 | - fossil_free(z); | |
| 298 | - if( p->u.pTo ){ | |
| 299 | - fossil_print(" is a %s of\n", | |
| 300 | - p->u.pTo->fromIsParent ? "parent" : "child"); | |
| 301 | - }else{ | |
| 302 | - fossil_print("\n"); | |
| 303 | - } | |
| 304 | - } | |
| 367 | + fossil_print("%4d: %s\n", n, path_rid_desc(p->rid)); | |
| 368 | + } | |
| 369 | + path_debug = 0; | |
| 305 | 370 | } |
| 306 | 371 | |
| 307 | 372 | /* |
| 308 | 373 | ** Find the closest common ancestor of two nodes. "Closest" means the |
| 309 | 374 | ** fewest number of arcs. |
| 310 | 375 | */ |
| 311 | 376 | int path_common_ancestor(int iMe, int iYou){ |
| 312 | 377 | Stmt s; |
| 313 | - PathNode *pPrev; | |
| 378 | + PathNode *pThis; | |
| 314 | 379 | PathNode *p; |
| 315 | 380 | Bag me, you; |
| 316 | 381 | |
| 317 | 382 | if( iMe==iYou ) return iMe; |
| 318 | 383 | if( iMe==0 || iYou==0 ) return 0; |
| @@ -323,45 +388,40 @@ | ||
| 323 | 388 | db_prepare(&s, "SELECT pid FROM plink WHERE cid=:cid"); |
| 324 | 389 | bag_init(&me); |
| 325 | 390 | bag_insert(&me, iMe); |
| 326 | 391 | bag_init(&you); |
| 327 | 392 | bag_insert(&you, iYou); |
| 328 | - while( path.pCurrent ){ | |
| 329 | - pPrev = path.pCurrent; | |
| 330 | - path.pCurrent = 0; | |
| 331 | - while( pPrev ){ | |
| 332 | - db_bind_int(&s, ":cid", pPrev->rid); | |
| 333 | - while( db_step(&s)==SQLITE_ROW ){ | |
| 334 | - int pid = db_column_int(&s, 0); | |
| 335 | - if( bag_find(pPrev->isPrim ? &you : &me, pid) ){ | |
| 336 | - /* pid is the common ancestor */ | |
| 337 | - PathNode *pNext; | |
| 338 | - for(p=path.pAll; p && p->rid!=pid; p=p->pAll){} | |
| 339 | - assert( p!=0 ); | |
| 340 | - pNext = p; | |
| 341 | - while( pNext ){ | |
| 342 | - pNext = p->pFrom; | |
| 343 | - p->pFrom = pPrev; | |
| 344 | - pPrev = p; | |
| 345 | - p = pNext; | |
| 346 | - } | |
| 347 | - if( pPrev==path.pStart ) path.pStart = path.pEnd; | |
| 348 | - path.pEnd = pPrev; | |
| 349 | - path_reverse_path(); | |
| 350 | - db_finalize(&s); | |
| 351 | - return pid; | |
| 352 | - }else if( bag_find(&path.seen, pid) ){ | |
| 353 | - /* pid is just an alternative path on one of the legs */ | |
| 354 | - continue; | |
| 355 | - } | |
| 356 | - p = path_new_node(pid, pPrev, 0); | |
| 357 | - p->isPrim = pPrev->isPrim; | |
| 358 | - bag_insert(pPrev->isPrim ? &me : &you, pid); | |
| 359 | - } | |
| 360 | - db_reset(&s); | |
| 361 | - pPrev = pPrev->u.pPeer; | |
| 362 | - } | |
| 393 | + while( (pThis = pqueuex_extract_ptr(&path.pending))!=0 ){ | |
| 394 | + db_bind_int(&s, ":cid", pThis->rid); | |
| 395 | + while( db_step(&s)==SQLITE_ROW ){ | |
| 396 | + int pid = db_column_int(&s, 0); | |
| 397 | + if( bag_find(pThis->isPrim ? &you : &me, pid) ){ | |
| 398 | + /* pid is the common ancestor */ | |
| 399 | + PathNode *pNext; | |
| 400 | + for(p=path.pAll; p && p->rid!=pid; p=p->pAll){} | |
| 401 | + assert( p!=0 ); | |
| 402 | + pNext = p; | |
| 403 | + while( pNext ){ | |
| 404 | + pNext = p->pFrom; | |
| 405 | + p->pFrom = pThis; | |
| 406 | + pThis = p; | |
| 407 | + p = pNext; | |
| 408 | + } | |
| 409 | + if( pThis==path.pStart ) path.pStart = path.pEnd; | |
| 410 | + path.pEnd = pThis; | |
| 411 | + path_reverse_path(); | |
| 412 | + db_finalize(&s); | |
| 413 | + return pid; | |
| 414 | + }else if( bag_find(pThis->isPrim ? &me : &you, pid) ){ | |
| 415 | + /* pid is just an alternative path to a node we've already visited */ | |
| 416 | + continue; | |
| 417 | + } | |
| 418 | + p = path_new_node(pid, pThis, 0); | |
| 419 | + p->isPrim = pThis->isPrim; | |
| 420 | + bag_insert(pThis->isPrim ? &me : &you, pid); | |
| 421 | + } | |
| 422 | + db_reset(&s); | |
| 363 | 423 | } |
| 364 | 424 | db_finalize(&s); |
| 365 | 425 | path_reset(); |
| 366 | 426 | return 0; |
| 367 | 427 | } |
| @@ -453,11 +513,11 @@ | ||
| 453 | 513 | }else if(0==iTo){ |
| 454 | 514 | fossil_fatal("Invalid 'to' RID: 0"); |
| 455 | 515 | } |
| 456 | 516 | if( iFrom==iTo ) return; |
| 457 | 517 | path_reset(); |
| 458 | - p = path_shortest(iFrom, iTo, 1, revOK==0, 0); | |
| 518 | + p = path_shortest(iFrom, iTo, 1, revOK==0, 0, 0); | |
| 459 | 519 | if( p==0 ) return; |
| 460 | 520 | path_reverse_path(); |
| 461 | 521 | db_prepare(&q1, |
| 462 | 522 | "SELECT pfnid, fnid FROM mlink" |
| 463 | 523 | " WHERE mid=:mid AND (pfnid>0 OR fid==0)" |
| 464 | 524 |
| --- src/path.c | |
| +++ src/path.c | |
| @@ -18,40 +18,45 @@ | |
| 18 | ** directed acyclic graph (DAG) of check-ins. |
| 19 | */ |
| 20 | #include "config.h" |
| 21 | #include "path.h" |
| 22 | #include <assert.h> |
| 23 | |
| 24 | #if INTERFACE |
| 25 | /* Nodes for the paths through the DAG. |
| 26 | */ |
| 27 | struct PathNode { |
| 28 | int rid; /* ID for this node */ |
| 29 | u8 fromIsParent; /* True if pFrom is the parent of rid */ |
| 30 | u8 isPrim; /* True if primary side of common ancestor */ |
| 31 | u8 isHidden; /* Abbreviate output in "fossil bisect ls" */ |
| 32 | PathNode *pFrom; /* Node we came from */ |
| 33 | union { |
| 34 | PathNode *pPeer; /* List of nodes of the same generation */ |
| 35 | PathNode *pTo; /* Next on path from beginning to end */ |
| 36 | } u; |
| 37 | PathNode *pAll; /* List of all nodes */ |
| 38 | }; |
| 39 | #endif |
| 40 | |
| 41 | /* |
| 42 | ** Local variables for this module |
| 43 | */ |
| 44 | static struct { |
| 45 | PathNode *pCurrent; /* Current generation of nodes */ |
| 46 | PathNode *pAll; /* All nodes */ |
| 47 | Bag seen; /* Nodes seen before */ |
| 48 | int nStep; /* Number of steps from first to last */ |
| 49 | int nNotHidden; /* Number of steps not counting hidden nodes */ |
| 50 | PathNode *pStart; /* Earliest node */ |
| 51 | PathNode *pEnd; /* Most recent */ |
| 52 | } path; |
| 53 | |
| 54 | /* |
| 55 | ** Return the first (last) element of the computed path. |
| 56 | */ |
| 57 | PathNode *path_first(void){ return path.pStart; } |
| @@ -66,25 +71,71 @@ | |
| 66 | ** Return the number of non-hidden steps in the computed path. |
| 67 | */ |
| 68 | int path_length_not_hidden(void){ return path.nNotHidden; } |
| 69 | |
| 70 | /* |
| 71 | ** Create a new node |
| 72 | */ |
| 73 | static PathNode *path_new_node(int rid, PathNode *pFrom, int isParent){ |
| 74 | PathNode *p; |
| 75 | |
| 76 | p = fossil_malloc( sizeof(*p) ); |
| 77 | memset(p, 0, sizeof(*p)); |
| 78 | p->rid = rid; |
| 79 | p->fromIsParent = isParent; |
| 80 | p->pFrom = pFrom; |
| 81 | p->u.pPeer = path.pCurrent; |
| 82 | path.pCurrent = p; |
| 83 | p->pAll = path.pAll; |
| 84 | path.pAll = p; |
| 85 | bag_insert(&path.seen, rid); |
| 86 | return p; |
| 87 | } |
| 88 | |
| 89 | /* |
| 90 | ** Reset memory used by the shortest path algorithm. |
| @@ -92,13 +143,14 @@ | |
| 92 | void path_reset(void){ |
| 93 | PathNode *p; |
| 94 | while( path.pAll ){ |
| 95 | p = path.pAll; |
| 96 | path.pAll = p->pAll; |
| 97 | fossil_free(p); |
| 98 | } |
| 99 | bag_clear(&path.seen); |
| 100 | memset(&path, 0, sizeof(path)); |
| 101 | } |
| 102 | |
| 103 | /* |
| 104 | ** Construct the path from path.pStart to path.pEnd in the u.pTo fields. |
| @@ -128,17 +180,19 @@ | |
| 128 | PathNode *path_shortest( |
| 129 | int iFrom, /* Path starts here */ |
| 130 | int iTo, /* Path ends here */ |
| 131 | int directOnly, /* No merge links if true */ |
| 132 | int oneWayOnly, /* Parent->child only if true */ |
| 133 | Bag *pHidden /* Hidden nodes */ |
| 134 | ){ |
| 135 | Stmt s; |
| 136 | PathNode *pPrev; |
| 137 | PathNode *p; |
| 138 | |
| 139 | path_reset(); |
| 140 | path.pStart = path_new_node(iFrom, 0, 0); |
| 141 | if( iTo==iFrom ){ |
| 142 | path.pEnd = path.pStart; |
| 143 | return path.pStart; |
| 144 | } |
| @@ -152,44 +206,46 @@ | |
| 152 | ); |
| 153 | }else if( directOnly ){ |
| 154 | db_prepare(&s, |
| 155 | "SELECT cid, 1 FROM plink WHERE pid=:pid AND isprim " |
| 156 | "UNION ALL " |
| 157 | "SELECT pid, 0 FROM plink WHERE cid=:pid AND isprim" |
| 158 | ); |
| 159 | }else{ |
| 160 | db_prepare(&s, |
| 161 | "SELECT cid, 1 FROM plink WHERE pid=:pid " |
| 162 | "UNION ALL " |
| 163 | "SELECT pid, 0 FROM plink WHERE cid=:pid" |
| 164 | ); |
| 165 | } |
| 166 | while( path.pCurrent ){ |
| 167 | path.nStep++; |
| 168 | pPrev = path.pCurrent; |
| 169 | path.pCurrent = 0; |
| 170 | while( pPrev ){ |
| 171 | db_bind_int(&s, ":pid", pPrev->rid); |
| 172 | while( db_step(&s)==SQLITE_ROW ){ |
| 173 | int cid = db_column_int(&s, 0); |
| 174 | int isParent = db_column_int(&s, 1); |
| 175 | if( bag_find(&path.seen, cid) ) continue; |
| 176 | p = path_new_node(cid, pPrev, isParent); |
| 177 | if( pHidden && bag_find(pHidden,cid) ) p->isHidden = 1; |
| 178 | if( cid==iTo ){ |
| 179 | db_finalize(&s); |
| 180 | path.pEnd = p; |
| 181 | path_reverse_path(); |
| 182 | for(p=path.pStart->u.pTo; p; p=p->u.pTo ){ |
| 183 | if( !p->isHidden ) path.nNotHidden++; |
| 184 | } |
| 185 | return path.pStart; |
| 186 | } |
| 187 | } |
| 188 | db_reset(&s); |
| 189 | pPrev = pPrev->u.pPeer; |
| 190 | } |
| 191 | } |
| 192 | db_finalize(&s); |
| 193 | path_reset(); |
| 194 | return 0; |
| 195 | } |
| @@ -215,10 +271,22 @@ | |
| 215 | PathNode *p; |
| 216 | p = path.pStart; |
| 217 | if( p ) p = p->u.pTo; |
| 218 | return p; |
| 219 | } |
| 220 | |
| 221 | /* |
| 222 | ** Return an estimate of the number of comparisons remaining in order |
| 223 | ** to bisect path. This is based on the log2() of path.nStep. |
| 224 | */ |
| @@ -238,11 +306,11 @@ | |
| 238 | int cid /* RID for check-in at the end of the path */ |
| 239 | ){ |
| 240 | PathNode *pPath; |
| 241 | int gen = 0; |
| 242 | Stmt ins; |
| 243 | pPath = path_shortest(cid, origid, 1, 0, 0); |
| 244 | db_multi_exec( |
| 245 | "CREATE TEMP TABLE IF NOT EXISTS ancestor(" |
| 246 | " rid INT UNIQUE," |
| 247 | " generation INTEGER PRIMARY KEY" |
| 248 | ");" |
| @@ -261,58 +329,55 @@ | |
| 261 | } |
| 262 | |
| 263 | /* |
| 264 | ** COMMAND: test-shortest-path |
| 265 | ** |
| 266 | ** Usage: %fossil test-shortest-path ?--no-merge? VERSION1 VERSION2 |
| 267 | ** |
| 268 | ** Report the shortest path between two check-ins. If the --no-merge flag |
| 269 | ** is used, follow only direct parent-child paths and omit merge links. |
| 270 | */ |
| 271 | void shortest_path_test_cmd(void){ |
| 272 | int iFrom; |
| 273 | int iTo; |
| 274 | PathNode *p; |
| 275 | int n; |
| 276 | int directOnly; |
| 277 | int oneWay; |
| 278 | |
| 279 | db_find_and_open_repository(0,0); |
| 280 | directOnly = find_option("no-merge",0,0)!=0; |
| 281 | oneWay = find_option("one-way",0,0)!=0; |
| 282 | if( g.argc!=4 ) usage("VERSION1 VERSION2"); |
| 283 | iFrom = name_to_rid(g.argv[2]); |
| 284 | iTo = name_to_rid(g.argv[3]); |
| 285 | p = path_shortest(iFrom, iTo, directOnly, oneWay, 0); |
| 286 | if( p==0 ){ |
| 287 | fossil_fatal("no path from %s to %s", g.argv[1], g.argv[2]); |
| 288 | } |
| 289 | for(n=1, p=path.pStart; p; p=p->u.pTo, n++){ |
| 290 | char *z; |
| 291 | z = db_text(0, |
| 292 | "SELECT substr(uuid,1,12) || ' ' || datetime(mtime)" |
| 293 | " FROM blob, event" |
| 294 | " WHERE blob.rid=%d AND event.objid=%d AND event.type='ci'", |
| 295 | p->rid, p->rid); |
| 296 | fossil_print("%4d: %5d %s", n, p->rid, z); |
| 297 | fossil_free(z); |
| 298 | if( p->u.pTo ){ |
| 299 | fossil_print(" is a %s of\n", |
| 300 | p->u.pTo->fromIsParent ? "parent" : "child"); |
| 301 | }else{ |
| 302 | fossil_print("\n"); |
| 303 | } |
| 304 | } |
| 305 | } |
| 306 | |
| 307 | /* |
| 308 | ** Find the closest common ancestor of two nodes. "Closest" means the |
| 309 | ** fewest number of arcs. |
| 310 | */ |
| 311 | int path_common_ancestor(int iMe, int iYou){ |
| 312 | Stmt s; |
| 313 | PathNode *pPrev; |
| 314 | PathNode *p; |
| 315 | Bag me, you; |
| 316 | |
| 317 | if( iMe==iYou ) return iMe; |
| 318 | if( iMe==0 || iYou==0 ) return 0; |
| @@ -323,45 +388,40 @@ | |
| 323 | db_prepare(&s, "SELECT pid FROM plink WHERE cid=:cid"); |
| 324 | bag_init(&me); |
| 325 | bag_insert(&me, iMe); |
| 326 | bag_init(&you); |
| 327 | bag_insert(&you, iYou); |
| 328 | while( path.pCurrent ){ |
| 329 | pPrev = path.pCurrent; |
| 330 | path.pCurrent = 0; |
| 331 | while( pPrev ){ |
| 332 | db_bind_int(&s, ":cid", pPrev->rid); |
| 333 | while( db_step(&s)==SQLITE_ROW ){ |
| 334 | int pid = db_column_int(&s, 0); |
| 335 | if( bag_find(pPrev->isPrim ? &you : &me, pid) ){ |
| 336 | /* pid is the common ancestor */ |
| 337 | PathNode *pNext; |
| 338 | for(p=path.pAll; p && p->rid!=pid; p=p->pAll){} |
| 339 | assert( p!=0 ); |
| 340 | pNext = p; |
| 341 | while( pNext ){ |
| 342 | pNext = p->pFrom; |
| 343 | p->pFrom = pPrev; |
| 344 | pPrev = p; |
| 345 | p = pNext; |
| 346 | } |
| 347 | if( pPrev==path.pStart ) path.pStart = path.pEnd; |
| 348 | path.pEnd = pPrev; |
| 349 | path_reverse_path(); |
| 350 | db_finalize(&s); |
| 351 | return pid; |
| 352 | }else if( bag_find(&path.seen, pid) ){ |
| 353 | /* pid is just an alternative path on one of the legs */ |
| 354 | continue; |
| 355 | } |
| 356 | p = path_new_node(pid, pPrev, 0); |
| 357 | p->isPrim = pPrev->isPrim; |
| 358 | bag_insert(pPrev->isPrim ? &me : &you, pid); |
| 359 | } |
| 360 | db_reset(&s); |
| 361 | pPrev = pPrev->u.pPeer; |
| 362 | } |
| 363 | } |
| 364 | db_finalize(&s); |
| 365 | path_reset(); |
| 366 | return 0; |
| 367 | } |
| @@ -453,11 +513,11 @@ | |
| 453 | }else if(0==iTo){ |
| 454 | fossil_fatal("Invalid 'to' RID: 0"); |
| 455 | } |
| 456 | if( iFrom==iTo ) return; |
| 457 | path_reset(); |
| 458 | p = path_shortest(iFrom, iTo, 1, revOK==0, 0); |
| 459 | if( p==0 ) return; |
| 460 | path_reverse_path(); |
| 461 | db_prepare(&q1, |
| 462 | "SELECT pfnid, fnid FROM mlink" |
| 463 | " WHERE mid=:mid AND (pfnid>0 OR fid==0)" |
| 464 |
| --- src/path.c | |
| +++ src/path.c | |
| @@ -18,40 +18,45 @@ | |
| 18 | ** directed acyclic graph (DAG) of check-ins. |
| 19 | */ |
| 20 | #include "config.h" |
| 21 | #include "path.h" |
| 22 | #include <assert.h> |
| 23 | #include <math.h> |
| 24 | |
| 25 | #if INTERFACE |
| 26 | /* Nodes for the paths through the DAG. |
| 27 | */ |
| 28 | struct PathNode { |
| 29 | int rid; /* ID for this node */ |
| 30 | u8 fromIsParent; /* True if pFrom is the parent of rid */ |
| 31 | u8 isPrim; /* True if primary side of common ancestor */ |
| 32 | u8 isHidden; /* Abbreviate output in "fossil bisect ls" */ |
| 33 | char *zBranch; /* Branch name for this node. Might be NULL */ |
| 34 | double mtime; /* Date/time of this check-in */ |
| 35 | PathNode *pFrom; /* Node we came from */ |
| 36 | union { |
| 37 | double rCost; /* Cost of getting to this node from pStart */ |
| 38 | PathNode *pTo; /* Next on path from beginning to end */ |
| 39 | } u; |
| 40 | PathNode *pAll; /* List of all nodes */ |
| 41 | }; |
| 42 | #endif |
| 43 | |
| 44 | /* |
| 45 | ** Local variables for this module |
| 46 | */ |
| 47 | static struct { |
| 48 | PQueue pending; /* Nodes pending review for inclusion in the graph */ |
| 49 | PathNode *pAll; /* All nodes */ |
| 50 | int nStep; /* Number of steps from first to last */ |
| 51 | int nNotHidden; /* Number of steps not counting hidden nodes */ |
| 52 | int brCost; /* Extra cost for moving to a different branch */ |
| 53 | int revCost; /* Extra cost for changing directions */ |
| 54 | PathNode *pStart; /* Earliest node */ |
| 55 | PathNode *pEnd; /* Most recent */ |
| 56 | } path; |
| 57 | static int path_debug = 0; /* Flag to enable debugging */ |
| 58 | |
| 59 | /* |
| 60 | ** Return the first (last) element of the computed path. |
| 61 | */ |
| 62 | PathNode *path_first(void){ return path.pStart; } |
| @@ -66,25 +71,71 @@ | |
| 71 | ** Return the number of non-hidden steps in the computed path. |
| 72 | */ |
| 73 | int path_length_not_hidden(void){ return path.nNotHidden; } |
| 74 | |
| 75 | /* |
| 76 | ** Used for debugging only. |
| 77 | ** |
| 78 | ** Given a RID, return the ISO date/time string and branch for the |
| 79 | ** corresponding check-in. Memory is held locally and is overwritten |
| 80 | ** with each call. |
| 81 | */ |
| 82 | char *path_rid_desc(int rid){ |
| 83 | static Stmt q; |
| 84 | static char *zDesc = 0; |
| 85 | db_static_prepare(&q, |
| 86 | "SELECT concat(strftime('%%Y%%m%%d%%H%%M',event.mtime),'/',value)" |
| 87 | " FROM event, tagxref" |
| 88 | " WHERE event.objid=:rid" |
| 89 | " AND tagxref.rid=:rid" |
| 90 | " AND tagxref.tagid=%d" |
| 91 | " AND tagxref.tagtype>0", |
| 92 | TAG_BRANCH |
| 93 | ); |
| 94 | fossil_free(zDesc); |
| 95 | db_bind_int(&q, ":rid", rid); |
| 96 | if( db_step(&q)==SQLITE_ROW ){ |
| 97 | zDesc = fossil_strdup(db_column_text(&q,0)); |
| 98 | } |
| 99 | db_reset(&q); |
| 100 | return zDesc ? zDesc : "???"; |
| 101 | } |
| 102 | |
| 103 | /* |
| 104 | ** Create a new node and insert it into the path.pending queue. |
| 105 | */ |
| 106 | static PathNode *path_new_node(int rid, PathNode *pFrom, int isParent){ |
| 107 | PathNode *p; |
| 108 | |
| 109 | p = fossil_malloc( sizeof(*p) ); |
| 110 | memset(p, 0, sizeof(*p)); |
| 111 | p->pAll = path.pAll; |
| 112 | path.pAll = p; |
| 113 | p->rid = rid; |
| 114 | p->fromIsParent = isParent; |
| 115 | p->pFrom = pFrom; |
| 116 | p->u.rCost = pFrom ? pFrom->u.rCost : 0.0; |
| 117 | if( path.brCost ){ |
| 118 | p->zBranch = branch_of_rid(rid); |
| 119 | p->mtime = mtime_of_rid(rid, 0.0); |
| 120 | if( pFrom ){ |
| 121 | p->u.rCost += fabs(pFrom->mtime - p->mtime); |
| 122 | if( fossil_strcmp(p->zBranch, pFrom->zBranch)!=0 ){ |
| 123 | p->u.rCost += path.brCost; |
| 124 | } |
| 125 | } |
| 126 | }else{ |
| 127 | /* When brCost==0, we try to minimize the number of nodes |
| 128 | ** along the path. The cost is just the number of nodes back |
| 129 | ** to the start. We do not need to know the branch name nor |
| 130 | ** the mtime */ |
| 131 | p->u.rCost += 1.0; |
| 132 | } |
| 133 | if( path_debug ){ |
| 134 | fossil_print("PUSH %-50s cost = %g\n", path_rid_desc(p->rid), p->u.rCost); |
| 135 | } |
| 136 | pqueuex_insert_ptr(&path.pending, (void*)p, p->u.rCost); |
| 137 | return p; |
| 138 | } |
| 139 | |
| 140 | /* |
| 141 | ** Reset memory used by the shortest path algorithm. |
| @@ -92,13 +143,14 @@ | |
| 143 | void path_reset(void){ |
| 144 | PathNode *p; |
| 145 | while( path.pAll ){ |
| 146 | p = path.pAll; |
| 147 | path.pAll = p->pAll; |
| 148 | fossil_free(p->zBranch); |
| 149 | fossil_free(p); |
| 150 | } |
| 151 | pqueuex_clear(&path.pending); |
| 152 | memset(&path, 0, sizeof(path)); |
| 153 | } |
| 154 | |
| 155 | /* |
| 156 | ** Construct the path from path.pStart to path.pEnd in the u.pTo fields. |
| @@ -128,17 +180,19 @@ | |
| 180 | PathNode *path_shortest( |
| 181 | int iFrom, /* Path starts here */ |
| 182 | int iTo, /* Path ends here */ |
| 183 | int directOnly, /* No merge links if true */ |
| 184 | int oneWayOnly, /* Parent->child only if true */ |
| 185 | Bag *pHidden, /* Hidden nodes */ |
| 186 | int branchCost /* Add extra cost to changing branches */ |
| 187 | ){ |
| 188 | Stmt s; |
| 189 | Bag seen; |
| 190 | PathNode *p; |
| 191 | |
| 192 | path_reset(); |
| 193 | path.brCost = branchCost; |
| 194 | path.pStart = path_new_node(iFrom, 0, 0); |
| 195 | if( iTo==iFrom ){ |
| 196 | path.pEnd = path.pStart; |
| 197 | return path.pStart; |
| 198 | } |
| @@ -152,44 +206,46 @@ | |
| 206 | ); |
| 207 | }else if( directOnly ){ |
| 208 | db_prepare(&s, |
| 209 | "SELECT cid, 1 FROM plink WHERE pid=:pid AND isprim " |
| 210 | "UNION ALL " |
| 211 | "SELECT pid, 0 FROM plink WHERE :back AND cid=:pid AND isprim" |
| 212 | ); |
| 213 | }else{ |
| 214 | db_prepare(&s, |
| 215 | "SELECT cid, 1 FROM plink WHERE pid=:pid " |
| 216 | "UNION ALL " |
| 217 | "SELECT pid, 0 FROM plink WHERE :back AND cid=:pid" |
| 218 | ); |
| 219 | } |
| 220 | bag_init(&seen); |
| 221 | while( (p = pqueuex_extract_ptr(&path.pending))!=0 ){ |
| 222 | if( path_debug ){ |
| 223 | printf("PULL %s %g\n", path_rid_desc(p->rid), p->u.rCost); |
| 224 | } |
| 225 | if( p->rid==iTo ){ |
| 226 | db_finalize(&s); |
| 227 | path.pEnd = p; |
| 228 | path_reverse_path(); |
| 229 | for(p=path.pStart->u.pTo; p; p=p->u.pTo ){ |
| 230 | if( !p->isHidden ) path.nNotHidden++; |
| 231 | } |
| 232 | return path.pStart; |
| 233 | } |
| 234 | if( bag_find(&seen, p->rid) ) continue; |
| 235 | bag_insert(&seen, p->rid); |
| 236 | db_bind_int(&s, ":pid", p->rid); |
| 237 | if( !oneWayOnly ) db_bind_int(&s, ":back", !p->fromIsParent); |
| 238 | while( db_step(&s)==SQLITE_ROW ){ |
| 239 | int cid = db_column_int(&s, 0); |
| 240 | int isParent = db_column_int(&s, 1); |
| 241 | PathNode *pNew; |
| 242 | if( bag_find(&seen, cid) ) continue; |
| 243 | pNew = path_new_node(cid, p, isParent); |
| 244 | if( pHidden && bag_find(pHidden,cid) ) pNew->isHidden = 1; |
| 245 | } |
| 246 | db_reset(&s); |
| 247 | } |
| 248 | db_finalize(&s); |
| 249 | path_reset(); |
| 250 | return 0; |
| 251 | } |
| @@ -215,10 +271,22 @@ | |
| 271 | PathNode *p; |
| 272 | p = path.pStart; |
| 273 | if( p ) p = p->u.pTo; |
| 274 | return p; |
| 275 | } |
| 276 | |
| 277 | /* |
| 278 | ** Return the branch for a path node. |
| 279 | ** |
| 280 | ** Storage space is managed by the path subsystem. The returned value |
| 281 | ** is valid until the path is reset. |
| 282 | */ |
| 283 | const char *path_branch(PathNode *p){ |
| 284 | if( p==0 ) return 0; |
| 285 | if( p->zBranch==0 ) p->zBranch = branch_of_rid(p->rid); |
| 286 | return p->zBranch; |
| 287 | } |
| 288 | |
| 289 | /* |
| 290 | ** Return an estimate of the number of comparisons remaining in order |
| 291 | ** to bisect path. This is based on the log2() of path.nStep. |
| 292 | */ |
| @@ -238,11 +306,11 @@ | |
| 306 | int cid /* RID for check-in at the end of the path */ |
| 307 | ){ |
| 308 | PathNode *pPath; |
| 309 | int gen = 0; |
| 310 | Stmt ins; |
| 311 | pPath = path_shortest(cid, origid, 1, 0, 0, 0); |
| 312 | db_multi_exec( |
| 313 | "CREATE TEMP TABLE IF NOT EXISTS ancestor(" |
| 314 | " rid INT UNIQUE," |
| 315 | " generation INTEGER PRIMARY KEY" |
| 316 | ");" |
| @@ -261,58 +329,55 @@ | |
| 329 | } |
| 330 | |
| 331 | /* |
| 332 | ** COMMAND: test-shortest-path |
| 333 | ** |
| 334 | ** Usage: %fossil test-shortest-path [OPTIONS] VERSION1 VERSION2 |
| 335 | ** |
| 336 | ** Report the shortest path between two check-ins. Options: |
| 337 | ** |
| 338 | ** --branch-cost N Additional cost N for changing branches |
| 339 | ** --debug Show debugging output |
| 340 | ** --one-way One-way forwards in time, parent->child only |
| 341 | ** --no-merge Follow only direct parent-child paths and omit |
| 342 | ** merge links. |
| 343 | */ |
| 344 | void shortest_path_test_cmd(void){ |
| 345 | int iFrom; |
| 346 | int iTo; |
| 347 | PathNode *p; |
| 348 | int n; |
| 349 | int directOnly; |
| 350 | int oneWay; |
| 351 | const char *zBrCost; |
| 352 | |
| 353 | db_find_and_open_repository(0,0); |
| 354 | directOnly = find_option("no-merge",0,0)!=0; |
| 355 | oneWay = find_option("one-way",0,0)!=0; |
| 356 | zBrCost = find_option("branch-cost",0,1); |
| 357 | if( find_option("debug",0,0)!=0 ) path_debug = 1; |
| 358 | if( g.argc!=4 ) usage("VERSION1 VERSION2"); |
| 359 | iFrom = name_to_rid(g.argv[2]); |
| 360 | iTo = name_to_rid(g.argv[3]); |
| 361 | p = path_shortest(iFrom, iTo, directOnly, oneWay, 0, |
| 362 | zBrCost ? atoi(zBrCost) : 0); |
| 363 | if( p==0 ){ |
| 364 | fossil_fatal("no path from %s to %s", g.argv[1], g.argv[2]); |
| 365 | } |
| 366 | for(n=1, p=path.pStart; p; p=p->u.pTo, n++){ |
| 367 | fossil_print("%4d: %s\n", n, path_rid_desc(p->rid)); |
| 368 | } |
| 369 | path_debug = 0; |
| 370 | } |
| 371 | |
| 372 | /* |
| 373 | ** Find the closest common ancestor of two nodes. "Closest" means the |
| 374 | ** fewest number of arcs. |
| 375 | */ |
| 376 | int path_common_ancestor(int iMe, int iYou){ |
| 377 | Stmt s; |
| 378 | PathNode *pThis; |
| 379 | PathNode *p; |
| 380 | Bag me, you; |
| 381 | |
| 382 | if( iMe==iYou ) return iMe; |
| 383 | if( iMe==0 || iYou==0 ) return 0; |
| @@ -323,45 +388,40 @@ | |
| 388 | db_prepare(&s, "SELECT pid FROM plink WHERE cid=:cid"); |
| 389 | bag_init(&me); |
| 390 | bag_insert(&me, iMe); |
| 391 | bag_init(&you); |
| 392 | bag_insert(&you, iYou); |
| 393 | while( (pThis = pqueuex_extract_ptr(&path.pending))!=0 ){ |
| 394 | db_bind_int(&s, ":cid", pThis->rid); |
| 395 | while( db_step(&s)==SQLITE_ROW ){ |
| 396 | int pid = db_column_int(&s, 0); |
| 397 | if( bag_find(pThis->isPrim ? &you : &me, pid) ){ |
| 398 | /* pid is the common ancestor */ |
| 399 | PathNode *pNext; |
| 400 | for(p=path.pAll; p && p->rid!=pid; p=p->pAll){} |
| 401 | assert( p!=0 ); |
| 402 | pNext = p; |
| 403 | while( pNext ){ |
| 404 | pNext = p->pFrom; |
| 405 | p->pFrom = pThis; |
| 406 | pThis = p; |
| 407 | p = pNext; |
| 408 | } |
| 409 | if( pThis==path.pStart ) path.pStart = path.pEnd; |
| 410 | path.pEnd = pThis; |
| 411 | path_reverse_path(); |
| 412 | db_finalize(&s); |
| 413 | return pid; |
| 414 | }else if( bag_find(pThis->isPrim ? &me : &you, pid) ){ |
| 415 | /* pid is just an alternative path to a node we've already visited */ |
| 416 | continue; |
| 417 | } |
| 418 | p = path_new_node(pid, pThis, 0); |
| 419 | p->isPrim = pThis->isPrim; |
| 420 | bag_insert(pThis->isPrim ? &me : &you, pid); |
| 421 | } |
| 422 | db_reset(&s); |
| 423 | } |
| 424 | db_finalize(&s); |
| 425 | path_reset(); |
| 426 | return 0; |
| 427 | } |
| @@ -453,11 +513,11 @@ | |
| 513 | }else if(0==iTo){ |
| 514 | fossil_fatal("Invalid 'to' RID: 0"); |
| 515 | } |
| 516 | if( iFrom==iTo ) return; |
| 517 | path_reset(); |
| 518 | p = path_shortest(iFrom, iTo, 1, revOK==0, 0, 0); |
| 519 | if( p==0 ) return; |
| 520 | path_reverse_path(); |
| 521 | db_prepare(&q1, |
| 522 | "SELECT pfnid, fnid FROM mlink" |
| 523 | " WHERE mid=:mid AND (pfnid>0 OR fid==0)" |
| 524 |
+147
-27
| --- src/pqueue.c | ||
| +++ src/pqueue.c | ||
| @@ -15,17 +15,24 @@ | ||
| 15 | 15 | ** |
| 16 | 16 | ******************************************************************************* |
| 17 | 17 | ** |
| 18 | 18 | ** This file contains code used to implement a priority queue. |
| 19 | 19 | ** A priority queue is a list of items order by a floating point |
| 20 | -** value. We can insert integers with each integer tied to its | |
| 21 | -** value then extract the integer with the smallest value. | |
| 20 | +** value. Each value can be associated with either a pointer or | |
| 21 | +** an integer. Items are inserted into the queue in an arbitrary | |
| 22 | +** order, but are returned in order of the floating point value. | |
| 23 | +** | |
| 24 | +** This implementation uses a heap of QueueElement objects. The | |
| 25 | +** root of the heap is PQueue.a[0]. Each node a[x] has two daughter | |
| 26 | +** nodes a[x*2+1] and a[x*2+2]. The mother node of a[y] is a[(y-1)/2] | |
| 27 | +** (assuming integer division rounded down). The following is always true: | |
| 28 | +** | |
| 29 | +** The value of any node is less than or equal two the values | |
| 30 | +** of both daughter nodes. (The Heap Property). | |
| 22 | 31 | ** |
| 23 | -** The way this queue is used, we never expect it to contain more | |
| 24 | -** than 2 or 3 elements, so a simple array is sufficient as the | |
| 25 | -** implementation. This could give worst case O(N) insert times, | |
| 26 | -** but because of the nature of the problem we expect O(1) performance. | |
| 32 | +** A consequence of the heap property is that a[0] always contains | |
| 33 | +** the node with the smallest value. | |
| 27 | 34 | ** |
| 28 | 35 | ** Compatibility note: Some versions of OpenSSL export a symbols |
| 29 | 36 | ** like "pqueue_insert". This is, technically, a bug in OpenSSL. |
| 30 | 37 | ** We work around it here by using "pqueuex_" instead of "pqueue_". |
| 31 | 38 | */ |
| @@ -41,11 +48,14 @@ | ||
| 41 | 48 | */ |
| 42 | 49 | struct PQueue { |
| 43 | 50 | int cnt; /* Number of entries in the queue */ |
| 44 | 51 | int sz; /* Number of slots in a[] */ |
| 45 | 52 | struct QueueElement { |
| 46 | - int id; /* ID of the element */ | |
| 53 | + union { | |
| 54 | + int id; /* ID of the element */ | |
| 55 | + void *p; /* Pointer to an object */ | |
| 56 | + } u; | |
| 47 | 57 | double value; /* Value of element. Kept in ascending order */ |
| 48 | 58 | } *a; |
| 49 | 59 | }; |
| 50 | 60 | #endif |
| 51 | 61 | |
| @@ -69,44 +79,154 @@ | ||
| 69 | 79 | */ |
| 70 | 80 | static void pqueuex_resize(PQueue *p, int N){ |
| 71 | 81 | p->a = fossil_realloc(p->a, sizeof(p->a[0])*N); |
| 72 | 82 | p->sz = N; |
| 73 | 83 | } |
| 84 | + | |
| 85 | +/* | |
| 86 | +** Allocate a new queue entry and return a pointer to it. | |
| 87 | +*/ | |
| 88 | +static struct QueueElement *pqueuex_new_entry(PQueue *p){ | |
| 89 | + if( p->cnt+1>p->sz ){ | |
| 90 | + pqueuex_resize(p, p->cnt+7); | |
| 91 | + } | |
| 92 | + return &p->a[p->cnt++]; | |
| 93 | +} | |
| 94 | + | |
| 95 | +/* | |
| 96 | +** Element p->a[p->cnt-1] has just been inserted. Shift entries | |
| 97 | +** around so as to preserve the heap property. | |
| 98 | +*/ | |
| 99 | +static void pqueuex_rebalance(PQueue *p){ | |
| 100 | + int i, j; | |
| 101 | + struct QueueElement *a = p->a; | |
| 102 | + i = p->cnt-1; | |
| 103 | + while( (j = (i-1)/2)>=0 && a[j].value>a[i].value ){ | |
| 104 | + struct QueueElement t = a[j]; | |
| 105 | + a[j] = a[i]; | |
| 106 | + a[i] = t; | |
| 107 | + i = j; | |
| 108 | + } | |
| 109 | +} | |
| 74 | 110 | |
| 75 | 111 | /* |
| 76 | 112 | ** Insert element e into the queue. |
| 77 | 113 | */ |
| 78 | 114 | void pqueuex_insert(PQueue *p, int e, double v){ |
| 115 | + struct QueueElement *pE = pqueuex_new_entry(p); | |
| 116 | + pE->value = v; | |
| 117 | + pE->u.id = e; | |
| 118 | + pqueuex_rebalance(p); | |
| 119 | +} | |
| 120 | +void pqueuex_insert_ptr(PQueue *p, void *pPtr, double v){ | |
| 121 | + struct QueueElement *pE = pqueuex_new_entry(p); | |
| 122 | + pE->value = v; | |
| 123 | + pE->u.p = pPtr; | |
| 124 | + pqueuex_rebalance(p); | |
| 125 | +} | |
| 126 | + | |
| 127 | +/* | |
| 128 | +** Remove and discard p->a[0] element from the queue. Rearrange | |
| 129 | +** nodes to preserve the heap property. | |
| 130 | +*/ | |
| 131 | +static void pqueuex_pop(PQueue *p){ | |
| 79 | 132 | int i, j; |
| 80 | - if( p->cnt+1>p->sz ){ | |
| 81 | - pqueuex_resize(p, p->cnt+5); | |
| 82 | - } | |
| 83 | - for(i=0; i<p->cnt; i++){ | |
| 84 | - if( p->a[i].value>v ){ | |
| 85 | - for(j=p->cnt; j>i; j--){ | |
| 86 | - p->a[j] = p->a[j-1]; | |
| 87 | - } | |
| 88 | - break; | |
| 89 | - } | |
| 90 | - } | |
| 91 | - p->a[i].id = e; | |
| 92 | - p->a[i].value = v; | |
| 93 | - p->cnt++; | |
| 133 | + struct QueueElement *a = p->a; | |
| 134 | + struct QueueElement tmp; | |
| 135 | + i = 0; | |
| 136 | + a[0] = a[p->cnt-1]; | |
| 137 | + p->cnt--; | |
| 138 | + while( (j = i*2+1)<p->cnt ){ | |
| 139 | + if( j+1<p->cnt && a[j].value > a[j+1].value ) j++; | |
| 140 | + if( a[i].value < a[j].value ) break; | |
| 141 | + tmp = a[i]; | |
| 142 | + a[i] = a[j]; | |
| 143 | + a[j] = tmp; | |
| 144 | + i = j; | |
| 145 | + } | |
| 94 | 146 | } |
| 95 | 147 | |
| 96 | 148 | /* |
| 97 | 149 | ** Extract the first element from the queue (the element with |
| 98 | 150 | ** the smallest value) and return its ID. Return 0 if the queue |
| 99 | 151 | ** is empty. |
| 100 | 152 | */ |
| 101 | 153 | int pqueuex_extract(PQueue *p){ |
| 102 | - int e, i; | |
| 154 | + int e; | |
| 155 | + if( p->cnt==0 ){ | |
| 156 | + return 0; | |
| 157 | + } | |
| 158 | + e = p->a[0].u.id; | |
| 159 | + pqueuex_pop(p); | |
| 160 | + return e; | |
| 161 | +} | |
| 162 | +void *pqueuex_extract_ptr(PQueue *p){ | |
| 163 | + void *pPtr; | |
| 103 | 164 | if( p->cnt==0 ){ |
| 104 | 165 | return 0; |
| 105 | 166 | } |
| 106 | - e = p->a[0].id; | |
| 107 | - for(i=0; i<p->cnt-1; i++){ | |
| 108 | - p->a[i] = p->a[i+1]; | |
| 167 | + pPtr = p->a[0].u.p; | |
| 168 | + pqueuex_pop(p); | |
| 169 | + return pPtr; | |
| 170 | +} | |
| 171 | + | |
| 172 | +/* | |
| 173 | +** Print the entire heap associated with the test-pqueue command. | |
| 174 | +*/ | |
| 175 | +static void pqueuex_test_print(PQueue *p){ | |
| 176 | + int j; | |
| 177 | + for(j=0; j<p->cnt; j++){ | |
| 178 | + fossil_print("(%d) %g/%s ",j,p->a[j].value,p->a[j].u.p); | |
| 179 | + } | |
| 180 | + fossil_print("\n"); | |
| 181 | +} | |
| 182 | + | |
| 183 | +/* | |
| 184 | +** COMMAND: test-pqueue | |
| 185 | +** | |
| 186 | +** This command is used for testing the PQueue object. There are one | |
| 187 | +** or more arguments, each of the form: | |
| 188 | +** | |
| 189 | +** (1) NUMBER/TEXT | |
| 190 | +** (2) ^ | |
| 191 | +** (3) -v | |
| 192 | +** | |
| 193 | +** Form (1) arguments add an entry to the queue with value NUMBER and | |
| 194 | +** content TEXT. Form (2) pops off the queue entry with the smallest | |
| 195 | +** value. Form (3) (the -v option) causes the heap to be displayed after | |
| 196 | +** each subsequent operation. | |
| 197 | +*/ | |
| 198 | +void pqueuex_test_cmd(void){ | |
| 199 | + int i; | |
| 200 | + PQueue x; | |
| 201 | + const char *zId; | |
| 202 | + int bDebug = 0; | |
| 203 | + | |
| 204 | + pqueuex_init(&x); | |
| 205 | + for(i=2; i<g.argc; i++){ | |
| 206 | + const char *zArg = g.argv[i]; | |
| 207 | + if( strcmp(zArg,"-v")==0 ){ | |
| 208 | + bDebug = 1; | |
| 209 | + }else if( strcmp(zArg, "^")==0 ){ | |
| 210 | + zId = pqueuex_extract_ptr(&x); | |
| 211 | + if( zId==0 ){ | |
| 212 | + fossil_print("%2d: POP NULL\n", i); | |
| 213 | + }else{ | |
| 214 | + fossil_print("%2d: POP \"%s\"\n", i, zId); | |
| 215 | + } | |
| 216 | + if( bDebug) pqueuex_test_print(&x); | |
| 217 | + }else{ | |
| 218 | + double r = atof(zArg); | |
| 219 | + zId = strchr(zArg,'/'); | |
| 220 | + if( zId==0 ) zId = zArg; | |
| 221 | + if( zId[0]=='/' ) zId++; | |
| 222 | + pqueuex_insert_ptr(&x, (void*)zId, r); | |
| 223 | + fossil_print("%2d: INSERT \"%s\"\n", i, zId); | |
| 224 | + if( bDebug) pqueuex_test_print(&x); | |
| 225 | + } | |
| 226 | + } | |
| 227 | + while( (zId = pqueuex_extract_ptr(&x))!=0 ){ | |
| 228 | + fossil_print("... POP \"%s\"\n", zId); | |
| 229 | + if( bDebug) pqueuex_test_print(&x); | |
| 109 | 230 | } |
| 110 | - p->cnt--; | |
| 111 | - return e; | |
| 231 | + pqueuex_clear(&x); | |
| 112 | 232 | } |
| 113 | 233 |
| --- src/pqueue.c | |
| +++ src/pqueue.c | |
| @@ -15,17 +15,24 @@ | |
| 15 | ** |
| 16 | ******************************************************************************* |
| 17 | ** |
| 18 | ** This file contains code used to implement a priority queue. |
| 19 | ** A priority queue is a list of items order by a floating point |
| 20 | ** value. We can insert integers with each integer tied to its |
| 21 | ** value then extract the integer with the smallest value. |
| 22 | ** |
| 23 | ** The way this queue is used, we never expect it to contain more |
| 24 | ** than 2 or 3 elements, so a simple array is sufficient as the |
| 25 | ** implementation. This could give worst case O(N) insert times, |
| 26 | ** but because of the nature of the problem we expect O(1) performance. |
| 27 | ** |
| 28 | ** Compatibility note: Some versions of OpenSSL export a symbols |
| 29 | ** like "pqueue_insert". This is, technically, a bug in OpenSSL. |
| 30 | ** We work around it here by using "pqueuex_" instead of "pqueue_". |
| 31 | */ |
| @@ -41,11 +48,14 @@ | |
| 41 | */ |
| 42 | struct PQueue { |
| 43 | int cnt; /* Number of entries in the queue */ |
| 44 | int sz; /* Number of slots in a[] */ |
| 45 | struct QueueElement { |
| 46 | int id; /* ID of the element */ |
| 47 | double value; /* Value of element. Kept in ascending order */ |
| 48 | } *a; |
| 49 | }; |
| 50 | #endif |
| 51 | |
| @@ -69,44 +79,154 @@ | |
| 69 | */ |
| 70 | static void pqueuex_resize(PQueue *p, int N){ |
| 71 | p->a = fossil_realloc(p->a, sizeof(p->a[0])*N); |
| 72 | p->sz = N; |
| 73 | } |
| 74 | |
| 75 | /* |
| 76 | ** Insert element e into the queue. |
| 77 | */ |
| 78 | void pqueuex_insert(PQueue *p, int e, double v){ |
| 79 | int i, j; |
| 80 | if( p->cnt+1>p->sz ){ |
| 81 | pqueuex_resize(p, p->cnt+5); |
| 82 | } |
| 83 | for(i=0; i<p->cnt; i++){ |
| 84 | if( p->a[i].value>v ){ |
| 85 | for(j=p->cnt; j>i; j--){ |
| 86 | p->a[j] = p->a[j-1]; |
| 87 | } |
| 88 | break; |
| 89 | } |
| 90 | } |
| 91 | p->a[i].id = e; |
| 92 | p->a[i].value = v; |
| 93 | p->cnt++; |
| 94 | } |
| 95 | |
| 96 | /* |
| 97 | ** Extract the first element from the queue (the element with |
| 98 | ** the smallest value) and return its ID. Return 0 if the queue |
| 99 | ** is empty. |
| 100 | */ |
| 101 | int pqueuex_extract(PQueue *p){ |
| 102 | int e, i; |
| 103 | if( p->cnt==0 ){ |
| 104 | return 0; |
| 105 | } |
| 106 | e = p->a[0].id; |
| 107 | for(i=0; i<p->cnt-1; i++){ |
| 108 | p->a[i] = p->a[i+1]; |
| 109 | } |
| 110 | p->cnt--; |
| 111 | return e; |
| 112 | } |
| 113 |
| --- src/pqueue.c | |
| +++ src/pqueue.c | |
| @@ -15,17 +15,24 @@ | |
| 15 | ** |
| 16 | ******************************************************************************* |
| 17 | ** |
| 18 | ** This file contains code used to implement a priority queue. |
| 19 | ** A priority queue is a list of items order by a floating point |
| 20 | ** value. Each value can be associated with either a pointer or |
| 21 | ** an integer. Items are inserted into the queue in an arbitrary |
| 22 | ** order, but are returned in order of the floating point value. |
| 23 | ** |
| 24 | ** This implementation uses a heap of QueueElement objects. The |
| 25 | ** root of the heap is PQueue.a[0]. Each node a[x] has two daughter |
| 26 | ** nodes a[x*2+1] and a[x*2+2]. The mother node of a[y] is a[(y-1)/2] |
| 27 | ** (assuming integer division rounded down). The following is always true: |
| 28 | ** |
| 29 | ** The value of any node is less than or equal two the values |
| 30 | ** of both daughter nodes. (The Heap Property). |
| 31 | ** |
| 32 | ** A consequence of the heap property is that a[0] always contains |
| 33 | ** the node with the smallest value. |
| 34 | ** |
| 35 | ** Compatibility note: Some versions of OpenSSL export a symbols |
| 36 | ** like "pqueue_insert". This is, technically, a bug in OpenSSL. |
| 37 | ** We work around it here by using "pqueuex_" instead of "pqueue_". |
| 38 | */ |
| @@ -41,11 +48,14 @@ | |
| 48 | */ |
| 49 | struct PQueue { |
| 50 | int cnt; /* Number of entries in the queue */ |
| 51 | int sz; /* Number of slots in a[] */ |
| 52 | struct QueueElement { |
| 53 | union { |
| 54 | int id; /* ID of the element */ |
| 55 | void *p; /* Pointer to an object */ |
| 56 | } u; |
| 57 | double value; /* Value of element. Kept in ascending order */ |
| 58 | } *a; |
| 59 | }; |
| 60 | #endif |
| 61 | |
| @@ -69,44 +79,154 @@ | |
| 79 | */ |
| 80 | static void pqueuex_resize(PQueue *p, int N){ |
| 81 | p->a = fossil_realloc(p->a, sizeof(p->a[0])*N); |
| 82 | p->sz = N; |
| 83 | } |
| 84 | |
| 85 | /* |
| 86 | ** Allocate a new queue entry and return a pointer to it. |
| 87 | */ |
| 88 | static struct QueueElement *pqueuex_new_entry(PQueue *p){ |
| 89 | if( p->cnt+1>p->sz ){ |
| 90 | pqueuex_resize(p, p->cnt+7); |
| 91 | } |
| 92 | return &p->a[p->cnt++]; |
| 93 | } |
| 94 | |
| 95 | /* |
| 96 | ** Element p->a[p->cnt-1] has just been inserted. Shift entries |
| 97 | ** around so as to preserve the heap property. |
| 98 | */ |
| 99 | static void pqueuex_rebalance(PQueue *p){ |
| 100 | int i, j; |
| 101 | struct QueueElement *a = p->a; |
| 102 | i = p->cnt-1; |
| 103 | while( (j = (i-1)/2)>=0 && a[j].value>a[i].value ){ |
| 104 | struct QueueElement t = a[j]; |
| 105 | a[j] = a[i]; |
| 106 | a[i] = t; |
| 107 | i = j; |
| 108 | } |
| 109 | } |
| 110 | |
| 111 | /* |
| 112 | ** Insert element e into the queue. |
| 113 | */ |
| 114 | void pqueuex_insert(PQueue *p, int e, double v){ |
| 115 | struct QueueElement *pE = pqueuex_new_entry(p); |
| 116 | pE->value = v; |
| 117 | pE->u.id = e; |
| 118 | pqueuex_rebalance(p); |
| 119 | } |
| 120 | void pqueuex_insert_ptr(PQueue *p, void *pPtr, double v){ |
| 121 | struct QueueElement *pE = pqueuex_new_entry(p); |
| 122 | pE->value = v; |
| 123 | pE->u.p = pPtr; |
| 124 | pqueuex_rebalance(p); |
| 125 | } |
| 126 | |
| 127 | /* |
| 128 | ** Remove and discard p->a[0] element from the queue. Rearrange |
| 129 | ** nodes to preserve the heap property. |
| 130 | */ |
| 131 | static void pqueuex_pop(PQueue *p){ |
| 132 | int i, j; |
| 133 | struct QueueElement *a = p->a; |
| 134 | struct QueueElement tmp; |
| 135 | i = 0; |
| 136 | a[0] = a[p->cnt-1]; |
| 137 | p->cnt--; |
| 138 | while( (j = i*2+1)<p->cnt ){ |
| 139 | if( j+1<p->cnt && a[j].value > a[j+1].value ) j++; |
| 140 | if( a[i].value < a[j].value ) break; |
| 141 | tmp = a[i]; |
| 142 | a[i] = a[j]; |
| 143 | a[j] = tmp; |
| 144 | i = j; |
| 145 | } |
| 146 | } |
| 147 | |
| 148 | /* |
| 149 | ** Extract the first element from the queue (the element with |
| 150 | ** the smallest value) and return its ID. Return 0 if the queue |
| 151 | ** is empty. |
| 152 | */ |
| 153 | int pqueuex_extract(PQueue *p){ |
| 154 | int e; |
| 155 | if( p->cnt==0 ){ |
| 156 | return 0; |
| 157 | } |
| 158 | e = p->a[0].u.id; |
| 159 | pqueuex_pop(p); |
| 160 | return e; |
| 161 | } |
| 162 | void *pqueuex_extract_ptr(PQueue *p){ |
| 163 | void *pPtr; |
| 164 | if( p->cnt==0 ){ |
| 165 | return 0; |
| 166 | } |
| 167 | pPtr = p->a[0].u.p; |
| 168 | pqueuex_pop(p); |
| 169 | return pPtr; |
| 170 | } |
| 171 | |
| 172 | /* |
| 173 | ** Print the entire heap associated with the test-pqueue command. |
| 174 | */ |
| 175 | static void pqueuex_test_print(PQueue *p){ |
| 176 | int j; |
| 177 | for(j=0; j<p->cnt; j++){ |
| 178 | fossil_print("(%d) %g/%s ",j,p->a[j].value,p->a[j].u.p); |
| 179 | } |
| 180 | fossil_print("\n"); |
| 181 | } |
| 182 | |
| 183 | /* |
| 184 | ** COMMAND: test-pqueue |
| 185 | ** |
| 186 | ** This command is used for testing the PQueue object. There are one |
| 187 | ** or more arguments, each of the form: |
| 188 | ** |
| 189 | ** (1) NUMBER/TEXT |
| 190 | ** (2) ^ |
| 191 | ** (3) -v |
| 192 | ** |
| 193 | ** Form (1) arguments add an entry to the queue with value NUMBER and |
| 194 | ** content TEXT. Form (2) pops off the queue entry with the smallest |
| 195 | ** value. Form (3) (the -v option) causes the heap to be displayed after |
| 196 | ** each subsequent operation. |
| 197 | */ |
| 198 | void pqueuex_test_cmd(void){ |
| 199 | int i; |
| 200 | PQueue x; |
| 201 | const char *zId; |
| 202 | int bDebug = 0; |
| 203 | |
| 204 | pqueuex_init(&x); |
| 205 | for(i=2; i<g.argc; i++){ |
| 206 | const char *zArg = g.argv[i]; |
| 207 | if( strcmp(zArg,"-v")==0 ){ |
| 208 | bDebug = 1; |
| 209 | }else if( strcmp(zArg, "^")==0 ){ |
| 210 | zId = pqueuex_extract_ptr(&x); |
| 211 | if( zId==0 ){ |
| 212 | fossil_print("%2d: POP NULL\n", i); |
| 213 | }else{ |
| 214 | fossil_print("%2d: POP \"%s\"\n", i, zId); |
| 215 | } |
| 216 | if( bDebug) pqueuex_test_print(&x); |
| 217 | }else{ |
| 218 | double r = atof(zArg); |
| 219 | zId = strchr(zArg,'/'); |
| 220 | if( zId==0 ) zId = zArg; |
| 221 | if( zId[0]=='/' ) zId++; |
| 222 | pqueuex_insert_ptr(&x, (void*)zId, r); |
| 223 | fossil_print("%2d: INSERT \"%s\"\n", i, zId); |
| 224 | if( bDebug) pqueuex_test_print(&x); |
| 225 | } |
| 226 | } |
| 227 | while( (zId = pqueuex_extract_ptr(&x))!=0 ){ |
| 228 | fossil_print("... POP \"%s\"\n", zId); |
| 229 | if( bDebug) pqueuex_test_print(&x); |
| 230 | } |
| 231 | pqueuex_clear(&x); |
| 232 | } |
| 233 |
+25
-9
| --- src/printf.c | ||
| +++ src/printf.c | ||
| @@ -240,28 +240,44 @@ | ||
| 240 | 240 | while( (N-- != 0) && *(z++)!=0 ){ n++; } |
| 241 | 241 | return n; |
| 242 | 242 | } |
| 243 | 243 | #endif |
| 244 | 244 | |
| 245 | +/* | |
| 246 | +** SETTING: timeline-plaintext boolean default=off | |
| 247 | +** | |
| 248 | +** If enabled, no wiki-formatting is done for timeline comment messages. | |
| 249 | +** Hyperlinks are activated, but they show up on screen using the | |
| 250 | +** complete input text, not just the display text. No other formatting | |
| 251 | +** is done. | |
| 252 | +*/ | |
| 253 | +/* | |
| 254 | +** SETTING: timeline-hard-newlines boolean default=off | |
| 255 | +** | |
| 256 | +** If enabled, the timeline honors newline characters in check-in comments. | |
| 257 | +** In other words, newlines are coverted into <br> for HTML display. | |
| 258 | +** The default behavior, when this setting is off, is that newlines are | |
| 259 | +** treated like any other whitespace character. | |
| 260 | +*/ | |
| 261 | + | |
| 245 | 262 | /* |
| 246 | 263 | ** Return an appropriate set of flags for wiki_convert() for displaying |
| 247 | 264 | ** comments on a timeline. These flag settings are determined by |
| 248 | 265 | ** configuration parameters. |
| 249 | 266 | ** |
| 250 | 267 | ** The altForm2 argument is true for "%!W" (with the "!" alternate-form-2 |
| 251 | -** flags) and is false for plain "%W". The ! indicates that the text is | |
| 252 | -** to be rendered on a form rather than the timeline and that block markup | |
| 253 | -** is acceptable even if the "timeline-block-markup" setting is false. | |
| 268 | +** flags) and is false for plain "%W". The ! flag indicates that the | |
| 269 | +** formatting is for display of a check-in comment on the timeline. Such | |
| 270 | +** comments used to be renderedd differently, but ever since 2020, they | |
| 271 | +** have been rendered identially, so the ! flag does not make any different | |
| 272 | +** in the output any more. | |
| 254 | 273 | */ |
| 255 | -static int wiki_convert_flags(int altForm2){ | |
| 274 | +int wiki_convert_flags(int altForm2){ | |
| 256 | 275 | static int wikiFlags = 0; |
| 276 | + (void)altForm2; | |
| 257 | 277 | if( wikiFlags==0 ){ |
| 258 | - if( altForm2 || db_get_boolean("timeline-block-markup", 0) ){ | |
| 259 | - wikiFlags = WIKI_INLINE | WIKI_NOBADLINKS; | |
| 260 | - }else{ | |
| 261 | - wikiFlags = WIKI_INLINE | WIKI_NOBLOCK | WIKI_NOBADLINKS; | |
| 262 | - } | |
| 278 | + wikiFlags = WIKI_INLINE | WIKI_NOBADLINKS; | |
| 263 | 279 | if( db_get_boolean("timeline-plaintext", 0) ){ |
| 264 | 280 | wikiFlags |= WIKI_LINKSONLY; |
| 265 | 281 | } |
| 266 | 282 | if( db_get_boolean("timeline-hard-newlines", 0) ){ |
| 267 | 283 | wikiFlags |= WIKI_NEWLINE; |
| 268 | 284 |
| --- src/printf.c | |
| +++ src/printf.c | |
| @@ -240,28 +240,44 @@ | |
| 240 | while( (N-- != 0) && *(z++)!=0 ){ n++; } |
| 241 | return n; |
| 242 | } |
| 243 | #endif |
| 244 | |
| 245 | /* |
| 246 | ** Return an appropriate set of flags for wiki_convert() for displaying |
| 247 | ** comments on a timeline. These flag settings are determined by |
| 248 | ** configuration parameters. |
| 249 | ** |
| 250 | ** The altForm2 argument is true for "%!W" (with the "!" alternate-form-2 |
| 251 | ** flags) and is false for plain "%W". The ! indicates that the text is |
| 252 | ** to be rendered on a form rather than the timeline and that block markup |
| 253 | ** is acceptable even if the "timeline-block-markup" setting is false. |
| 254 | */ |
| 255 | static int wiki_convert_flags(int altForm2){ |
| 256 | static int wikiFlags = 0; |
| 257 | if( wikiFlags==0 ){ |
| 258 | if( altForm2 || db_get_boolean("timeline-block-markup", 0) ){ |
| 259 | wikiFlags = WIKI_INLINE | WIKI_NOBADLINKS; |
| 260 | }else{ |
| 261 | wikiFlags = WIKI_INLINE | WIKI_NOBLOCK | WIKI_NOBADLINKS; |
| 262 | } |
| 263 | if( db_get_boolean("timeline-plaintext", 0) ){ |
| 264 | wikiFlags |= WIKI_LINKSONLY; |
| 265 | } |
| 266 | if( db_get_boolean("timeline-hard-newlines", 0) ){ |
| 267 | wikiFlags |= WIKI_NEWLINE; |
| 268 |
| --- src/printf.c | |
| +++ src/printf.c | |
| @@ -240,28 +240,44 @@ | |
| 240 | while( (N-- != 0) && *(z++)!=0 ){ n++; } |
| 241 | return n; |
| 242 | } |
| 243 | #endif |
| 244 | |
| 245 | /* |
| 246 | ** SETTING: timeline-plaintext boolean default=off |
| 247 | ** |
| 248 | ** If enabled, no wiki-formatting is done for timeline comment messages. |
| 249 | ** Hyperlinks are activated, but they show up on screen using the |
| 250 | ** complete input text, not just the display text. No other formatting |
| 251 | ** is done. |
| 252 | */ |
| 253 | /* |
| 254 | ** SETTING: timeline-hard-newlines boolean default=off |
| 255 | ** |
| 256 | ** If enabled, the timeline honors newline characters in check-in comments. |
| 257 | ** In other words, newlines are coverted into <br> for HTML display. |
| 258 | ** The default behavior, when this setting is off, is that newlines are |
| 259 | ** treated like any other whitespace character. |
| 260 | */ |
| 261 | |
| 262 | /* |
| 263 | ** Return an appropriate set of flags for wiki_convert() for displaying |
| 264 | ** comments on a timeline. These flag settings are determined by |
| 265 | ** configuration parameters. |
| 266 | ** |
| 267 | ** The altForm2 argument is true for "%!W" (with the "!" alternate-form-2 |
| 268 | ** flags) and is false for plain "%W". The ! flag indicates that the |
| 269 | ** formatting is for display of a check-in comment on the timeline. Such |
| 270 | ** comments used to be renderedd differently, but ever since 2020, they |
| 271 | ** have been rendered identially, so the ! flag does not make any different |
| 272 | ** in the output any more. |
| 273 | */ |
| 274 | int wiki_convert_flags(int altForm2){ |
| 275 | static int wikiFlags = 0; |
| 276 | (void)altForm2; |
| 277 | if( wikiFlags==0 ){ |
| 278 | wikiFlags = WIKI_INLINE | WIKI_NOBADLINKS; |
| 279 | if( db_get_boolean("timeline-plaintext", 0) ){ |
| 280 | wikiFlags |= WIKI_LINKSONLY; |
| 281 | } |
| 282 | if( db_get_boolean("timeline-hard-newlines", 0) ){ |
| 283 | wikiFlags |= WIKI_NEWLINE; |
| 284 |
+1
-1
| --- src/rebuild.c | ||
| +++ src/rebuild.c | ||
| @@ -1414,11 +1414,11 @@ | ||
| 1414 | 1414 | } |
| 1415 | 1415 | |
| 1416 | 1416 | /* |
| 1417 | 1417 | ** COMMAND: deconstruct* |
| 1418 | 1418 | ** |
| 1419 | -** Usage %fossil deconstruct ?OPTIONS? DESTINATION | |
| 1419 | +** Usage: %fossil deconstruct ?OPTIONS? DESTINATION | |
| 1420 | 1420 | ** |
| 1421 | 1421 | ** This command exports all artifacts of a given repository and writes all |
| 1422 | 1422 | ** artifacts to the file system. The DESTINATION directory will be populated |
| 1423 | 1423 | ** with subdirectories AA and files AA/BBBBBBBBB.., where AABBBBBBBBB.. is the |
| 1424 | 1424 | ** 40+ character artifact ID, AA the first 2 characters. |
| 1425 | 1425 |
| --- src/rebuild.c | |
| +++ src/rebuild.c | |
| @@ -1414,11 +1414,11 @@ | |
| 1414 | } |
| 1415 | |
| 1416 | /* |
| 1417 | ** COMMAND: deconstruct* |
| 1418 | ** |
| 1419 | ** Usage %fossil deconstruct ?OPTIONS? DESTINATION |
| 1420 | ** |
| 1421 | ** This command exports all artifacts of a given repository and writes all |
| 1422 | ** artifacts to the file system. The DESTINATION directory will be populated |
| 1423 | ** with subdirectories AA and files AA/BBBBBBBBB.., where AABBBBBBBBB.. is the |
| 1424 | ** 40+ character artifact ID, AA the first 2 characters. |
| 1425 |
| --- src/rebuild.c | |
| +++ src/rebuild.c | |
| @@ -1414,11 +1414,11 @@ | |
| 1414 | } |
| 1415 | |
| 1416 | /* |
| 1417 | ** COMMAND: deconstruct* |
| 1418 | ** |
| 1419 | ** Usage: %fossil deconstruct ?OPTIONS? DESTINATION |
| 1420 | ** |
| 1421 | ** This command exports all artifacts of a given repository and writes all |
| 1422 | ** artifacts to the file system. The DESTINATION directory will be populated |
| 1423 | ** with subdirectories AA and files AA/BBBBBBBBB.., where AABBBBBBBBB.. is the |
| 1424 | ** 40+ character artifact ID, AA the first 2 characters. |
| 1425 |
+39
| --- src/regexp.c | ||
| +++ src/regexp.c | ||
| @@ -682,10 +682,49 @@ | ||
| 682 | 682 | if( j>0 && pRe->zInit[j-1]==0 ) j--; |
| 683 | 683 | pRe->nInit = j; |
| 684 | 684 | } |
| 685 | 685 | return pRe->zErr; |
| 686 | 686 | } |
| 687 | + | |
| 688 | +/* | |
| 689 | +** The input zIn is a string that we want to match exactly as part of | |
| 690 | +** a regular expression. Return a new string (in space obtained from | |
| 691 | +** fossil_malloc() or the equivalent) that escapes all regexp syntax | |
| 692 | +** characters in zIn. | |
| 693 | +*/ | |
| 694 | +char *re_quote(const char *zIn){ | |
| 695 | + Blob out; | |
| 696 | + blob_init(&out, 0, 0); | |
| 697 | + while( zIn[0] ){ | |
| 698 | + switch( zIn[0] ){ | |
| 699 | + case '.': | |
| 700 | + case '?': | |
| 701 | + case '*': | |
| 702 | + case '+': | |
| 703 | + case '\\': | |
| 704 | + case '(': | |
| 705 | + case ')': | |
| 706 | + case '[': | |
| 707 | + case ']': | |
| 708 | + case '|': | |
| 709 | + case '^': | |
| 710 | + case '$': | |
| 711 | + case '{': | |
| 712 | + case '}': { | |
| 713 | + blob_appendf(&out,"\\x%02x", (unsigned char)zIn[0]); | |
| 714 | + break; | |
| 715 | + } | |
| 716 | + default: { | |
| 717 | + blob_append_char(&out, zIn[0]); | |
| 718 | + break; | |
| 719 | + } | |
| 720 | + } | |
| 721 | + zIn++; | |
| 722 | + } | |
| 723 | + blob_materialize(&out); | |
| 724 | + return out.aData; | |
| 725 | +} | |
| 687 | 726 | |
| 688 | 727 | /* |
| 689 | 728 | ** Implementation of the regexp() SQL function. This function implements |
| 690 | 729 | ** the build-in REGEXP operator. The first argument to the function is the |
| 691 | 730 | ** pattern and the second argument is the string. So, the SQL statements: |
| 692 | 731 |
| --- src/regexp.c | |
| +++ src/regexp.c | |
| @@ -682,10 +682,49 @@ | |
| 682 | if( j>0 && pRe->zInit[j-1]==0 ) j--; |
| 683 | pRe->nInit = j; |
| 684 | } |
| 685 | return pRe->zErr; |
| 686 | } |
| 687 | |
| 688 | /* |
| 689 | ** Implementation of the regexp() SQL function. This function implements |
| 690 | ** the build-in REGEXP operator. The first argument to the function is the |
| 691 | ** pattern and the second argument is the string. So, the SQL statements: |
| 692 |
| --- src/regexp.c | |
| +++ src/regexp.c | |
| @@ -682,10 +682,49 @@ | |
| 682 | if( j>0 && pRe->zInit[j-1]==0 ) j--; |
| 683 | pRe->nInit = j; |
| 684 | } |
| 685 | return pRe->zErr; |
| 686 | } |
| 687 | |
| 688 | /* |
| 689 | ** The input zIn is a string that we want to match exactly as part of |
| 690 | ** a regular expression. Return a new string (in space obtained from |
| 691 | ** fossil_malloc() or the equivalent) that escapes all regexp syntax |
| 692 | ** characters in zIn. |
| 693 | */ |
| 694 | char *re_quote(const char *zIn){ |
| 695 | Blob out; |
| 696 | blob_init(&out, 0, 0); |
| 697 | while( zIn[0] ){ |
| 698 | switch( zIn[0] ){ |
| 699 | case '.': |
| 700 | case '?': |
| 701 | case '*': |
| 702 | case '+': |
| 703 | case '\\': |
| 704 | case '(': |
| 705 | case ')': |
| 706 | case '[': |
| 707 | case ']': |
| 708 | case '|': |
| 709 | case '^': |
| 710 | case '$': |
| 711 | case '{': |
| 712 | case '}': { |
| 713 | blob_appendf(&out,"\\x%02x", (unsigned char)zIn[0]); |
| 714 | break; |
| 715 | } |
| 716 | default: { |
| 717 | blob_append_char(&out, zIn[0]); |
| 718 | break; |
| 719 | } |
| 720 | } |
| 721 | zIn++; |
| 722 | } |
| 723 | blob_materialize(&out); |
| 724 | return out.aData; |
| 725 | } |
| 726 | |
| 727 | /* |
| 728 | ** Implementation of the regexp() SQL function. This function implements |
| 729 | ** the build-in REGEXP operator. The first argument to the function is the |
| 730 | ** pattern and the second argument is the string. So, the SQL statements: |
| 731 |
+3
-2
| --- src/repolist.c | ||
| +++ src/repolist.c | ||
| @@ -316,20 +316,21 @@ | ||
| 316 | 316 | style_header("Repository List"); |
| 317 | 317 | @ %s(blob_str(&html)) |
| 318 | 318 | style_table_sorter(); |
| 319 | 319 | style_finish_page(); |
| 320 | 320 | }else{ |
| 321 | + const char *zTitle = PD("FOSSIL_REPOLIST_TITLE","Repository List"); | |
| 321 | 322 | /* If no repositories were found that had the "repolist_skin" |
| 322 | 323 | ** property set, then use a default skin */ |
| 323 | 324 | @ <html> |
| 324 | 325 | @ <head> |
| 325 | 326 | @ <base href="%s(g.zBaseURL)/"> |
| 326 | 327 | @ <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 327 | - @ <title>Repository List</title> | |
| 328 | + @ <title>%h(zTitle)</title> | |
| 328 | 329 | @ </head> |
| 329 | 330 | @ <body> |
| 330 | - @ <h1 align="center">Fossil Repositories</h1> | |
| 331 | + @ <h1 align="center">%h(zTitle)</h1> | |
| 331 | 332 | @ %s(blob_str(&html)) |
| 332 | 333 | @ <script>%s(builtin_text("sorttable.js"))</script> |
| 333 | 334 | @ </body> |
| 334 | 335 | @ </html> |
| 335 | 336 | } |
| 336 | 337 |
| --- src/repolist.c | |
| +++ src/repolist.c | |
| @@ -316,20 +316,21 @@ | |
| 316 | style_header("Repository List"); |
| 317 | @ %s(blob_str(&html)) |
| 318 | style_table_sorter(); |
| 319 | style_finish_page(); |
| 320 | }else{ |
| 321 | /* If no repositories were found that had the "repolist_skin" |
| 322 | ** property set, then use a default skin */ |
| 323 | @ <html> |
| 324 | @ <head> |
| 325 | @ <base href="%s(g.zBaseURL)/"> |
| 326 | @ <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 327 | @ <title>Repository List</title> |
| 328 | @ </head> |
| 329 | @ <body> |
| 330 | @ <h1 align="center">Fossil Repositories</h1> |
| 331 | @ %s(blob_str(&html)) |
| 332 | @ <script>%s(builtin_text("sorttable.js"))</script> |
| 333 | @ </body> |
| 334 | @ </html> |
| 335 | } |
| 336 |
| --- src/repolist.c | |
| +++ src/repolist.c | |
| @@ -316,20 +316,21 @@ | |
| 316 | style_header("Repository List"); |
| 317 | @ %s(blob_str(&html)) |
| 318 | style_table_sorter(); |
| 319 | style_finish_page(); |
| 320 | }else{ |
| 321 | const char *zTitle = PD("FOSSIL_REPOLIST_TITLE","Repository List"); |
| 322 | /* If no repositories were found that had the "repolist_skin" |
| 323 | ** property set, then use a default skin */ |
| 324 | @ <html> |
| 325 | @ <head> |
| 326 | @ <base href="%s(g.zBaseURL)/"> |
| 327 | @ <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| 328 | @ <title>%h(zTitle)</title> |
| 329 | @ </head> |
| 330 | @ <body> |
| 331 | @ <h1 align="center">%h(zTitle)</h1> |
| 332 | @ %s(blob_str(&html)) |
| 333 | @ <script>%s(builtin_text("sorttable.js"))</script> |
| 334 | @ </body> |
| 335 | @ </html> |
| 336 | } |
| 337 |
+1
-1
| --- src/schema.c | ||
| +++ src/schema.c | ||
| @@ -511,11 +511,11 @@ | ||
| 511 | 511 | ** Predefined tagid values |
| 512 | 512 | */ |
| 513 | 513 | #if INTERFACE |
| 514 | 514 | # define TAG_BGCOLOR 1 /* Set the background color for display */ |
| 515 | 515 | # define TAG_COMMENT 2 /* The check-in comment */ |
| 516 | -# define TAG_USER 3 /* User who made a checking */ | |
| 516 | +# define TAG_USER 3 /* User who made a check-in */ | |
| 517 | 517 | # define TAG_DATE 4 /* The date of a check-in */ |
| 518 | 518 | # define TAG_HIDDEN 5 /* Do not display in timeline */ |
| 519 | 519 | # define TAG_PRIVATE 6 /* Do not sync */ |
| 520 | 520 | # define TAG_CLUSTER 7 /* A cluster */ |
| 521 | 521 | # define TAG_BRANCH 8 /* Value is name of the current branch */ |
| 522 | 522 |
| --- src/schema.c | |
| +++ src/schema.c | |
| @@ -511,11 +511,11 @@ | |
| 511 | ** Predefined tagid values |
| 512 | */ |
| 513 | #if INTERFACE |
| 514 | # define TAG_BGCOLOR 1 /* Set the background color for display */ |
| 515 | # define TAG_COMMENT 2 /* The check-in comment */ |
| 516 | # define TAG_USER 3 /* User who made a checking */ |
| 517 | # define TAG_DATE 4 /* The date of a check-in */ |
| 518 | # define TAG_HIDDEN 5 /* Do not display in timeline */ |
| 519 | # define TAG_PRIVATE 6 /* Do not sync */ |
| 520 | # define TAG_CLUSTER 7 /* A cluster */ |
| 521 | # define TAG_BRANCH 8 /* Value is name of the current branch */ |
| 522 |
| --- src/schema.c | |
| +++ src/schema.c | |
| @@ -511,11 +511,11 @@ | |
| 511 | ** Predefined tagid values |
| 512 | */ |
| 513 | #if INTERFACE |
| 514 | # define TAG_BGCOLOR 1 /* Set the background color for display */ |
| 515 | # define TAG_COMMENT 2 /* The check-in comment */ |
| 516 | # define TAG_USER 3 /* User who made a check-in */ |
| 517 | # define TAG_DATE 4 /* The date of a check-in */ |
| 518 | # define TAG_HIDDEN 5 /* Do not display in timeline */ |
| 519 | # define TAG_PRIVATE 6 /* Do not sync */ |
| 520 | # define TAG_CLUSTER 7 /* A cluster */ |
| 521 | # define TAG_BRANCH 8 /* Value is name of the current branch */ |
| 522 |
+240
-112
| --- src/search.c | ||
| +++ src/search.c | ||
| @@ -564,90 +564,142 @@ | ||
| 564 | 564 | /* |
| 565 | 565 | ** Testing the search function. |
| 566 | 566 | ** |
| 567 | 567 | ** COMMAND: search* |
| 568 | 568 | ** |
| 569 | -** Usage: %fossil search [-a|-all] [-n|-limit #] [-W|-width #] pattern... | |
| 570 | -** | |
| 571 | -** Search for timeline entries matching all words provided on the | |
| 572 | -** command line. Whole-word matches scope more highly than partial | |
| 573 | -** matches. | |
| 574 | -** | |
| 575 | -** Note: This command only searches the EVENT table. So it will only | |
| 576 | -** display check-in comments or other comments that appear on an | |
| 577 | -** unaugmented timeline. It does not search document text or forum | |
| 578 | -** messages. | |
| 579 | -** | |
| 580 | -** Outputs, by default, some top-N fraction of the results. The -all | |
| 581 | -** option can be used to output all matches, regardless of their search | |
| 582 | -** score. The -limit option can be used to limit the number of entries | |
| 583 | -** returned. The -width option can be used to set the output width used | |
| 584 | -** when printing matches. | |
| 569 | +** Usage: %fossil search [OPTIONS] PATTERN... | |
| 570 | +** | |
| 571 | +** Search the repository for PATTERN and show matches. Depending on | |
| 572 | +** options and how the administrator has search configured for the | |
| 573 | +** repository, the search can cover: | |
| 574 | +** | |
| 575 | +** * check-in comments (-c) | |
| 576 | +** * embedded documentation (--docs) | |
| 577 | +** * forum posts (--forum) | |
| 578 | +** * tickets (--tickets) | |
| 579 | +** * tech notes (--technotes) | |
| 580 | +** * wiki pages (--wiki) | |
| 581 | +** * built-in fossil help text (-h) | |
| 582 | +** * all of the above (-a) | |
| 583 | +** | |
| 584 | +** Use options below to select the scope of the search. The | |
| 585 | +** default is check-in comments only (-c). | |
| 586 | +** | |
| 587 | +** Output is colorized if writing to a TTY and if the NO_COLOR environment | |
| 588 | +** variable is not set. Use the "--highlight 0" option to disable colorization | |
| 589 | +** or use "--highlight 91" to force it on. Change the argument to --highlight | |
| 590 | +** to change the color. | |
| 585 | 591 | ** |
| 586 | 592 | ** Options: |
| 587 | -** -a|--all Output all matches, not just best matches | |
| 588 | -** --debug Show additional debug content on --fts search | |
| 589 | -** --fts Use the full-text search mechanism (testing only) | |
| 593 | +** -a|--all Search everything | |
| 594 | +** -c|--checkins Search checkin comments | |
| 595 | +** --docs Search embedded documentation | |
| 596 | +** --forum Search forum posts | |
| 597 | +** -h|--bi-help Search built-in help | |
| 598 | +** --highlight N Used VT100 color N for matching text. 0 means "off". | |
| 590 | 599 | ** -n|--limit N Limit output to N matches |
| 591 | -** --scope SCOPE Scope of search. Valid for --fts only. One or | |
| 592 | -** more of: all, c, d, e, f, t, w. Defaults to all. | |
| 600 | +** --technotes Search tech notes | |
| 601 | +** --tickets Search tickets | |
| 593 | 602 | ** -W|--width WIDTH Set display width to WIDTH columns, 0 for |
| 594 | -** unlimited. Defaults the terminal's width. | |
| 603 | +** unlimited. Defaults to the terminal's width. | |
| 604 | +** --wiki Search wiki | |
| 595 | 605 | */ |
| 596 | 606 | void search_cmd(void){ |
| 597 | 607 | Blob pattern; |
| 598 | 608 | int i; |
| 599 | 609 | Blob sql = empty_blob; |
| 600 | 610 | Stmt q; |
| 601 | 611 | int iBest; |
| 612 | + int srchFlags = 0; | |
| 613 | + int bFts = 1; /* Use FTS search by default now */ | |
| 602 | 614 | char fAll = NULL != find_option("all", "a", 0); |
| 603 | 615 | const char *zLimit = find_option("limit","n",1); |
| 616 | + const char *zScope = 0; | |
| 604 | 617 | const char *zWidth = find_option("width","W",1); |
| 605 | - const char *zScope = find_option("scope",0,1); | |
| 606 | - int bDebug = find_option("debug",0,0)!=0; | |
| 618 | + int bDebug = find_option("debug",0,0)!=0; /* Undocumented */ | |
| 607 | 619 | int nLimit = zLimit ? atoi(zLimit) : -1000; |
| 608 | 620 | int width; |
| 609 | - int bFts = find_option("fts",0,0)!=0; | |
| 621 | + int nTty = 0; /* VT100 highlight color for matching text */ | |
| 622 | + const char *zHighlight = 0; | |
| 623 | + | |
| 624 | + nTty = terminal_is_vt100(); | |
| 625 | + | |
| 626 | + /* Undocumented option to change highlight color */ | |
| 627 | + zHighlight = find_option("highlight",0,1); | |
| 628 | + if( zHighlight ) nTty = atoi(zHighlight); | |
| 629 | + | |
| 630 | + /* Undocumented option (legacy) */ | |
| 631 | + zScope = find_option("scope",0,1); | |
| 632 | + | |
| 633 | + if( find_option("fts",0,0)!=0 ) bFts = 1; /* Undocumented legacy */ | |
| 634 | + if( find_option("legacy",0,0)!=0 ) bFts = 0; /* Undocumented */ | |
| 610 | 635 | |
| 611 | 636 | if( zWidth ){ |
| 612 | 637 | width = atoi(zWidth); |
| 613 | 638 | if( (width!=0) && (width<=20) ){ |
| 614 | 639 | fossil_fatal("-W|--width value must be >20 or 0"); |
| 615 | 640 | } |
| 616 | 641 | }else{ |
| 617 | 642 | width = -1; |
| 618 | 643 | } |
| 644 | + if( zScope ){ | |
| 645 | + for(i=0; zScope[i]; i++){ | |
| 646 | + switch( zScope[i] ){ | |
| 647 | + case 'a': srchFlags = SRCH_ALL; break; | |
| 648 | + case 'c': srchFlags |= SRCH_CKIN; break; | |
| 649 | + case 'd': srchFlags |= SRCH_DOC; break; | |
| 650 | + case 'e': srchFlags |= SRCH_TECHNOTE; break; | |
| 651 | + case 'f': srchFlags |= SRCH_FORUM; break; | |
| 652 | + case 'h': srchFlags |= SRCH_HELP; break; | |
| 653 | + case 't': srchFlags |= SRCH_TKT; break; | |
| 654 | + case 'w': srchFlags |= SRCH_WIKI; break; | |
| 655 | + } | |
| 656 | + } | |
| 657 | + bFts = 1; | |
| 658 | + } | |
| 659 | + if( find_option("all","a",0) ){ srchFlags |= SRCH_ALL; bFts = 1; } | |
| 660 | + if( find_option("bi-help","h",0) ){ srchFlags |= SRCH_HELP; bFts = 1; } | |
| 661 | + if( find_option("checkins","c",0) ){ srchFlags |= SRCH_CKIN; bFts = 1; } | |
| 662 | + if( find_option("docs",0,0) ){ srchFlags |= SRCH_DOC; bFts = 1; } | |
| 663 | + if( find_option("forum",0,0) ){ srchFlags |= SRCH_FORUM; bFts = 1; } | |
| 664 | + if( find_option("technotes",0,0) ){ srchFlags |= SRCH_TECHNOTE; bFts = 1; } | |
| 665 | + if( find_option("tickets",0,0) ){ srchFlags |= SRCH_TKT; bFts = 1; } | |
| 666 | + if( find_option("wiki",0,0) ){ srchFlags |= SRCH_WIKI; bFts = 1; } | |
| 667 | + | |
| 668 | + /* If no search objects are specified, default to "check-in comments" */ | |
| 669 | + if( srchFlags==0 ) srchFlags = SRCH_CKIN; | |
| 670 | + | |
| 619 | 671 | |
| 620 | 672 | db_find_and_open_repository(0, 0); |
| 673 | + verify_all_options(); | |
| 621 | 674 | if( g.argc<3 ) return; |
| 675 | + login_set_capabilities("s", 0); | |
| 676 | + if( search_restrict(srchFlags)==0 && (srchFlags & SRCH_HELP)==0 ){ | |
| 677 | + const char *zC1 = 0, *zPlural = "s"; | |
| 678 | + if( srchFlags & SRCH_TECHNOTE ){ zC1 = "technote"; } | |
| 679 | + if( srchFlags & SRCH_TKT ){ zC1 = "ticket"; } | |
| 680 | + if( srchFlags & SRCH_FORUM ){ zC1 = "forum"; zPlural = ""; } | |
| 681 | + if( srchFlags & SRCH_DOC ){ zC1 = "document"; } | |
| 682 | + if( srchFlags & SRCH_WIKI ){ zC1 = "wiki"; zPlural = ""; } | |
| 683 | + if( srchFlags & SRCH_CKIN ){ zC1 = "check-in"; } | |
| 684 | + fossil_print( | |
| 685 | + "Search of %s%s is disabled on this repository.\n" | |
| 686 | + "Enable using \"fossil fts-config enable %s\".\n", | |
| 687 | + zC1, zPlural, zC1 | |
| 688 | + ); | |
| 689 | + return; | |
| 690 | + } | |
| 691 | + | |
| 622 | 692 | blob_init(&pattern, g.argv[2], -1); |
| 623 | 693 | for(i=3; i<g.argc; i++){ |
| 624 | 694 | blob_appendf(&pattern, " %s", g.argv[i]); |
| 625 | 695 | } |
| 626 | 696 | if( bFts ){ |
| 627 | 697 | /* Search using FTS */ |
| 628 | 698 | Blob com; |
| 629 | 699 | Blob snip; |
| 630 | 700 | const char *zPattern = blob_str(&pattern); |
| 631 | - int srchFlags; | |
| 632 | - unsigned int j; | |
| 633 | - if( zScope==0 ){ | |
| 634 | - srchFlags = SRCH_ALL; | |
| 635 | - }else{ | |
| 636 | - srchFlags = 0; | |
| 637 | - for(i=0; zScope[i]; i++){ | |
| 638 | - switch( zScope[i] ){ | |
| 639 | - case 'a': srchFlags = SRCH_ALL; break; | |
| 640 | - case 'c': srchFlags |= SRCH_CKIN; break; | |
| 641 | - case 'd': srchFlags |= SRCH_DOC; break; | |
| 642 | - case 'e': srchFlags |= SRCH_TECHNOTE; break; | |
| 643 | - case 'f': srchFlags |= SRCH_FORUM; break; | |
| 644 | - case 't': srchFlags |= SRCH_TKT; break; | |
| 645 | - case 'w': srchFlags |= SRCH_WIKI; break; | |
| 646 | - } | |
| 647 | - } | |
| 648 | - } | |
| 649 | 701 | search_sql_setup(g.db); |
| 650 | 702 | add_content_sql_commands(g.db); |
| 651 | 703 | db_multi_exec( |
| 652 | 704 | "CREATE TEMP TABLE x(label,url,score,id,date,snip);" |
| 653 | 705 | ); |
| @@ -654,30 +706,32 @@ | ||
| 654 | 706 | if( !search_index_exists() ){ |
| 655 | 707 | search_fullscan(zPattern, srchFlags); /* Full-scan search */ |
| 656 | 708 | }else{ |
| 657 | 709 | search_update_index(srchFlags); /* Update the index */ |
| 658 | 710 | search_indexed(zPattern, srchFlags); /* Indexed search */ |
| 711 | + if( srchFlags & SRCH_HELP ){ | |
| 712 | + search_fullscan(zPattern, SRCH_HELP); | |
| 713 | + } | |
| 659 | 714 | } |
| 660 | 715 | db_prepare(&q, "SELECT snip, label, score, id, date" |
| 661 | 716 | " FROM x" |
| 662 | 717 | " ORDER BY score DESC, date DESC;"); |
| 663 | 718 | blob_init(&com, 0, 0); |
| 664 | 719 | blob_init(&snip, 0, 0); |
| 665 | - if( width<0 ) width = 80; | |
| 720 | + if( width<0 ) width = terminal_get_width(80); | |
| 666 | 721 | while( db_step(&q)==SQLITE_ROW ){ |
| 667 | 722 | const char *zSnippet = db_column_text(&q, 0); |
| 668 | 723 | const char *zLabel = db_column_text(&q, 1); |
| 669 | 724 | const char *zDate = db_column_text(&q, 4); |
| 670 | 725 | const char *zScore = db_column_text(&q, 2); |
| 671 | 726 | const char *zId = db_column_text(&q, 3); |
| 727 | + char *zOrig; | |
| 672 | 728 | blob_appendf(&snip, "%s", zSnippet); |
| 673 | - for(j=0; j<snip.nUsed; j++){ | |
| 674 | - if( snip.aData[j]=='\n' ){ | |
| 675 | - if( j>0 && snip.aData[j-1]=='\r' ) snip.aData[j-1] = ' '; | |
| 676 | - snip.aData[j] = ' '; | |
| 677 | - } | |
| 678 | - } | |
| 729 | + zOrig = blob_materialize(&snip); | |
| 730 | + blob_init(&snip, 0, 0); | |
| 731 | + html_to_plaintext(zOrig, &snip, (nTty?HTOT_VT100:0)|HTOT_FLOW|HTOT_TRIM); | |
| 732 | + fossil_free(zOrig); | |
| 679 | 733 | blob_appendf(&com, "%s\n%s\n%s", zLabel, blob_str(&snip), zDate); |
| 680 | 734 | if( bDebug ){ |
| 681 | 735 | blob_appendf(&com," score: %s id: %s", zScore, zId); |
| 682 | 736 | } |
| 683 | 737 | comment_print(blob_str(&com), 0, 5, width, |
| @@ -731,28 +785,33 @@ | ||
| 731 | 785 | #define SRCH_DOC 0x0002 /* Search over embedded documents */ |
| 732 | 786 | #define SRCH_TKT 0x0004 /* Search over tickets */ |
| 733 | 787 | #define SRCH_WIKI 0x0008 /* Search over wiki */ |
| 734 | 788 | #define SRCH_TECHNOTE 0x0010 /* Search over tech notes */ |
| 735 | 789 | #define SRCH_FORUM 0x0020 /* Search over forum messages */ |
| 736 | -#define SRCH_ALL 0x003f /* Search over everything */ | |
| 790 | +#define SRCH_HELP 0x0040 /* Search built-in help (full-scan only) */ | |
| 791 | +#define SRCH_ALL 0x007f /* Search over everything */ | |
| 737 | 792 | #endif |
| 738 | 793 | |
| 739 | 794 | /* |
| 740 | 795 | ** Remove bits from srchFlags which are disallowed by either the |
| 741 | 796 | ** current server configuration or by user permissions. Return |
| 742 | 797 | ** the revised search flags mask. |
| 798 | +** | |
| 799 | +** If bFlex is true, that means allow through the SRCH_HELP option | |
| 800 | +** even if it is not explicitly enabled. | |
| 743 | 801 | */ |
| 744 | 802 | unsigned int search_restrict(unsigned int srchFlags){ |
| 745 | 803 | static unsigned int knownGood = 0; |
| 746 | 804 | static unsigned int knownBad = 0; |
| 747 | 805 | static const struct { unsigned m; const char *zKey; } aSetng[] = { |
| 748 | - { SRCH_CKIN, "search-ci" }, | |
| 749 | - { SRCH_DOC, "search-doc" }, | |
| 750 | - { SRCH_TKT, "search-tkt" }, | |
| 751 | - { SRCH_WIKI, "search-wiki" }, | |
| 806 | + { SRCH_CKIN, "search-ci" }, | |
| 807 | + { SRCH_DOC, "search-doc" }, | |
| 808 | + { SRCH_TKT, "search-tkt" }, | |
| 809 | + { SRCH_WIKI, "search-wiki" }, | |
| 752 | 810 | { SRCH_TECHNOTE, "search-technote" }, |
| 753 | - { SRCH_FORUM, "search-forum" }, | |
| 811 | + { SRCH_FORUM, "search-forum" }, | |
| 812 | + { SRCH_HELP, "search-help" }, | |
| 754 | 813 | }; |
| 755 | 814 | int i; |
| 756 | 815 | if( g.perm.Read==0 ) srchFlags &= ~(SRCH_CKIN|SRCH_DOC|SRCH_TECHNOTE); |
| 757 | 816 | if( g.perm.RdTkt==0 ) srchFlags &= ~(SRCH_TKT); |
| 758 | 817 | if( g.perm.RdWiki==0 ) srchFlags &= ~(SRCH_WIKI); |
| @@ -912,10 +971,31 @@ | ||
| 912 | 971 | " search_snippet()" |
| 913 | 972 | " FROM event JOIN blob on event.objid=blob.rid" |
| 914 | 973 | " WHERE search_match('',body('f',rid,NULL));" |
| 915 | 974 | ); |
| 916 | 975 | } |
| 976 | + if( (srchFlags & SRCH_HELP)!=0 ){ | |
| 977 | + const char *zPrefix; | |
| 978 | + helptext_vtab_register(g.db); | |
| 979 | + if( srchFlags==SRCH_HELP ){ | |
| 980 | + zPrefix = "The"; | |
| 981 | + }else{ | |
| 982 | + zPrefix = "Built-in help for the"; | |
| 983 | + } | |
| 984 | + db_multi_exec( | |
| 985 | + "INSERT INTO x(label,url,score,id,snip)" | |
| 986 | + " SELECT format('%q \"%%s\" %%s',name,type)," | |
| 987 | + " '/help?cmd='||name," | |
| 988 | + " search_score()," | |
| 989 | + " 'h'||rowid," | |
| 990 | + " search_snippet()" | |
| 991 | + " FROM helptext" | |
| 992 | + " WHERE search_match(format('the \"%%s\" %%s',name,type)," | |
| 993 | + " helptext.helptext);", | |
| 994 | + zPrefix | |
| 995 | + ); | |
| 996 | + } | |
| 917 | 997 | } |
| 918 | 998 | |
| 919 | 999 | /* |
| 920 | 1000 | ** Number of significant bits in a u32 |
| 921 | 1001 | */ |
| @@ -1068,10 +1148,11 @@ | ||
| 1068 | 1148 | { SRCH_DOC, 'd' }, |
| 1069 | 1149 | { SRCH_TKT, 't' }, |
| 1070 | 1150 | { SRCH_WIKI, 'w' }, |
| 1071 | 1151 | { SRCH_TECHNOTE, 'e' }, |
| 1072 | 1152 | { SRCH_FORUM, 'f' }, |
| 1153 | + { SRCH_HELP, 'h' }, | |
| 1073 | 1154 | }; |
| 1074 | 1155 | int i; |
| 1075 | 1156 | for(i=0; i<count(aMask); i++){ |
| 1076 | 1157 | if( srchFlags & aMask[i].m ){ |
| 1077 | 1158 | blob_appendf(&sql, "%sftsdocs.type='%c'", zSep, aMask[i].c); |
| @@ -1157,11 +1238,11 @@ | ||
| 1157 | 1238 | int nLimit = db_get_int("search-limit", 100); |
| 1158 | 1239 | |
| 1159 | 1240 | if( P("searchlimit")!=0 ){ |
| 1160 | 1241 | nLimit = atoi(P("searchlimit")); |
| 1161 | 1242 | } |
| 1162 | - srchFlags = search_restrict(srchFlags); | |
| 1243 | + srchFlags = search_restrict(srchFlags) | (srchFlags & SRCH_HELP); | |
| 1163 | 1244 | if( srchFlags==0 ) return 0; |
| 1164 | 1245 | search_sql_setup(g.db); |
| 1165 | 1246 | add_content_sql_commands(g.db); |
| 1166 | 1247 | db_multi_exec( |
| 1167 | 1248 | "CREATE TEMP TABLE x(label,url,score,id,date,snip);" |
| @@ -1169,10 +1250,13 @@ | ||
| 1169 | 1250 | if( !search_index_exists() ){ |
| 1170 | 1251 | search_fullscan(zPattern, srchFlags); /* Full-scan search */ |
| 1171 | 1252 | }else{ |
| 1172 | 1253 | search_update_index(srchFlags); /* Update the index, if necessary */ |
| 1173 | 1254 | search_indexed(zPattern, srchFlags); /* Indexed search */ |
| 1255 | + if( srchFlags & SRCH_HELP ){ | |
| 1256 | + search_fullscan(zPattern, SRCH_HELP); | |
| 1257 | + } | |
| 1174 | 1258 | } |
| 1175 | 1259 | db_prepare(&q, "SELECT url, snip, label, score, id, substr(date,1,10)" |
| 1176 | 1260 | " FROM x" |
| 1177 | 1261 | " ORDER BY score DESC, date DESC;"); |
| 1178 | 1262 | while( db_step(&q)==SQLITE_ROW ){ |
| @@ -1223,28 +1307,35 @@ | ||
| 1223 | 1307 | ** |
| 1224 | 1308 | ** 0x02 Show nothing if search is disabled. |
| 1225 | 1309 | ** |
| 1226 | 1310 | ** Return true if there are search results. |
| 1227 | 1311 | */ |
| 1228 | -int search_screen(unsigned srchFlags, int mFlags){ | |
| 1312 | +int search_screen(unsigned srchAllowed, int mFlags){ | |
| 1229 | 1313 | const char *zType = 0; |
| 1230 | 1314 | const char *zClass = 0; |
| 1231 | 1315 | const char *zDisable1; |
| 1232 | 1316 | const char *zDisable2; |
| 1233 | 1317 | const char *zPattern; |
| 1234 | 1318 | int fDebug = PB("debug"); |
| 1235 | 1319 | int haveResult = 0; |
| 1236 | - srchFlags = search_restrict(srchFlags); | |
| 1237 | - switch( srchFlags ){ | |
| 1320 | + int srchThisTime; | |
| 1321 | + const char *zY = PD("y","all"); | |
| 1322 | + if( zY[0]=='h' && zY[1]==0 ){ | |
| 1323 | + srchAllowed = search_restrict(srchAllowed) | (srchAllowed & SRCH_HELP); | |
| 1324 | + }else{ | |
| 1325 | + srchAllowed = search_restrict(srchAllowed); | |
| 1326 | + } | |
| 1327 | + switch( srchAllowed ){ | |
| 1238 | 1328 | case SRCH_CKIN: zType = " Check-ins"; zClass = "Ckin"; break; |
| 1239 | 1329 | case SRCH_DOC: zType = " Docs"; zClass = "Doc"; break; |
| 1240 | 1330 | case SRCH_TKT: zType = " Tickets"; zClass = "Tkt"; break; |
| 1241 | 1331 | case SRCH_WIKI: zType = " Wiki"; zClass = "Wiki"; break; |
| 1242 | 1332 | case SRCH_TECHNOTE: zType = " Tech Notes"; zClass = "Note"; break; |
| 1243 | 1333 | case SRCH_FORUM: zType = " Forum"; zClass = "Frm"; break; |
| 1334 | + case SRCH_HELP: zType = " Help"; zClass = "Hlp"; break; | |
| 1244 | 1335 | } |
| 1245 | - if( srchFlags==0 ){ | |
| 1336 | + if( srchAllowed==0 ){ | |
| 1246 | 1337 | if( mFlags & 0x02 ) return 0; |
| 1247 | 1338 | zDisable1 = " disabled"; |
| 1248 | 1339 | zDisable2 = " disabled"; |
| 1249 | 1340 | zPattern = ""; |
| 1250 | 1341 | }else{ |
| @@ -1257,41 +1348,46 @@ | ||
| 1257 | 1348 | @ <div class='searchForm searchForm%s(zClass)'> |
| 1258 | 1349 | }else{ |
| 1259 | 1350 | @ <div class='searchForm'> |
| 1260 | 1351 | } |
| 1261 | 1352 | @ <input type="text" name="s" size="40" value="%h(zPattern)"%s(zDisable1)> |
| 1262 | - if( (mFlags & 0x01)!=0 && (srchFlags & (srchFlags-1))!=0 ){ | |
| 1263 | - static const struct { const char *z; const char *zNm; unsigned m; } aY[] = { | |
| 1353 | + srchThisTime = srchAllowed; | |
| 1354 | + if( (mFlags & 0x01)!=0 && (srchAllowed & (srchAllowed-1))!=0 ){ | |
| 1355 | + static const struct { | |
| 1356 | + const char *z; | |
| 1357 | + const char *zNm; | |
| 1358 | + unsigned m; | |
| 1359 | + } aY[] = { | |
| 1264 | 1360 | { "all", "All", SRCH_ALL }, |
| 1265 | 1361 | { "c", "Check-ins", SRCH_CKIN }, |
| 1266 | 1362 | { "d", "Docs", SRCH_DOC }, |
| 1267 | 1363 | { "t", "Tickets", SRCH_TKT }, |
| 1268 | 1364 | { "w", "Wiki", SRCH_WIKI }, |
| 1269 | 1365 | { "e", "Tech Notes", SRCH_TECHNOTE }, |
| 1270 | 1366 | { "f", "Forum", SRCH_FORUM }, |
| 1367 | + { "h", "Help", SRCH_HELP }, | |
| 1271 | 1368 | }; |
| 1272 | - const char *zY = PD("y","all"); | |
| 1273 | - unsigned newFlags = srchFlags; | |
| 1274 | 1369 | int i; |
| 1275 | 1370 | @ <select size='1' name='y'> |
| 1276 | 1371 | for(i=0; i<count(aY); i++){ |
| 1277 | - if( (aY[i].m & srchFlags)==0 ) continue; | |
| 1372 | + if( (aY[i].m & srchAllowed)==0 ) continue; | |
| 1373 | + if( aY[i].m==SRCH_HELP && fossil_strcmp(zY,"h")!=0 | |
| 1374 | + && search_restrict(SRCH_HELP)==0 ) continue; | |
| 1278 | 1375 | cgi_printf("<option value='%s'", aY[i].z); |
| 1279 | 1376 | if( fossil_strcmp(zY,aY[i].z)==0 ){ |
| 1280 | - newFlags &= aY[i].m; | |
| 1377 | + srchThisTime &= aY[i].m; | |
| 1281 | 1378 | cgi_printf(" selected"); |
| 1282 | 1379 | } |
| 1283 | 1380 | cgi_printf(">%s</option>\n", aY[i].zNm); |
| 1284 | 1381 | } |
| 1285 | 1382 | @ </select> |
| 1286 | - srchFlags = newFlags; | |
| 1287 | 1383 | } |
| 1288 | 1384 | if( fDebug ){ |
| 1289 | 1385 | @ <input type="hidden" name="debug" value="1"> |
| 1290 | 1386 | } |
| 1291 | 1387 | @ <input type="submit" value="Search%s(zType)"%s(zDisable2)> |
| 1292 | - if( srchFlags==0 ){ | |
| 1388 | + if( srchAllowed==0 && srchThisTime==0 ){ | |
| 1293 | 1389 | @ <p class="generalError">Search is disabled</p> |
| 1294 | 1390 | } |
| 1295 | 1391 | @ </div></form> |
| 1296 | 1392 | while( fossil_isspace(zPattern[0]) ) zPattern++; |
| 1297 | 1393 | if( zPattern[0] ){ |
| @@ -1298,11 +1394,11 @@ | ||
| 1298 | 1394 | if( zClass ){ |
| 1299 | 1395 | @ <div class='searchResult searchResult%s(zClass)'> |
| 1300 | 1396 | }else{ |
| 1301 | 1397 | @ <div class='searchResult'> |
| 1302 | 1398 | } |
| 1303 | - if( search_run_and_output(zPattern, srchFlags, fDebug)==0 ){ | |
| 1399 | + if( search_run_and_output(zPattern, srchThisTime, fDebug)==0 ){ | |
| 1304 | 1400 | @ <p class='searchEmpty'>No matches for: <span>%h(zPattern)</span></p> |
| 1305 | 1401 | } |
| 1306 | 1402 | @ </div> |
| 1307 | 1403 | haveResult = 1; |
| 1308 | 1404 | } |
| @@ -1315,17 +1411,18 @@ | ||
| 1315 | 1411 | ** Search for check-in comments, documents, tickets, or wiki that |
| 1316 | 1412 | ** match a user-supplied pattern. |
| 1317 | 1413 | ** |
| 1318 | 1414 | ** s=PATTERN Specify the full-text pattern to search for |
| 1319 | 1415 | ** y=TYPE What to search. |
| 1320 | -** c -> check-ins | |
| 1321 | -** d -> documentation | |
| 1322 | -** t -> tickets | |
| 1323 | -** w -> wiki | |
| 1324 | -** e -> tech notes | |
| 1325 | -** f -> forum | |
| 1326 | -** all -> everything | |
| 1416 | +** c -> check-ins, | |
| 1417 | +** d -> documentation, | |
| 1418 | +** t -> tickets, | |
| 1419 | +** w -> wiki, | |
| 1420 | +** e -> tech notes, | |
| 1421 | +** f -> forum, | |
| 1422 | +** h -> built-in help, | |
| 1423 | +** all -> everything. | |
| 1327 | 1424 | */ |
| 1328 | 1425 | void search_page(void){ |
| 1329 | 1426 | const int isSearch = P("s")!=0; |
| 1330 | 1427 | login_check_credentials(); |
| 1331 | 1428 | style_header("Search%s", isSearch ? " Results" : ""); |
| @@ -1374,20 +1471,20 @@ | ||
| 1374 | 1471 | }else{ |
| 1375 | 1472 | blob_append(pOut, "\n", 1); |
| 1376 | 1473 | wiki_convert(pIn, &html, 0); |
| 1377 | 1474 | } |
| 1378 | 1475 | } |
| 1379 | - html_to_plaintext(blob_str(&html), pOut); | |
| 1476 | + html_to_plaintext(blob_str(&html), pOut, 0); | |
| 1380 | 1477 | }else if( fossil_strcmp(zMimetype,"text/x-markdown")==0 ){ |
| 1381 | 1478 | markdown_to_html(pIn, blob_size(&title) ? NULL : &title, &html); |
| 1382 | 1479 | }else if( fossil_strcmp(zMimetype,"text/html")==0 ){ |
| 1383 | 1480 | if( blob_size(&title)==0 ) doc_is_embedded_html(pIn, &title); |
| 1384 | 1481 | pHtml = pIn; |
| 1385 | 1482 | } |
| 1386 | 1483 | blob_appendf(pOut, "%s\n", blob_str(&title)); |
| 1387 | 1484 | if( blob_size(pHtml) ){ |
| 1388 | - html_to_plaintext(blob_str(pHtml), pOut); | |
| 1485 | + html_to_plaintext(blob_str(pHtml), pOut, 0); | |
| 1389 | 1486 | }else{ |
| 1390 | 1487 | blob_append(pOut, blob_buffer(pIn), blob_size(pIn)); |
| 1391 | 1488 | } |
| 1392 | 1489 | blob_reset(&html); |
| 1393 | 1490 | blob_reset(&title); |
| @@ -2108,32 +2205,32 @@ | ||
| 2108 | 2205 | } |
| 2109 | 2206 | fossil_print(" done\n"); |
| 2110 | 2207 | } |
| 2111 | 2208 | |
| 2112 | 2209 | /* |
| 2113 | -** COMMAND: fts-config* | |
| 2210 | +** COMMAND: fts-config* abbrv-subcom | |
| 2114 | 2211 | ** |
| 2115 | 2212 | ** Usage: fossil fts-config ?SUBCOMMAND? ?ARGUMENT? |
| 2116 | 2213 | ** |
| 2117 | 2214 | ** The "fossil fts-config" command configures the full-text search capabilities |
| 2118 | 2215 | ** of the repository. Subcommands: |
| 2119 | 2216 | ** |
| 2120 | -** reindex Rebuild the search index. This is a no-op if | |
| 2121 | -** index search is disabled | |
| 2122 | -** | |
| 2123 | -** index (on|off) Turn the search index on or off | |
| 2124 | -** | |
| 2125 | -** enable cdtwef Enable various kinds of search. c=Check-ins, | |
| 2126 | -** d=Documents, t=Tickets, w=Wiki, e=Tech Notes, | |
| 2127 | -** f=Forum. | |
| 2128 | -** | |
| 2129 | -** disable cdtwef Disable various kinds of search | |
| 2130 | -** | |
| 2131 | -** tokenizer VALUE Select a tokenizer for indexed search. VALUE | |
| 2132 | -** may be one of (porter, on, off, trigram, unicode61), | |
| 2133 | -** and "on" is equivalent to "porter". Unindexed | |
| 2134 | -** search never uses tokenization or stemming. | |
| 2217 | +** reindex Rebuild the search index. This is a no-op if | |
| 2218 | +** index search is disabled | |
| 2219 | +** | |
| 2220 | +** index (on|off) Turn the search index on or off | |
| 2221 | +** | |
| 2222 | +** enable TYPE .. Enable search for TYPE. TYPE is one of: | |
| 2223 | +** check-in, document, ticket, wiki, technote, | |
| 2224 | +** forum, help, or all | |
| 2225 | +** | |
| 2226 | +** disable TYPE ... Disable search for TYPE | |
| 2227 | +** | |
| 2228 | +** tokenizer VALUE Select a tokenizer for indexed search. VALUE | |
| 2229 | +** may be one of (porter, on, off, trigram, unicode61), | |
| 2230 | +** and "on" is equivalent to "porter". Unindexed | |
| 2231 | +** search never uses tokenization or stemming. | |
| 2135 | 2232 | ** |
| 2136 | 2233 | ** The current search settings are displayed after any changes are applied. |
| 2137 | 2234 | ** Run this command with no arguments to simply see the settings. |
| 2138 | 2235 | */ |
| 2139 | 2236 | void fts_config_cmd(void){ |
| @@ -2150,16 +2247,17 @@ | ||
| 2150 | 2247 | static const struct { |
| 2151 | 2248 | const char *zSetting; |
| 2152 | 2249 | const char *zName; |
| 2153 | 2250 | const char *zSw; |
| 2154 | 2251 | } aSetng[] = { |
| 2155 | - { "search-ci", "check-in search:", "c" }, | |
| 2156 | - { "search-doc", "document search:", "d" }, | |
| 2157 | - { "search-tkt", "ticket search:", "t" }, | |
| 2158 | - { "search-wiki", "wiki search:", "w" }, | |
| 2159 | - { "search-technote", "tech note search:", "e" }, | |
| 2160 | - { "search-forum", "forum search:", "f" }, | |
| 2252 | + { "search-ci", "check-in search:", "c" }, | |
| 2253 | + { "search-doc", "document search:", "d" }, | |
| 2254 | + { "search-tkt", "ticket search:", "t" }, | |
| 2255 | + { "search-wiki", "wiki search:", "w" }, | |
| 2256 | + { "search-technote", "technote search:", "e" }, | |
| 2257 | + { "search-forum", "forum search:", "f" }, | |
| 2258 | + { "search-help", "built-in help search:", "h" }, | |
| 2161 | 2259 | }; |
| 2162 | 2260 | char *zSubCmd = 0; |
| 2163 | 2261 | int i, j, n; |
| 2164 | 2262 | int iCmd = 0; |
| 2165 | 2263 | int iAction = 0; |
| @@ -2192,16 +2290,46 @@ | ||
| 2192 | 2290 | } |
| 2193 | 2291 | db_begin_transaction(); |
| 2194 | 2292 | |
| 2195 | 2293 | /* Adjust search settings */ |
| 2196 | 2294 | if( iCmd==3 || iCmd==4 ){ |
| 2295 | + int k; | |
| 2197 | 2296 | const char *zCtrl; |
| 2198 | - if( g.argc<4 ) usage(mprintf("%s STRING",zSubCmd)); | |
| 2199 | - zCtrl = g.argv[3]; | |
| 2200 | - for(j=0; j<count(aSetng); j++){ | |
| 2201 | - if( strchr(zCtrl, aSetng[j].zSw[0])!=0 ){ | |
| 2202 | - db_set_int(aSetng[j].zSetting/*works-like:"x"*/, iCmd-3, 0); | |
| 2297 | + for(k=2; k<g.argc; k++){ | |
| 2298 | + if( k==2 ){ | |
| 2299 | + if( g.argc<4 ){ | |
| 2300 | + zCtrl = "all"; | |
| 2301 | + }else{ | |
| 2302 | + zCtrl = g.argv[3]; | |
| 2303 | + k++; | |
| 2304 | + } | |
| 2305 | + }else{ | |
| 2306 | + zCtrl = g.argv[k]; | |
| 2307 | + } | |
| 2308 | + if( fossil_strcmp(zCtrl,"all")==0 ){ | |
| 2309 | + zCtrl = "cdtwefh"; | |
| 2310 | + } | |
| 2311 | + if( strlen(zCtrl)>=4 ){ | |
| 2312 | + /* If the argument to "enable" or "disable" is a string of at least | |
| 2313 | + ** 4 characters which matches part of any aSetng.zName, then use that | |
| 2314 | + ** one aSetng value only. */ | |
| 2315 | + char *zGlob = mprintf("*%s*", zCtrl); | |
| 2316 | + for(j=0; j<count(aSetng); j++){ | |
| 2317 | + if( sqlite3_strglob(zGlob, aSetng[j].zName)==0 ){ | |
| 2318 | + db_set_int(aSetng[j].zSetting/*works-like:"x"*/, iCmd-3, 0); | |
| 2319 | + zCtrl = 0; | |
| 2320 | + break; | |
| 2321 | + } | |
| 2322 | + } | |
| 2323 | + fossil_free(zGlob); | |
| 2324 | + } | |
| 2325 | + if( zCtrl ){ | |
| 2326 | + for(j=0; j<count(aSetng); j++){ | |
| 2327 | + if( strchr(zCtrl, aSetng[j].zSw[0])!=0 ){ | |
| 2328 | + db_set_int(aSetng[j].zSetting/*works-like:"x"*/, iCmd-3, 0); | |
| 2329 | + } | |
| 2330 | + } | |
| 2203 | 2331 | } |
| 2204 | 2332 | } |
| 2205 | 2333 | }else if( iCmd==5 ){ |
| 2206 | 2334 | int iOldTokenizer, iNewTokenizer; |
| 2207 | 2335 | if( g.argc<4 ) usage("tokenizer porter|on|off|trigram|unicode61"); |
| @@ -2224,30 +2352,30 @@ | ||
| 2224 | 2352 | search_rebuild_index(); |
| 2225 | 2353 | } |
| 2226 | 2354 | |
| 2227 | 2355 | /* Always show the status before ending */ |
| 2228 | 2356 | for(i=0; i<count(aSetng); i++){ |
| 2229 | - fossil_print("%-17s %s\n", aSetng[i].zName, | |
| 2357 | + fossil_print("%-21s %s\n", aSetng[i].zName, | |
| 2230 | 2358 | db_get_boolean(aSetng[i].zSetting,0) ? "on" : "off"); |
| 2231 | 2359 | } |
| 2232 | - fossil_print("%-17s %s\n", "tokenizer:", | |
| 2360 | + fossil_print("%-21s %s\n", "tokenizer:", | |
| 2233 | 2361 | search_tokenizer_for_string(0)); |
| 2234 | 2362 | if( search_index_exists() ){ |
| 2235 | 2363 | int pgsz = db_int64(0, "PRAGMA repository.page_size;"); |
| 2236 | 2364 | i64 nTotal = db_int64(0, "PRAGMA repository.page_count;")*pgsz; |
| 2237 | 2365 | i64 nFts = db_int64(0, "SELECT count(*) FROM dbstat" |
| 2238 | 2366 | " WHERE schema='repository'" |
| 2239 | 2367 | " AND name LIKE 'fts%%'")*pgsz; |
| 2240 | 2368 | char zSize[50]; |
| 2241 | - fossil_print("%-17s FTS%d\n", "full-text index:", search_index_type(1)); | |
| 2242 | - fossil_print("%-17s %d\n", "documents:", | |
| 2369 | + fossil_print("%-21s FTS%d\n", "full-text index:", search_index_type(1)); | |
| 2370 | + fossil_print("%-21s %d\n", "documents:", | |
| 2243 | 2371 | db_int(0, "SELECT count(*) FROM ftsdocs")); |
| 2244 | 2372 | approxSizeName(sizeof(zSize), zSize, nFts); |
| 2245 | - fossil_print("%-17s %s (%.1f%% of repository)\n", "space used", | |
| 2373 | + fossil_print("%-21s %s (%.1f%% of repository)\n", "space used", | |
| 2246 | 2374 | zSize, 100.0*((double)nFts/(double)nTotal)); |
| 2247 | 2375 | }else{ |
| 2248 | - fossil_print("%-17s disabled\n", "full-text index:"); | |
| 2376 | + fossil_print("%-21s disabled\n", "full-text index:"); | |
| 2249 | 2377 | } |
| 2250 | 2378 | db_end_transaction(0); |
| 2251 | 2379 | } |
| 2252 | 2380 | |
| 2253 | 2381 | /* |
| 2254 | 2382 |
| --- src/search.c | |
| +++ src/search.c | |
| @@ -564,90 +564,142 @@ | |
| 564 | /* |
| 565 | ** Testing the search function. |
| 566 | ** |
| 567 | ** COMMAND: search* |
| 568 | ** |
| 569 | ** Usage: %fossil search [-a|-all] [-n|-limit #] [-W|-width #] pattern... |
| 570 | ** |
| 571 | ** Search for timeline entries matching all words provided on the |
| 572 | ** command line. Whole-word matches scope more highly than partial |
| 573 | ** matches. |
| 574 | ** |
| 575 | ** Note: This command only searches the EVENT table. So it will only |
| 576 | ** display check-in comments or other comments that appear on an |
| 577 | ** unaugmented timeline. It does not search document text or forum |
| 578 | ** messages. |
| 579 | ** |
| 580 | ** Outputs, by default, some top-N fraction of the results. The -all |
| 581 | ** option can be used to output all matches, regardless of their search |
| 582 | ** score. The -limit option can be used to limit the number of entries |
| 583 | ** returned. The -width option can be used to set the output width used |
| 584 | ** when printing matches. |
| 585 | ** |
| 586 | ** Options: |
| 587 | ** -a|--all Output all matches, not just best matches |
| 588 | ** --debug Show additional debug content on --fts search |
| 589 | ** --fts Use the full-text search mechanism (testing only) |
| 590 | ** -n|--limit N Limit output to N matches |
| 591 | ** --scope SCOPE Scope of search. Valid for --fts only. One or |
| 592 | ** more of: all, c, d, e, f, t, w. Defaults to all. |
| 593 | ** -W|--width WIDTH Set display width to WIDTH columns, 0 for |
| 594 | ** unlimited. Defaults the terminal's width. |
| 595 | */ |
| 596 | void search_cmd(void){ |
| 597 | Blob pattern; |
| 598 | int i; |
| 599 | Blob sql = empty_blob; |
| 600 | Stmt q; |
| 601 | int iBest; |
| 602 | char fAll = NULL != find_option("all", "a", 0); |
| 603 | const char *zLimit = find_option("limit","n",1); |
| 604 | const char *zWidth = find_option("width","W",1); |
| 605 | const char *zScope = find_option("scope",0,1); |
| 606 | int bDebug = find_option("debug",0,0)!=0; |
| 607 | int nLimit = zLimit ? atoi(zLimit) : -1000; |
| 608 | int width; |
| 609 | int bFts = find_option("fts",0,0)!=0; |
| 610 | |
| 611 | if( zWidth ){ |
| 612 | width = atoi(zWidth); |
| 613 | if( (width!=0) && (width<=20) ){ |
| 614 | fossil_fatal("-W|--width value must be >20 or 0"); |
| 615 | } |
| 616 | }else{ |
| 617 | width = -1; |
| 618 | } |
| 619 | |
| 620 | db_find_and_open_repository(0, 0); |
| 621 | if( g.argc<3 ) return; |
| 622 | blob_init(&pattern, g.argv[2], -1); |
| 623 | for(i=3; i<g.argc; i++){ |
| 624 | blob_appendf(&pattern, " %s", g.argv[i]); |
| 625 | } |
| 626 | if( bFts ){ |
| 627 | /* Search using FTS */ |
| 628 | Blob com; |
| 629 | Blob snip; |
| 630 | const char *zPattern = blob_str(&pattern); |
| 631 | int srchFlags; |
| 632 | unsigned int j; |
| 633 | if( zScope==0 ){ |
| 634 | srchFlags = SRCH_ALL; |
| 635 | }else{ |
| 636 | srchFlags = 0; |
| 637 | for(i=0; zScope[i]; i++){ |
| 638 | switch( zScope[i] ){ |
| 639 | case 'a': srchFlags = SRCH_ALL; break; |
| 640 | case 'c': srchFlags |= SRCH_CKIN; break; |
| 641 | case 'd': srchFlags |= SRCH_DOC; break; |
| 642 | case 'e': srchFlags |= SRCH_TECHNOTE; break; |
| 643 | case 'f': srchFlags |= SRCH_FORUM; break; |
| 644 | case 't': srchFlags |= SRCH_TKT; break; |
| 645 | case 'w': srchFlags |= SRCH_WIKI; break; |
| 646 | } |
| 647 | } |
| 648 | } |
| 649 | search_sql_setup(g.db); |
| 650 | add_content_sql_commands(g.db); |
| 651 | db_multi_exec( |
| 652 | "CREATE TEMP TABLE x(label,url,score,id,date,snip);" |
| 653 | ); |
| @@ -654,30 +706,32 @@ | |
| 654 | if( !search_index_exists() ){ |
| 655 | search_fullscan(zPattern, srchFlags); /* Full-scan search */ |
| 656 | }else{ |
| 657 | search_update_index(srchFlags); /* Update the index */ |
| 658 | search_indexed(zPattern, srchFlags); /* Indexed search */ |
| 659 | } |
| 660 | db_prepare(&q, "SELECT snip, label, score, id, date" |
| 661 | " FROM x" |
| 662 | " ORDER BY score DESC, date DESC;"); |
| 663 | blob_init(&com, 0, 0); |
| 664 | blob_init(&snip, 0, 0); |
| 665 | if( width<0 ) width = 80; |
| 666 | while( db_step(&q)==SQLITE_ROW ){ |
| 667 | const char *zSnippet = db_column_text(&q, 0); |
| 668 | const char *zLabel = db_column_text(&q, 1); |
| 669 | const char *zDate = db_column_text(&q, 4); |
| 670 | const char *zScore = db_column_text(&q, 2); |
| 671 | const char *zId = db_column_text(&q, 3); |
| 672 | blob_appendf(&snip, "%s", zSnippet); |
| 673 | for(j=0; j<snip.nUsed; j++){ |
| 674 | if( snip.aData[j]=='\n' ){ |
| 675 | if( j>0 && snip.aData[j-1]=='\r' ) snip.aData[j-1] = ' '; |
| 676 | snip.aData[j] = ' '; |
| 677 | } |
| 678 | } |
| 679 | blob_appendf(&com, "%s\n%s\n%s", zLabel, blob_str(&snip), zDate); |
| 680 | if( bDebug ){ |
| 681 | blob_appendf(&com," score: %s id: %s", zScore, zId); |
| 682 | } |
| 683 | comment_print(blob_str(&com), 0, 5, width, |
| @@ -731,28 +785,33 @@ | |
| 731 | #define SRCH_DOC 0x0002 /* Search over embedded documents */ |
| 732 | #define SRCH_TKT 0x0004 /* Search over tickets */ |
| 733 | #define SRCH_WIKI 0x0008 /* Search over wiki */ |
| 734 | #define SRCH_TECHNOTE 0x0010 /* Search over tech notes */ |
| 735 | #define SRCH_FORUM 0x0020 /* Search over forum messages */ |
| 736 | #define SRCH_ALL 0x003f /* Search over everything */ |
| 737 | #endif |
| 738 | |
| 739 | /* |
| 740 | ** Remove bits from srchFlags which are disallowed by either the |
| 741 | ** current server configuration or by user permissions. Return |
| 742 | ** the revised search flags mask. |
| 743 | */ |
| 744 | unsigned int search_restrict(unsigned int srchFlags){ |
| 745 | static unsigned int knownGood = 0; |
| 746 | static unsigned int knownBad = 0; |
| 747 | static const struct { unsigned m; const char *zKey; } aSetng[] = { |
| 748 | { SRCH_CKIN, "search-ci" }, |
| 749 | { SRCH_DOC, "search-doc" }, |
| 750 | { SRCH_TKT, "search-tkt" }, |
| 751 | { SRCH_WIKI, "search-wiki" }, |
| 752 | { SRCH_TECHNOTE, "search-technote" }, |
| 753 | { SRCH_FORUM, "search-forum" }, |
| 754 | }; |
| 755 | int i; |
| 756 | if( g.perm.Read==0 ) srchFlags &= ~(SRCH_CKIN|SRCH_DOC|SRCH_TECHNOTE); |
| 757 | if( g.perm.RdTkt==0 ) srchFlags &= ~(SRCH_TKT); |
| 758 | if( g.perm.RdWiki==0 ) srchFlags &= ~(SRCH_WIKI); |
| @@ -912,10 +971,31 @@ | |
| 912 | " search_snippet()" |
| 913 | " FROM event JOIN blob on event.objid=blob.rid" |
| 914 | " WHERE search_match('',body('f',rid,NULL));" |
| 915 | ); |
| 916 | } |
| 917 | } |
| 918 | |
| 919 | /* |
| 920 | ** Number of significant bits in a u32 |
| 921 | */ |
| @@ -1068,10 +1148,11 @@ | |
| 1068 | { SRCH_DOC, 'd' }, |
| 1069 | { SRCH_TKT, 't' }, |
| 1070 | { SRCH_WIKI, 'w' }, |
| 1071 | { SRCH_TECHNOTE, 'e' }, |
| 1072 | { SRCH_FORUM, 'f' }, |
| 1073 | }; |
| 1074 | int i; |
| 1075 | for(i=0; i<count(aMask); i++){ |
| 1076 | if( srchFlags & aMask[i].m ){ |
| 1077 | blob_appendf(&sql, "%sftsdocs.type='%c'", zSep, aMask[i].c); |
| @@ -1157,11 +1238,11 @@ | |
| 1157 | int nLimit = db_get_int("search-limit", 100); |
| 1158 | |
| 1159 | if( P("searchlimit")!=0 ){ |
| 1160 | nLimit = atoi(P("searchlimit")); |
| 1161 | } |
| 1162 | srchFlags = search_restrict(srchFlags); |
| 1163 | if( srchFlags==0 ) return 0; |
| 1164 | search_sql_setup(g.db); |
| 1165 | add_content_sql_commands(g.db); |
| 1166 | db_multi_exec( |
| 1167 | "CREATE TEMP TABLE x(label,url,score,id,date,snip);" |
| @@ -1169,10 +1250,13 @@ | |
| 1169 | if( !search_index_exists() ){ |
| 1170 | search_fullscan(zPattern, srchFlags); /* Full-scan search */ |
| 1171 | }else{ |
| 1172 | search_update_index(srchFlags); /* Update the index, if necessary */ |
| 1173 | search_indexed(zPattern, srchFlags); /* Indexed search */ |
| 1174 | } |
| 1175 | db_prepare(&q, "SELECT url, snip, label, score, id, substr(date,1,10)" |
| 1176 | " FROM x" |
| 1177 | " ORDER BY score DESC, date DESC;"); |
| 1178 | while( db_step(&q)==SQLITE_ROW ){ |
| @@ -1223,28 +1307,35 @@ | |
| 1223 | ** |
| 1224 | ** 0x02 Show nothing if search is disabled. |
| 1225 | ** |
| 1226 | ** Return true if there are search results. |
| 1227 | */ |
| 1228 | int search_screen(unsigned srchFlags, int mFlags){ |
| 1229 | const char *zType = 0; |
| 1230 | const char *zClass = 0; |
| 1231 | const char *zDisable1; |
| 1232 | const char *zDisable2; |
| 1233 | const char *zPattern; |
| 1234 | int fDebug = PB("debug"); |
| 1235 | int haveResult = 0; |
| 1236 | srchFlags = search_restrict(srchFlags); |
| 1237 | switch( srchFlags ){ |
| 1238 | case SRCH_CKIN: zType = " Check-ins"; zClass = "Ckin"; break; |
| 1239 | case SRCH_DOC: zType = " Docs"; zClass = "Doc"; break; |
| 1240 | case SRCH_TKT: zType = " Tickets"; zClass = "Tkt"; break; |
| 1241 | case SRCH_WIKI: zType = " Wiki"; zClass = "Wiki"; break; |
| 1242 | case SRCH_TECHNOTE: zType = " Tech Notes"; zClass = "Note"; break; |
| 1243 | case SRCH_FORUM: zType = " Forum"; zClass = "Frm"; break; |
| 1244 | } |
| 1245 | if( srchFlags==0 ){ |
| 1246 | if( mFlags & 0x02 ) return 0; |
| 1247 | zDisable1 = " disabled"; |
| 1248 | zDisable2 = " disabled"; |
| 1249 | zPattern = ""; |
| 1250 | }else{ |
| @@ -1257,41 +1348,46 @@ | |
| 1257 | @ <div class='searchForm searchForm%s(zClass)'> |
| 1258 | }else{ |
| 1259 | @ <div class='searchForm'> |
| 1260 | } |
| 1261 | @ <input type="text" name="s" size="40" value="%h(zPattern)"%s(zDisable1)> |
| 1262 | if( (mFlags & 0x01)!=0 && (srchFlags & (srchFlags-1))!=0 ){ |
| 1263 | static const struct { const char *z; const char *zNm; unsigned m; } aY[] = { |
| 1264 | { "all", "All", SRCH_ALL }, |
| 1265 | { "c", "Check-ins", SRCH_CKIN }, |
| 1266 | { "d", "Docs", SRCH_DOC }, |
| 1267 | { "t", "Tickets", SRCH_TKT }, |
| 1268 | { "w", "Wiki", SRCH_WIKI }, |
| 1269 | { "e", "Tech Notes", SRCH_TECHNOTE }, |
| 1270 | { "f", "Forum", SRCH_FORUM }, |
| 1271 | }; |
| 1272 | const char *zY = PD("y","all"); |
| 1273 | unsigned newFlags = srchFlags; |
| 1274 | int i; |
| 1275 | @ <select size='1' name='y'> |
| 1276 | for(i=0; i<count(aY); i++){ |
| 1277 | if( (aY[i].m & srchFlags)==0 ) continue; |
| 1278 | cgi_printf("<option value='%s'", aY[i].z); |
| 1279 | if( fossil_strcmp(zY,aY[i].z)==0 ){ |
| 1280 | newFlags &= aY[i].m; |
| 1281 | cgi_printf(" selected"); |
| 1282 | } |
| 1283 | cgi_printf(">%s</option>\n", aY[i].zNm); |
| 1284 | } |
| 1285 | @ </select> |
| 1286 | srchFlags = newFlags; |
| 1287 | } |
| 1288 | if( fDebug ){ |
| 1289 | @ <input type="hidden" name="debug" value="1"> |
| 1290 | } |
| 1291 | @ <input type="submit" value="Search%s(zType)"%s(zDisable2)> |
| 1292 | if( srchFlags==0 ){ |
| 1293 | @ <p class="generalError">Search is disabled</p> |
| 1294 | } |
| 1295 | @ </div></form> |
| 1296 | while( fossil_isspace(zPattern[0]) ) zPattern++; |
| 1297 | if( zPattern[0] ){ |
| @@ -1298,11 +1394,11 @@ | |
| 1298 | if( zClass ){ |
| 1299 | @ <div class='searchResult searchResult%s(zClass)'> |
| 1300 | }else{ |
| 1301 | @ <div class='searchResult'> |
| 1302 | } |
| 1303 | if( search_run_and_output(zPattern, srchFlags, fDebug)==0 ){ |
| 1304 | @ <p class='searchEmpty'>No matches for: <span>%h(zPattern)</span></p> |
| 1305 | } |
| 1306 | @ </div> |
| 1307 | haveResult = 1; |
| 1308 | } |
| @@ -1315,17 +1411,18 @@ | |
| 1315 | ** Search for check-in comments, documents, tickets, or wiki that |
| 1316 | ** match a user-supplied pattern. |
| 1317 | ** |
| 1318 | ** s=PATTERN Specify the full-text pattern to search for |
| 1319 | ** y=TYPE What to search. |
| 1320 | ** c -> check-ins |
| 1321 | ** d -> documentation |
| 1322 | ** t -> tickets |
| 1323 | ** w -> wiki |
| 1324 | ** e -> tech notes |
| 1325 | ** f -> forum |
| 1326 | ** all -> everything |
| 1327 | */ |
| 1328 | void search_page(void){ |
| 1329 | const int isSearch = P("s")!=0; |
| 1330 | login_check_credentials(); |
| 1331 | style_header("Search%s", isSearch ? " Results" : ""); |
| @@ -1374,20 +1471,20 @@ | |
| 1374 | }else{ |
| 1375 | blob_append(pOut, "\n", 1); |
| 1376 | wiki_convert(pIn, &html, 0); |
| 1377 | } |
| 1378 | } |
| 1379 | html_to_plaintext(blob_str(&html), pOut); |
| 1380 | }else if( fossil_strcmp(zMimetype,"text/x-markdown")==0 ){ |
| 1381 | markdown_to_html(pIn, blob_size(&title) ? NULL : &title, &html); |
| 1382 | }else if( fossil_strcmp(zMimetype,"text/html")==0 ){ |
| 1383 | if( blob_size(&title)==0 ) doc_is_embedded_html(pIn, &title); |
| 1384 | pHtml = pIn; |
| 1385 | } |
| 1386 | blob_appendf(pOut, "%s\n", blob_str(&title)); |
| 1387 | if( blob_size(pHtml) ){ |
| 1388 | html_to_plaintext(blob_str(pHtml), pOut); |
| 1389 | }else{ |
| 1390 | blob_append(pOut, blob_buffer(pIn), blob_size(pIn)); |
| 1391 | } |
| 1392 | blob_reset(&html); |
| 1393 | blob_reset(&title); |
| @@ -2108,32 +2205,32 @@ | |
| 2108 | } |
| 2109 | fossil_print(" done\n"); |
| 2110 | } |
| 2111 | |
| 2112 | /* |
| 2113 | ** COMMAND: fts-config* |
| 2114 | ** |
| 2115 | ** Usage: fossil fts-config ?SUBCOMMAND? ?ARGUMENT? |
| 2116 | ** |
| 2117 | ** The "fossil fts-config" command configures the full-text search capabilities |
| 2118 | ** of the repository. Subcommands: |
| 2119 | ** |
| 2120 | ** reindex Rebuild the search index. This is a no-op if |
| 2121 | ** index search is disabled |
| 2122 | ** |
| 2123 | ** index (on|off) Turn the search index on or off |
| 2124 | ** |
| 2125 | ** enable cdtwef Enable various kinds of search. c=Check-ins, |
| 2126 | ** d=Documents, t=Tickets, w=Wiki, e=Tech Notes, |
| 2127 | ** f=Forum. |
| 2128 | ** |
| 2129 | ** disable cdtwef Disable various kinds of search |
| 2130 | ** |
| 2131 | ** tokenizer VALUE Select a tokenizer for indexed search. VALUE |
| 2132 | ** may be one of (porter, on, off, trigram, unicode61), |
| 2133 | ** and "on" is equivalent to "porter". Unindexed |
| 2134 | ** search never uses tokenization or stemming. |
| 2135 | ** |
| 2136 | ** The current search settings are displayed after any changes are applied. |
| 2137 | ** Run this command with no arguments to simply see the settings. |
| 2138 | */ |
| 2139 | void fts_config_cmd(void){ |
| @@ -2150,16 +2247,17 @@ | |
| 2150 | static const struct { |
| 2151 | const char *zSetting; |
| 2152 | const char *zName; |
| 2153 | const char *zSw; |
| 2154 | } aSetng[] = { |
| 2155 | { "search-ci", "check-in search:", "c" }, |
| 2156 | { "search-doc", "document search:", "d" }, |
| 2157 | { "search-tkt", "ticket search:", "t" }, |
| 2158 | { "search-wiki", "wiki search:", "w" }, |
| 2159 | { "search-technote", "tech note search:", "e" }, |
| 2160 | { "search-forum", "forum search:", "f" }, |
| 2161 | }; |
| 2162 | char *zSubCmd = 0; |
| 2163 | int i, j, n; |
| 2164 | int iCmd = 0; |
| 2165 | int iAction = 0; |
| @@ -2192,16 +2290,46 @@ | |
| 2192 | } |
| 2193 | db_begin_transaction(); |
| 2194 | |
| 2195 | /* Adjust search settings */ |
| 2196 | if( iCmd==3 || iCmd==4 ){ |
| 2197 | const char *zCtrl; |
| 2198 | if( g.argc<4 ) usage(mprintf("%s STRING",zSubCmd)); |
| 2199 | zCtrl = g.argv[3]; |
| 2200 | for(j=0; j<count(aSetng); j++){ |
| 2201 | if( strchr(zCtrl, aSetng[j].zSw[0])!=0 ){ |
| 2202 | db_set_int(aSetng[j].zSetting/*works-like:"x"*/, iCmd-3, 0); |
| 2203 | } |
| 2204 | } |
| 2205 | }else if( iCmd==5 ){ |
| 2206 | int iOldTokenizer, iNewTokenizer; |
| 2207 | if( g.argc<4 ) usage("tokenizer porter|on|off|trigram|unicode61"); |
| @@ -2224,30 +2352,30 @@ | |
| 2224 | search_rebuild_index(); |
| 2225 | } |
| 2226 | |
| 2227 | /* Always show the status before ending */ |
| 2228 | for(i=0; i<count(aSetng); i++){ |
| 2229 | fossil_print("%-17s %s\n", aSetng[i].zName, |
| 2230 | db_get_boolean(aSetng[i].zSetting,0) ? "on" : "off"); |
| 2231 | } |
| 2232 | fossil_print("%-17s %s\n", "tokenizer:", |
| 2233 | search_tokenizer_for_string(0)); |
| 2234 | if( search_index_exists() ){ |
| 2235 | int pgsz = db_int64(0, "PRAGMA repository.page_size;"); |
| 2236 | i64 nTotal = db_int64(0, "PRAGMA repository.page_count;")*pgsz; |
| 2237 | i64 nFts = db_int64(0, "SELECT count(*) FROM dbstat" |
| 2238 | " WHERE schema='repository'" |
| 2239 | " AND name LIKE 'fts%%'")*pgsz; |
| 2240 | char zSize[50]; |
| 2241 | fossil_print("%-17s FTS%d\n", "full-text index:", search_index_type(1)); |
| 2242 | fossil_print("%-17s %d\n", "documents:", |
| 2243 | db_int(0, "SELECT count(*) FROM ftsdocs")); |
| 2244 | approxSizeName(sizeof(zSize), zSize, nFts); |
| 2245 | fossil_print("%-17s %s (%.1f%% of repository)\n", "space used", |
| 2246 | zSize, 100.0*((double)nFts/(double)nTotal)); |
| 2247 | }else{ |
| 2248 | fossil_print("%-17s disabled\n", "full-text index:"); |
| 2249 | } |
| 2250 | db_end_transaction(0); |
| 2251 | } |
| 2252 | |
| 2253 | /* |
| 2254 |
| --- src/search.c | |
| +++ src/search.c | |
| @@ -564,90 +564,142 @@ | |
| 564 | /* |
| 565 | ** Testing the search function. |
| 566 | ** |
| 567 | ** COMMAND: search* |
| 568 | ** |
| 569 | ** Usage: %fossil search [OPTIONS] PATTERN... |
| 570 | ** |
| 571 | ** Search the repository for PATTERN and show matches. Depending on |
| 572 | ** options and how the administrator has search configured for the |
| 573 | ** repository, the search can cover: |
| 574 | ** |
| 575 | ** * check-in comments (-c) |
| 576 | ** * embedded documentation (--docs) |
| 577 | ** * forum posts (--forum) |
| 578 | ** * tickets (--tickets) |
| 579 | ** * tech notes (--technotes) |
| 580 | ** * wiki pages (--wiki) |
| 581 | ** * built-in fossil help text (-h) |
| 582 | ** * all of the above (-a) |
| 583 | ** |
| 584 | ** Use options below to select the scope of the search. The |
| 585 | ** default is check-in comments only (-c). |
| 586 | ** |
| 587 | ** Output is colorized if writing to a TTY and if the NO_COLOR environment |
| 588 | ** variable is not set. Use the "--highlight 0" option to disable colorization |
| 589 | ** or use "--highlight 91" to force it on. Change the argument to --highlight |
| 590 | ** to change the color. |
| 591 | ** |
| 592 | ** Options: |
| 593 | ** -a|--all Search everything |
| 594 | ** -c|--checkins Search checkin comments |
| 595 | ** --docs Search embedded documentation |
| 596 | ** --forum Search forum posts |
| 597 | ** -h|--bi-help Search built-in help |
| 598 | ** --highlight N Used VT100 color N for matching text. 0 means "off". |
| 599 | ** -n|--limit N Limit output to N matches |
| 600 | ** --technotes Search tech notes |
| 601 | ** --tickets Search tickets |
| 602 | ** -W|--width WIDTH Set display width to WIDTH columns, 0 for |
| 603 | ** unlimited. Defaults to the terminal's width. |
| 604 | ** --wiki Search wiki |
| 605 | */ |
| 606 | void search_cmd(void){ |
| 607 | Blob pattern; |
| 608 | int i; |
| 609 | Blob sql = empty_blob; |
| 610 | Stmt q; |
| 611 | int iBest; |
| 612 | int srchFlags = 0; |
| 613 | int bFts = 1; /* Use FTS search by default now */ |
| 614 | char fAll = NULL != find_option("all", "a", 0); |
| 615 | const char *zLimit = find_option("limit","n",1); |
| 616 | const char *zScope = 0; |
| 617 | const char *zWidth = find_option("width","W",1); |
| 618 | int bDebug = find_option("debug",0,0)!=0; /* Undocumented */ |
| 619 | int nLimit = zLimit ? atoi(zLimit) : -1000; |
| 620 | int width; |
| 621 | int nTty = 0; /* VT100 highlight color for matching text */ |
| 622 | const char *zHighlight = 0; |
| 623 | |
| 624 | nTty = terminal_is_vt100(); |
| 625 | |
| 626 | /* Undocumented option to change highlight color */ |
| 627 | zHighlight = find_option("highlight",0,1); |
| 628 | if( zHighlight ) nTty = atoi(zHighlight); |
| 629 | |
| 630 | /* Undocumented option (legacy) */ |
| 631 | zScope = find_option("scope",0,1); |
| 632 | |
| 633 | if( find_option("fts",0,0)!=0 ) bFts = 1; /* Undocumented legacy */ |
| 634 | if( find_option("legacy",0,0)!=0 ) bFts = 0; /* Undocumented */ |
| 635 | |
| 636 | if( zWidth ){ |
| 637 | width = atoi(zWidth); |
| 638 | if( (width!=0) && (width<=20) ){ |
| 639 | fossil_fatal("-W|--width value must be >20 or 0"); |
| 640 | } |
| 641 | }else{ |
| 642 | width = -1; |
| 643 | } |
| 644 | if( zScope ){ |
| 645 | for(i=0; zScope[i]; i++){ |
| 646 | switch( zScope[i] ){ |
| 647 | case 'a': srchFlags = SRCH_ALL; break; |
| 648 | case 'c': srchFlags |= SRCH_CKIN; break; |
| 649 | case 'd': srchFlags |= SRCH_DOC; break; |
| 650 | case 'e': srchFlags |= SRCH_TECHNOTE; break; |
| 651 | case 'f': srchFlags |= SRCH_FORUM; break; |
| 652 | case 'h': srchFlags |= SRCH_HELP; break; |
| 653 | case 't': srchFlags |= SRCH_TKT; break; |
| 654 | case 'w': srchFlags |= SRCH_WIKI; break; |
| 655 | } |
| 656 | } |
| 657 | bFts = 1; |
| 658 | } |
| 659 | if( find_option("all","a",0) ){ srchFlags |= SRCH_ALL; bFts = 1; } |
| 660 | if( find_option("bi-help","h",0) ){ srchFlags |= SRCH_HELP; bFts = 1; } |
| 661 | if( find_option("checkins","c",0) ){ srchFlags |= SRCH_CKIN; bFts = 1; } |
| 662 | if( find_option("docs",0,0) ){ srchFlags |= SRCH_DOC; bFts = 1; } |
| 663 | if( find_option("forum",0,0) ){ srchFlags |= SRCH_FORUM; bFts = 1; } |
| 664 | if( find_option("technotes",0,0) ){ srchFlags |= SRCH_TECHNOTE; bFts = 1; } |
| 665 | if( find_option("tickets",0,0) ){ srchFlags |= SRCH_TKT; bFts = 1; } |
| 666 | if( find_option("wiki",0,0) ){ srchFlags |= SRCH_WIKI; bFts = 1; } |
| 667 | |
| 668 | /* If no search objects are specified, default to "check-in comments" */ |
| 669 | if( srchFlags==0 ) srchFlags = SRCH_CKIN; |
| 670 | |
| 671 | |
| 672 | db_find_and_open_repository(0, 0); |
| 673 | verify_all_options(); |
| 674 | if( g.argc<3 ) return; |
| 675 | login_set_capabilities("s", 0); |
| 676 | if( search_restrict(srchFlags)==0 && (srchFlags & SRCH_HELP)==0 ){ |
| 677 | const char *zC1 = 0, *zPlural = "s"; |
| 678 | if( srchFlags & SRCH_TECHNOTE ){ zC1 = "technote"; } |
| 679 | if( srchFlags & SRCH_TKT ){ zC1 = "ticket"; } |
| 680 | if( srchFlags & SRCH_FORUM ){ zC1 = "forum"; zPlural = ""; } |
| 681 | if( srchFlags & SRCH_DOC ){ zC1 = "document"; } |
| 682 | if( srchFlags & SRCH_WIKI ){ zC1 = "wiki"; zPlural = ""; } |
| 683 | if( srchFlags & SRCH_CKIN ){ zC1 = "check-in"; } |
| 684 | fossil_print( |
| 685 | "Search of %s%s is disabled on this repository.\n" |
| 686 | "Enable using \"fossil fts-config enable %s\".\n", |
| 687 | zC1, zPlural, zC1 |
| 688 | ); |
| 689 | return; |
| 690 | } |
| 691 | |
| 692 | blob_init(&pattern, g.argv[2], -1); |
| 693 | for(i=3; i<g.argc; i++){ |
| 694 | blob_appendf(&pattern, " %s", g.argv[i]); |
| 695 | } |
| 696 | if( bFts ){ |
| 697 | /* Search using FTS */ |
| 698 | Blob com; |
| 699 | Blob snip; |
| 700 | const char *zPattern = blob_str(&pattern); |
| 701 | search_sql_setup(g.db); |
| 702 | add_content_sql_commands(g.db); |
| 703 | db_multi_exec( |
| 704 | "CREATE TEMP TABLE x(label,url,score,id,date,snip);" |
| 705 | ); |
| @@ -654,30 +706,32 @@ | |
| 706 | if( !search_index_exists() ){ |
| 707 | search_fullscan(zPattern, srchFlags); /* Full-scan search */ |
| 708 | }else{ |
| 709 | search_update_index(srchFlags); /* Update the index */ |
| 710 | search_indexed(zPattern, srchFlags); /* Indexed search */ |
| 711 | if( srchFlags & SRCH_HELP ){ |
| 712 | search_fullscan(zPattern, SRCH_HELP); |
| 713 | } |
| 714 | } |
| 715 | db_prepare(&q, "SELECT snip, label, score, id, date" |
| 716 | " FROM x" |
| 717 | " ORDER BY score DESC, date DESC;"); |
| 718 | blob_init(&com, 0, 0); |
| 719 | blob_init(&snip, 0, 0); |
| 720 | if( width<0 ) width = terminal_get_width(80); |
| 721 | while( db_step(&q)==SQLITE_ROW ){ |
| 722 | const char *zSnippet = db_column_text(&q, 0); |
| 723 | const char *zLabel = db_column_text(&q, 1); |
| 724 | const char *zDate = db_column_text(&q, 4); |
| 725 | const char *zScore = db_column_text(&q, 2); |
| 726 | const char *zId = db_column_text(&q, 3); |
| 727 | char *zOrig; |
| 728 | blob_appendf(&snip, "%s", zSnippet); |
| 729 | zOrig = blob_materialize(&snip); |
| 730 | blob_init(&snip, 0, 0); |
| 731 | html_to_plaintext(zOrig, &snip, (nTty?HTOT_VT100:0)|HTOT_FLOW|HTOT_TRIM); |
| 732 | fossil_free(zOrig); |
| 733 | blob_appendf(&com, "%s\n%s\n%s", zLabel, blob_str(&snip), zDate); |
| 734 | if( bDebug ){ |
| 735 | blob_appendf(&com," score: %s id: %s", zScore, zId); |
| 736 | } |
| 737 | comment_print(blob_str(&com), 0, 5, width, |
| @@ -731,28 +785,33 @@ | |
| 785 | #define SRCH_DOC 0x0002 /* Search over embedded documents */ |
| 786 | #define SRCH_TKT 0x0004 /* Search over tickets */ |
| 787 | #define SRCH_WIKI 0x0008 /* Search over wiki */ |
| 788 | #define SRCH_TECHNOTE 0x0010 /* Search over tech notes */ |
| 789 | #define SRCH_FORUM 0x0020 /* Search over forum messages */ |
| 790 | #define SRCH_HELP 0x0040 /* Search built-in help (full-scan only) */ |
| 791 | #define SRCH_ALL 0x007f /* Search over everything */ |
| 792 | #endif |
| 793 | |
| 794 | /* |
| 795 | ** Remove bits from srchFlags which are disallowed by either the |
| 796 | ** current server configuration or by user permissions. Return |
| 797 | ** the revised search flags mask. |
| 798 | ** |
| 799 | ** If bFlex is true, that means allow through the SRCH_HELP option |
| 800 | ** even if it is not explicitly enabled. |
| 801 | */ |
| 802 | unsigned int search_restrict(unsigned int srchFlags){ |
| 803 | static unsigned int knownGood = 0; |
| 804 | static unsigned int knownBad = 0; |
| 805 | static const struct { unsigned m; const char *zKey; } aSetng[] = { |
| 806 | { SRCH_CKIN, "search-ci" }, |
| 807 | { SRCH_DOC, "search-doc" }, |
| 808 | { SRCH_TKT, "search-tkt" }, |
| 809 | { SRCH_WIKI, "search-wiki" }, |
| 810 | { SRCH_TECHNOTE, "search-technote" }, |
| 811 | { SRCH_FORUM, "search-forum" }, |
| 812 | { SRCH_HELP, "search-help" }, |
| 813 | }; |
| 814 | int i; |
| 815 | if( g.perm.Read==0 ) srchFlags &= ~(SRCH_CKIN|SRCH_DOC|SRCH_TECHNOTE); |
| 816 | if( g.perm.RdTkt==0 ) srchFlags &= ~(SRCH_TKT); |
| 817 | if( g.perm.RdWiki==0 ) srchFlags &= ~(SRCH_WIKI); |
| @@ -912,10 +971,31 @@ | |
| 971 | " search_snippet()" |
| 972 | " FROM event JOIN blob on event.objid=blob.rid" |
| 973 | " WHERE search_match('',body('f',rid,NULL));" |
| 974 | ); |
| 975 | } |
| 976 | if( (srchFlags & SRCH_HELP)!=0 ){ |
| 977 | const char *zPrefix; |
| 978 | helptext_vtab_register(g.db); |
| 979 | if( srchFlags==SRCH_HELP ){ |
| 980 | zPrefix = "The"; |
| 981 | }else{ |
| 982 | zPrefix = "Built-in help for the"; |
| 983 | } |
| 984 | db_multi_exec( |
| 985 | "INSERT INTO x(label,url,score,id,snip)" |
| 986 | " SELECT format('%q \"%%s\" %%s',name,type)," |
| 987 | " '/help?cmd='||name," |
| 988 | " search_score()," |
| 989 | " 'h'||rowid," |
| 990 | " search_snippet()" |
| 991 | " FROM helptext" |
| 992 | " WHERE search_match(format('the \"%%s\" %%s',name,type)," |
| 993 | " helptext.helptext);", |
| 994 | zPrefix |
| 995 | ); |
| 996 | } |
| 997 | } |
| 998 | |
| 999 | /* |
| 1000 | ** Number of significant bits in a u32 |
| 1001 | */ |
| @@ -1068,10 +1148,11 @@ | |
| 1148 | { SRCH_DOC, 'd' }, |
| 1149 | { SRCH_TKT, 't' }, |
| 1150 | { SRCH_WIKI, 'w' }, |
| 1151 | { SRCH_TECHNOTE, 'e' }, |
| 1152 | { SRCH_FORUM, 'f' }, |
| 1153 | { SRCH_HELP, 'h' }, |
| 1154 | }; |
| 1155 | int i; |
| 1156 | for(i=0; i<count(aMask); i++){ |
| 1157 | if( srchFlags & aMask[i].m ){ |
| 1158 | blob_appendf(&sql, "%sftsdocs.type='%c'", zSep, aMask[i].c); |
| @@ -1157,11 +1238,11 @@ | |
| 1238 | int nLimit = db_get_int("search-limit", 100); |
| 1239 | |
| 1240 | if( P("searchlimit")!=0 ){ |
| 1241 | nLimit = atoi(P("searchlimit")); |
| 1242 | } |
| 1243 | srchFlags = search_restrict(srchFlags) | (srchFlags & SRCH_HELP); |
| 1244 | if( srchFlags==0 ) return 0; |
| 1245 | search_sql_setup(g.db); |
| 1246 | add_content_sql_commands(g.db); |
| 1247 | db_multi_exec( |
| 1248 | "CREATE TEMP TABLE x(label,url,score,id,date,snip);" |
| @@ -1169,10 +1250,13 @@ | |
| 1250 | if( !search_index_exists() ){ |
| 1251 | search_fullscan(zPattern, srchFlags); /* Full-scan search */ |
| 1252 | }else{ |
| 1253 | search_update_index(srchFlags); /* Update the index, if necessary */ |
| 1254 | search_indexed(zPattern, srchFlags); /* Indexed search */ |
| 1255 | if( srchFlags & SRCH_HELP ){ |
| 1256 | search_fullscan(zPattern, SRCH_HELP); |
| 1257 | } |
| 1258 | } |
| 1259 | db_prepare(&q, "SELECT url, snip, label, score, id, substr(date,1,10)" |
| 1260 | " FROM x" |
| 1261 | " ORDER BY score DESC, date DESC;"); |
| 1262 | while( db_step(&q)==SQLITE_ROW ){ |
| @@ -1223,28 +1307,35 @@ | |
| 1307 | ** |
| 1308 | ** 0x02 Show nothing if search is disabled. |
| 1309 | ** |
| 1310 | ** Return true if there are search results. |
| 1311 | */ |
| 1312 | int search_screen(unsigned srchAllowed, int mFlags){ |
| 1313 | const char *zType = 0; |
| 1314 | const char *zClass = 0; |
| 1315 | const char *zDisable1; |
| 1316 | const char *zDisable2; |
| 1317 | const char *zPattern; |
| 1318 | int fDebug = PB("debug"); |
| 1319 | int haveResult = 0; |
| 1320 | int srchThisTime; |
| 1321 | const char *zY = PD("y","all"); |
| 1322 | if( zY[0]=='h' && zY[1]==0 ){ |
| 1323 | srchAllowed = search_restrict(srchAllowed) | (srchAllowed & SRCH_HELP); |
| 1324 | }else{ |
| 1325 | srchAllowed = search_restrict(srchAllowed); |
| 1326 | } |
| 1327 | switch( srchAllowed ){ |
| 1328 | case SRCH_CKIN: zType = " Check-ins"; zClass = "Ckin"; break; |
| 1329 | case SRCH_DOC: zType = " Docs"; zClass = "Doc"; break; |
| 1330 | case SRCH_TKT: zType = " Tickets"; zClass = "Tkt"; break; |
| 1331 | case SRCH_WIKI: zType = " Wiki"; zClass = "Wiki"; break; |
| 1332 | case SRCH_TECHNOTE: zType = " Tech Notes"; zClass = "Note"; break; |
| 1333 | case SRCH_FORUM: zType = " Forum"; zClass = "Frm"; break; |
| 1334 | case SRCH_HELP: zType = " Help"; zClass = "Hlp"; break; |
| 1335 | } |
| 1336 | if( srchAllowed==0 ){ |
| 1337 | if( mFlags & 0x02 ) return 0; |
| 1338 | zDisable1 = " disabled"; |
| 1339 | zDisable2 = " disabled"; |
| 1340 | zPattern = ""; |
| 1341 | }else{ |
| @@ -1257,41 +1348,46 @@ | |
| 1348 | @ <div class='searchForm searchForm%s(zClass)'> |
| 1349 | }else{ |
| 1350 | @ <div class='searchForm'> |
| 1351 | } |
| 1352 | @ <input type="text" name="s" size="40" value="%h(zPattern)"%s(zDisable1)> |
| 1353 | srchThisTime = srchAllowed; |
| 1354 | if( (mFlags & 0x01)!=0 && (srchAllowed & (srchAllowed-1))!=0 ){ |
| 1355 | static const struct { |
| 1356 | const char *z; |
| 1357 | const char *zNm; |
| 1358 | unsigned m; |
| 1359 | } aY[] = { |
| 1360 | { "all", "All", SRCH_ALL }, |
| 1361 | { "c", "Check-ins", SRCH_CKIN }, |
| 1362 | { "d", "Docs", SRCH_DOC }, |
| 1363 | { "t", "Tickets", SRCH_TKT }, |
| 1364 | { "w", "Wiki", SRCH_WIKI }, |
| 1365 | { "e", "Tech Notes", SRCH_TECHNOTE }, |
| 1366 | { "f", "Forum", SRCH_FORUM }, |
| 1367 | { "h", "Help", SRCH_HELP }, |
| 1368 | }; |
| 1369 | int i; |
| 1370 | @ <select size='1' name='y'> |
| 1371 | for(i=0; i<count(aY); i++){ |
| 1372 | if( (aY[i].m & srchAllowed)==0 ) continue; |
| 1373 | if( aY[i].m==SRCH_HELP && fossil_strcmp(zY,"h")!=0 |
| 1374 | && search_restrict(SRCH_HELP)==0 ) continue; |
| 1375 | cgi_printf("<option value='%s'", aY[i].z); |
| 1376 | if( fossil_strcmp(zY,aY[i].z)==0 ){ |
| 1377 | srchThisTime &= aY[i].m; |
| 1378 | cgi_printf(" selected"); |
| 1379 | } |
| 1380 | cgi_printf(">%s</option>\n", aY[i].zNm); |
| 1381 | } |
| 1382 | @ </select> |
| 1383 | } |
| 1384 | if( fDebug ){ |
| 1385 | @ <input type="hidden" name="debug" value="1"> |
| 1386 | } |
| 1387 | @ <input type="submit" value="Search%s(zType)"%s(zDisable2)> |
| 1388 | if( srchAllowed==0 && srchThisTime==0 ){ |
| 1389 | @ <p class="generalError">Search is disabled</p> |
| 1390 | } |
| 1391 | @ </div></form> |
| 1392 | while( fossil_isspace(zPattern[0]) ) zPattern++; |
| 1393 | if( zPattern[0] ){ |
| @@ -1298,11 +1394,11 @@ | |
| 1394 | if( zClass ){ |
| 1395 | @ <div class='searchResult searchResult%s(zClass)'> |
| 1396 | }else{ |
| 1397 | @ <div class='searchResult'> |
| 1398 | } |
| 1399 | if( search_run_and_output(zPattern, srchThisTime, fDebug)==0 ){ |
| 1400 | @ <p class='searchEmpty'>No matches for: <span>%h(zPattern)</span></p> |
| 1401 | } |
| 1402 | @ </div> |
| 1403 | haveResult = 1; |
| 1404 | } |
| @@ -1315,17 +1411,18 @@ | |
| 1411 | ** Search for check-in comments, documents, tickets, or wiki that |
| 1412 | ** match a user-supplied pattern. |
| 1413 | ** |
| 1414 | ** s=PATTERN Specify the full-text pattern to search for |
| 1415 | ** y=TYPE What to search. |
| 1416 | ** c -> check-ins, |
| 1417 | ** d -> documentation, |
| 1418 | ** t -> tickets, |
| 1419 | ** w -> wiki, |
| 1420 | ** e -> tech notes, |
| 1421 | ** f -> forum, |
| 1422 | ** h -> built-in help, |
| 1423 | ** all -> everything. |
| 1424 | */ |
| 1425 | void search_page(void){ |
| 1426 | const int isSearch = P("s")!=0; |
| 1427 | login_check_credentials(); |
| 1428 | style_header("Search%s", isSearch ? " Results" : ""); |
| @@ -1374,20 +1471,20 @@ | |
| 1471 | }else{ |
| 1472 | blob_append(pOut, "\n", 1); |
| 1473 | wiki_convert(pIn, &html, 0); |
| 1474 | } |
| 1475 | } |
| 1476 | html_to_plaintext(blob_str(&html), pOut, 0); |
| 1477 | }else if( fossil_strcmp(zMimetype,"text/x-markdown")==0 ){ |
| 1478 | markdown_to_html(pIn, blob_size(&title) ? NULL : &title, &html); |
| 1479 | }else if( fossil_strcmp(zMimetype,"text/html")==0 ){ |
| 1480 | if( blob_size(&title)==0 ) doc_is_embedded_html(pIn, &title); |
| 1481 | pHtml = pIn; |
| 1482 | } |
| 1483 | blob_appendf(pOut, "%s\n", blob_str(&title)); |
| 1484 | if( blob_size(pHtml) ){ |
| 1485 | html_to_plaintext(blob_str(pHtml), pOut, 0); |
| 1486 | }else{ |
| 1487 | blob_append(pOut, blob_buffer(pIn), blob_size(pIn)); |
| 1488 | } |
| 1489 | blob_reset(&html); |
| 1490 | blob_reset(&title); |
| @@ -2108,32 +2205,32 @@ | |
| 2205 | } |
| 2206 | fossil_print(" done\n"); |
| 2207 | } |
| 2208 | |
| 2209 | /* |
| 2210 | ** COMMAND: fts-config* abbrv-subcom |
| 2211 | ** |
| 2212 | ** Usage: fossil fts-config ?SUBCOMMAND? ?ARGUMENT? |
| 2213 | ** |
| 2214 | ** The "fossil fts-config" command configures the full-text search capabilities |
| 2215 | ** of the repository. Subcommands: |
| 2216 | ** |
| 2217 | ** reindex Rebuild the search index. This is a no-op if |
| 2218 | ** index search is disabled |
| 2219 | ** |
| 2220 | ** index (on|off) Turn the search index on or off |
| 2221 | ** |
| 2222 | ** enable TYPE .. Enable search for TYPE. TYPE is one of: |
| 2223 | ** check-in, document, ticket, wiki, technote, |
| 2224 | ** forum, help, or all |
| 2225 | ** |
| 2226 | ** disable TYPE ... Disable search for TYPE |
| 2227 | ** |
| 2228 | ** tokenizer VALUE Select a tokenizer for indexed search. VALUE |
| 2229 | ** may be one of (porter, on, off, trigram, unicode61), |
| 2230 | ** and "on" is equivalent to "porter". Unindexed |
| 2231 | ** search never uses tokenization or stemming. |
| 2232 | ** |
| 2233 | ** The current search settings are displayed after any changes are applied. |
| 2234 | ** Run this command with no arguments to simply see the settings. |
| 2235 | */ |
| 2236 | void fts_config_cmd(void){ |
| @@ -2150,16 +2247,17 @@ | |
| 2247 | static const struct { |
| 2248 | const char *zSetting; |
| 2249 | const char *zName; |
| 2250 | const char *zSw; |
| 2251 | } aSetng[] = { |
| 2252 | { "search-ci", "check-in search:", "c" }, |
| 2253 | { "search-doc", "document search:", "d" }, |
| 2254 | { "search-tkt", "ticket search:", "t" }, |
| 2255 | { "search-wiki", "wiki search:", "w" }, |
| 2256 | { "search-technote", "technote search:", "e" }, |
| 2257 | { "search-forum", "forum search:", "f" }, |
| 2258 | { "search-help", "built-in help search:", "h" }, |
| 2259 | }; |
| 2260 | char *zSubCmd = 0; |
| 2261 | int i, j, n; |
| 2262 | int iCmd = 0; |
| 2263 | int iAction = 0; |
| @@ -2192,16 +2290,46 @@ | |
| 2290 | } |
| 2291 | db_begin_transaction(); |
| 2292 | |
| 2293 | /* Adjust search settings */ |
| 2294 | if( iCmd==3 || iCmd==4 ){ |
| 2295 | int k; |
| 2296 | const char *zCtrl; |
| 2297 | for(k=2; k<g.argc; k++){ |
| 2298 | if( k==2 ){ |
| 2299 | if( g.argc<4 ){ |
| 2300 | zCtrl = "all"; |
| 2301 | }else{ |
| 2302 | zCtrl = g.argv[3]; |
| 2303 | k++; |
| 2304 | } |
| 2305 | }else{ |
| 2306 | zCtrl = g.argv[k]; |
| 2307 | } |
| 2308 | if( fossil_strcmp(zCtrl,"all")==0 ){ |
| 2309 | zCtrl = "cdtwefh"; |
| 2310 | } |
| 2311 | if( strlen(zCtrl)>=4 ){ |
| 2312 | /* If the argument to "enable" or "disable" is a string of at least |
| 2313 | ** 4 characters which matches part of any aSetng.zName, then use that |
| 2314 | ** one aSetng value only. */ |
| 2315 | char *zGlob = mprintf("*%s*", zCtrl); |
| 2316 | for(j=0; j<count(aSetng); j++){ |
| 2317 | if( sqlite3_strglob(zGlob, aSetng[j].zName)==0 ){ |
| 2318 | db_set_int(aSetng[j].zSetting/*works-like:"x"*/, iCmd-3, 0); |
| 2319 | zCtrl = 0; |
| 2320 | break; |
| 2321 | } |
| 2322 | } |
| 2323 | fossil_free(zGlob); |
| 2324 | } |
| 2325 | if( zCtrl ){ |
| 2326 | for(j=0; j<count(aSetng); j++){ |
| 2327 | if( strchr(zCtrl, aSetng[j].zSw[0])!=0 ){ |
| 2328 | db_set_int(aSetng[j].zSetting/*works-like:"x"*/, iCmd-3, 0); |
| 2329 | } |
| 2330 | } |
| 2331 | } |
| 2332 | } |
| 2333 | }else if( iCmd==5 ){ |
| 2334 | int iOldTokenizer, iNewTokenizer; |
| 2335 | if( g.argc<4 ) usage("tokenizer porter|on|off|trigram|unicode61"); |
| @@ -2224,30 +2352,30 @@ | |
| 2352 | search_rebuild_index(); |
| 2353 | } |
| 2354 | |
| 2355 | /* Always show the status before ending */ |
| 2356 | for(i=0; i<count(aSetng); i++){ |
| 2357 | fossil_print("%-21s %s\n", aSetng[i].zName, |
| 2358 | db_get_boolean(aSetng[i].zSetting,0) ? "on" : "off"); |
| 2359 | } |
| 2360 | fossil_print("%-21s %s\n", "tokenizer:", |
| 2361 | search_tokenizer_for_string(0)); |
| 2362 | if( search_index_exists() ){ |
| 2363 | int pgsz = db_int64(0, "PRAGMA repository.page_size;"); |
| 2364 | i64 nTotal = db_int64(0, "PRAGMA repository.page_count;")*pgsz; |
| 2365 | i64 nFts = db_int64(0, "SELECT count(*) FROM dbstat" |
| 2366 | " WHERE schema='repository'" |
| 2367 | " AND name LIKE 'fts%%'")*pgsz; |
| 2368 | char zSize[50]; |
| 2369 | fossil_print("%-21s FTS%d\n", "full-text index:", search_index_type(1)); |
| 2370 | fossil_print("%-21s %d\n", "documents:", |
| 2371 | db_int(0, "SELECT count(*) FROM ftsdocs")); |
| 2372 | approxSizeName(sizeof(zSize), zSize, nFts); |
| 2373 | fossil_print("%-21s %s (%.1f%% of repository)\n", "space used", |
| 2374 | zSize, 100.0*((double)nFts/(double)nTotal)); |
| 2375 | }else{ |
| 2376 | fossil_print("%-21s disabled\n", "full-text index:"); |
| 2377 | } |
| 2378 | db_end_transaction(0); |
| 2379 | } |
| 2380 | |
| 2381 | /* |
| 2382 |
+126
-150
| --- src/security_audit.c | ||
| +++ src/security_audit.c | ||
| @@ -553,17 +553,17 @@ | ||
| 553 | 553 | @ checkbox on the <a href="setup_access">Access Control</a> page. |
| 554 | 554 | } |
| 555 | 555 | |
| 556 | 556 | /* Logging should be turned on |
| 557 | 557 | */ |
| 558 | - if( db_get_boolean("access-log",0)==0 ){ | |
| 558 | + if( db_get_boolean("access-log",1)==0 ){ | |
| 559 | 559 | @ <li><p> |
| 560 | 560 | @ The <a href="access_log">User Log</a> is disabled. The user log |
| 561 | 561 | @ keeps a record of successful and unsuccessful login attempts and is |
| 562 | 562 | @ useful for security monitoring. |
| 563 | 563 | } |
| 564 | - if( db_get_boolean("admin-log",0)==0 ){ | |
| 564 | + if( db_get_boolean("admin-log",1)==0 ){ | |
| 565 | 565 | @ <li><p> |
| 566 | 566 | @ The <a href="admin_log">Administrative Log</a> is disabled. |
| 567 | 567 | @ The administrative log provides a record of configuration changes |
| 568 | 568 | @ and is useful for security monitoring. |
| 569 | 569 | } |
| @@ -804,37 +804,59 @@ | ||
| 804 | 804 | @ </pre></blockquote> |
| 805 | 805 | blob_reset(&fullname); |
| 806 | 806 | } |
| 807 | 807 | } |
| 808 | 808 | |
| 809 | -/* | |
| 810 | -** The maximum number of bytes of the error log to show by default. | |
| 811 | -*/ | |
| 812 | -#define MXSHOWLOG 500000 | |
| 813 | - | |
| 814 | 809 | /* |
| 815 | 810 | ** WEBPAGE: errorlog |
| 816 | 811 | ** |
| 817 | 812 | ** Show the content of the error log. Only the administrator can view |
| 818 | 813 | ** this page. |
| 814 | +** | |
| 815 | +** y=0x01 Show only hack attempts | |
| 816 | +** y=0x02 Show only panics and assertion faults | |
| 817 | +** y=0x04 Show hung backoffice processes | |
| 818 | +** y=0x08 Show POST requests from a different origin | |
| 819 | +** y=0x40 Show other uncategorized messages | |
| 820 | +** | |
| 821 | +** If y is omitted or is zero, a count of the various message types is | |
| 822 | +** shown. | |
| 819 | 823 | */ |
| 820 | 824 | void errorlog_page(void){ |
| 821 | 825 | i64 szFile; |
| 822 | 826 | FILE *in; |
| 823 | 827 | char *zLog; |
| 828 | + const char *zType = P("y"); | |
| 829 | + static const int eAllTypes = 0x4f; | |
| 830 | + long eType = 0; | |
| 831 | + int bOutput = 0; | |
| 832 | + int prevWasTime = 0; | |
| 833 | + int nHack = 0; | |
| 834 | + int nPanic = 0; | |
| 835 | + int nOther = 0; | |
| 836 | + int nHang = 0; | |
| 837 | + int nXPost = 0; | |
| 824 | 838 | char z[10000]; |
| 839 | + char zTime[10000]; | |
| 840 | + | |
| 825 | 841 | login_check_credentials(); |
| 826 | 842 | if( !g.perm.Admin ){ |
| 827 | 843 | login_needed(0); |
| 828 | 844 | return; |
| 829 | 845 | } |
| 846 | + if( zType ){ | |
| 847 | + eType = strtol(zType,0,0) & eAllTypes; | |
| 848 | + } | |
| 830 | 849 | style_header("Server Error Log"); |
| 831 | 850 | style_submenu_element("Test", "%R/test-warning"); |
| 832 | 851 | style_submenu_element("Refresh", "%R/errorlog"); |
| 852 | + style_submenu_element("Download", "%R/errorlog?download"); | |
| 853 | + style_submenu_element("Truncate", "%R/errorlog?truncate"); | |
| 833 | 854 | style_submenu_element("Log-Menu", "%R/setup-logmenu"); |
| 834 | - style_submenu_element("Panics", "%R/paniclog"); | |
| 835 | - style_submenu_element("Non-Hacks", "%R/hacklog?not"); | |
| 855 | + if( eType ){ | |
| 856 | + style_submenu_element("Summary", "%R/errorlog"); | |
| 857 | + } | |
| 836 | 858 | |
| 837 | 859 | if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){ |
| 838 | 860 | no_error_log_available(); |
| 839 | 861 | style_finish_page(); |
| 840 | 862 | return; |
| @@ -861,163 +883,117 @@ | ||
| 861 | 883 | return; |
| 862 | 884 | } |
| 863 | 885 | zLog = file_canonical_name_dup(g.zErrlog); |
| 864 | 886 | @ <p>The server error log at "%h(zLog)" is %,lld(szFile) bytes in size. |
| 865 | 887 | fossil_free(zLog); |
| 866 | - style_submenu_element("Download", "%R/errorlog?download"); | |
| 867 | - style_submenu_element("Truncate", "%R/errorlog?truncate"); | |
| 868 | 888 | in = fossil_fopen(g.zErrlog, "rb"); |
| 869 | 889 | if( in==0 ){ |
| 870 | 890 | @ <p class='generalError'>Unable to open that file for reading!</p> |
| 871 | 891 | style_finish_page(); |
| 872 | 892 | return; |
| 873 | 893 | } |
| 874 | - if( szFile>MXSHOWLOG && P("all")==0 ){ | |
| 875 | - @ <form action="%R/errorlog" method="POST"> | |
| 876 | - @ <p>Only the last %,d(MXSHOWLOG) bytes are shown. | |
| 877 | - @ <input type="submit" name="all" value="Show All"> | |
| 878 | - @ </form> | |
| 879 | - fseek(in, -MXSHOWLOG, SEEK_END); | |
| 880 | - } | |
| 881 | - @ <hr> | |
| 882 | - @ <pre> | |
| 883 | - while( fgets(z, sizeof(z), in) ){ | |
| 884 | - @ %h(z)\ | |
| 885 | - } | |
| 886 | - fclose(in); | |
| 887 | - @ </pre> | |
| 888 | - style_finish_page(); | |
| 889 | -} | |
| 890 | - | |
| 891 | -/* | |
| 892 | -** WEBPAGE: paniclog | |
| 893 | -** | |
| 894 | -** Scan the error log for panics. Show all panic messages, ignoring all | |
| 895 | -** other error log entries. | |
| 896 | -*/ | |
| 897 | -void paniclog_page(void){ | |
| 898 | - i64 szFile; | |
| 899 | - char *zLog; | |
| 900 | - FILE *in; | |
| 901 | - int bOutput = 0; | |
| 902 | - int prevWasTime = 0; | |
| 903 | - char z[10000]; | |
| 904 | - char zTime[10000]; | |
| 905 | - | |
| 906 | - login_check_credentials(); | |
| 907 | - if( !g.perm.Admin ){ | |
| 908 | - login_needed(0); | |
| 909 | - return; | |
| 910 | - } | |
| 911 | - style_header("Server Panic Log"); | |
| 912 | - style_submenu_element("Log-Menu", "%R/setup-logmenu"); | |
| 913 | - | |
| 914 | - if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){ | |
| 915 | - no_error_log_available(); | |
| 916 | - style_finish_page(); | |
| 917 | - return; | |
| 918 | - } | |
| 919 | - in = fossil_fopen(g.zErrlog, "rb"); | |
| 920 | - if( in==0 ){ | |
| 921 | - @ <p class='generalError'>Unable to open that file for reading!</p> | |
| 922 | - style_finish_page(); | |
| 923 | - return; | |
| 924 | - } | |
| 925 | - szFile = file_size(g.zErrlog, ExtFILE); | |
| 926 | - zLog = file_canonical_name_dup(g.zErrlog); | |
| 927 | - @ Panic messages contained within the %lld(szFile)-byte | |
| 928 | - @ <a href="%R/errorlog?all">error log</a> found at | |
| 929 | - @ "%h(zLog)". | |
| 930 | - fossil_free(zLog); | |
| 931 | - @ <hr> | |
| 932 | - @ <pre> | |
| 933 | - while( fgets(z, sizeof(z), in) ){ | |
| 934 | - if( prevWasTime | |
| 935 | - && (strncmp(z,"panic: ", 7)==0 || strstr(z," assertion fault ")!=0) | |
| 936 | - ){ | |
| 937 | - @ %h(zTime)\ | |
| 938 | - bOutput = 1; | |
| 894 | + if( eType==0 ){ | |
| 895 | + /* will do a summary */ | |
| 896 | + }else if( (eType&eAllTypes)!=eAllTypes ){ | |
| 897 | + @ Only the following types of messages displayed: | |
| 898 | + @ <ul> | |
| 899 | + if( eType & 0x01 ){ | |
| 900 | + @ <li>Hack attempts | |
| 901 | + } | |
| 902 | + if( eType & 0x02 ){ | |
| 903 | + @ <li>Panics and assertion faults | |
| 904 | + } | |
| 905 | + if( eType & 0x04 ){ | |
| 906 | + @ <li>Hung backoffice processes | |
| 907 | + } | |
| 908 | + if( eType & 0x08 ){ | |
| 909 | + @ <li>POST requests from different origin | |
| 910 | + } | |
| 911 | + if( eType & 0x40 ){ | |
| 912 | + @ <li>Other uncategorized messages | |
| 913 | + } | |
| 914 | + @ </ul> | |
| 915 | + } | |
| 916 | + @ <hr> | |
| 917 | + if( eType ){ | |
| 918 | + @ <pre> | |
| 919 | + } | |
| 920 | + while( fgets(z, sizeof(z), in) ){ | |
| 921 | + if( prevWasTime ){ | |
| 922 | + if( strncmp(z,"possible hack attempt - 418 ", 27)==0 ){ | |
| 923 | + bOutput = (eType & 0x01)!=0; | |
| 924 | + nHack++; | |
| 925 | + }else | |
| 926 | + if( (strncmp(z,"panic: ", 7)==0 || strstr(z," assertion fault ")!=0) ){ | |
| 927 | + bOutput = (eType & 0x02)!=0; | |
| 928 | + nPanic++; | |
| 929 | + }else | |
| 930 | + if( sqlite3_strglob("warning: backoffice process * still *",z)==0 ){ | |
| 931 | + bOutput = (eType & 0x04)!=0; | |
| 932 | + nHang++; | |
| 933 | + }else | |
| 934 | + if( sqlite3_strglob("warning: POST from different origin*",z)==0 ){ | |
| 935 | + bOutput = (eType & 0x08)!=0; | |
| 936 | + nXPost++; | |
| 937 | + }else | |
| 938 | + { | |
| 939 | + bOutput = (eType & 0x40)!=0; | |
| 940 | + nOther++; | |
| 941 | + } | |
| 942 | + if( bOutput ){ | |
| 943 | + @ %h(zTime)\ | |
| 944 | + } | |
| 939 | 945 | } |
| 940 | 946 | if( strncmp(z, "--------", 8)==0 ){ |
| 941 | 947 | size_t n = strlen(z); |
| 942 | 948 | memcpy(zTime, z, n+1); |
| 943 | 949 | prevWasTime = 1; |
| 944 | 950 | bOutput = 0; |
| 945 | 951 | }else{ |
| 946 | 952 | prevWasTime = 0; |
| 947 | 953 | } |
| 948 | - if( bOutput ){ | |
| 949 | - @ %h(z)\ | |
| 950 | - } | |
| 951 | - } | |
| 952 | - fclose(in); | |
| 953 | - @ </pre> | |
| 954 | - style_finish_page(); | |
| 955 | -} | |
| 956 | - | |
| 957 | -/* | |
| 958 | -** WEBPAGE: hacklog | |
| 959 | -** | |
| 960 | -** Scan the error log for "possible hack attempt" entries Show hack | |
| 961 | -** attempt messages only, omitting all others. Or if the "not" query | |
| 962 | -** parameter is present, show only messages that are not hack attempts. | |
| 963 | -*/ | |
| 964 | -void hacklog_page(void){ | |
| 965 | - i64 szFile; | |
| 966 | - char *zLog; | |
| 967 | - FILE *in; | |
| 968 | - int bOutput = 0; | |
| 969 | - int prevWasTime = 0; | |
| 970 | - int isNot = P("not")!=0; | |
| 971 | - char z[10000]; | |
| 972 | - char zTime[10000]; | |
| 973 | - | |
| 974 | - login_check_credentials(); | |
| 975 | - if( !g.perm.Admin ){ | |
| 976 | - login_needed(0); | |
| 977 | - return; | |
| 978 | - } | |
| 979 | - style_header("Server Hack Log"); | |
| 980 | - style_submenu_element("Log-Menu", "%R/setup-logmenu"); | |
| 981 | - | |
| 982 | - if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){ | |
| 983 | - no_error_log_available(); | |
| 984 | - style_finish_page(); | |
| 985 | - return; | |
| 986 | - } | |
| 987 | - in = fossil_fopen(g.zErrlog, "rb"); | |
| 988 | - if( in==0 ){ | |
| 989 | - @ <p class='generalError'>Unable to open that file for reading!</p> | |
| 990 | - style_finish_page(); | |
| 991 | - return; | |
| 992 | - } | |
| 993 | - szFile = file_size(g.zErrlog, ExtFILE); | |
| 994 | - zLog = file_canonical_name_dup(g.zErrlog); | |
| 995 | - @ %s(isNot?"Non-hack":"Hack") messages contained within the %lld(szFile)-byte | |
| 996 | - @ <a href="%R/errorlog?all">error log</a> found at | |
| 997 | - @ "%h(zLog)". | |
| 998 | - fossil_free(zLog); | |
| 999 | - @ <hr> | |
| 1000 | - @ <pre> | |
| 1001 | - while( fgets(z, sizeof(z), in) ){ | |
| 1002 | - if( prevWasTime | |
| 1003 | - && ((strncmp(z,"possible hack attempt - 418 ", 27)==0) ^ isNot) | |
| 1004 | - ){ | |
| 1005 | - @ %h(zTime)\ | |
| 1006 | - bOutput = 1; | |
| 1007 | - } | |
| 1008 | - if( strncmp(z, "--------", 8)==0 ){ | |
| 1009 | - size_t n = strlen(z); | |
| 1010 | - memcpy(zTime, z, n+1); | |
| 1011 | - prevWasTime = 1; | |
| 1012 | - bOutput = 0; | |
| 1013 | - }else{ | |
| 1014 | - prevWasTime = 0; | |
| 1015 | - } | |
| 1016 | - if( bOutput ){ | |
| 1017 | - @ %h(z)\ | |
| 1018 | - } | |
| 1019 | - } | |
| 1020 | - fclose(in); | |
| 1021 | - @ </pre> | |
| 954 | + if( bOutput && eType ){ | |
| 955 | + @ %h(z)\ | |
| 956 | + } | |
| 957 | + } | |
| 958 | + fclose(in); | |
| 959 | + if( eType ){ | |
| 960 | + @ </pre> | |
| 961 | + } | |
| 962 | + if( eType==0 ){ | |
| 963 | + int nNonHack = nPanic + nHang + nOther; | |
| 964 | + int nTotal = nNonHack + nHack + nXPost; | |
| 965 | + @ <p><table border="a" cellspacing="0" cellpadding="5"> | |
| 966 | + if( nPanic>0 ){ | |
| 967 | + @ <tr><td align="right">%d(nPanic)</td> | |
| 968 | + @ <td><a href="./errorlog?y=2">Panics</a></td> | |
| 969 | + } | |
| 970 | + if( nHack>0 ){ | |
| 971 | + @ <tr><td align="right">%d(nHack)</td> | |
| 972 | + @ <td><a href="./errorlog?y=1">Hack Attempts</a></td> | |
| 973 | + } | |
| 974 | + if( nHang>0 ){ | |
| 975 | + @ <tr><td align="right">%d(nHang)</td> | |
| 976 | + @ <td><a href="./errorlog?y=4/">Hung Backoffice</a></td> | |
| 977 | + } | |
| 978 | + if( nXPost>0 ){ | |
| 979 | + @ <tr><td align="right">%d(nXPost)</td> | |
| 980 | + @ <td><a href="./errorlog?y=8/">POSTs from different origin</a></td> | |
| 981 | + } | |
| 982 | + if( nOther>0 ){ | |
| 983 | + @ <tr><td align="right">%d(nOther)</td> | |
| 984 | + @ <td><a href="./errorlog?y=64/">Other</a></td> | |
| 985 | + } | |
| 986 | + if( nHack+nXPost>0 && nNonHack>0 ){ | |
| 987 | + @ <tr><td align="right">%d(nNonHack)</td> | |
| 988 | + @ <td><a href="%R/errorlog?y=70">Other than hack attempts</a></td> | |
| 989 | + } | |
| 990 | + @ <tr><td align="right">%d(nTotal)</td> | |
| 991 | + if( nTotal>0 ){ | |
| 992 | + @ <td><a href="./errorlog?y=255">All Messages</a></td> | |
| 993 | + }else{ | |
| 994 | + @ <td>All Messages</td> | |
| 995 | + } | |
| 996 | + @ </table> | |
| 997 | + } | |
| 1022 | 998 | style_finish_page(); |
| 1023 | 999 | } |
| 1024 | 1000 |
| --- src/security_audit.c | |
| +++ src/security_audit.c | |
| @@ -553,17 +553,17 @@ | |
| 553 | @ checkbox on the <a href="setup_access">Access Control</a> page. |
| 554 | } |
| 555 | |
| 556 | /* Logging should be turned on |
| 557 | */ |
| 558 | if( db_get_boolean("access-log",0)==0 ){ |
| 559 | @ <li><p> |
| 560 | @ The <a href="access_log">User Log</a> is disabled. The user log |
| 561 | @ keeps a record of successful and unsuccessful login attempts and is |
| 562 | @ useful for security monitoring. |
| 563 | } |
| 564 | if( db_get_boolean("admin-log",0)==0 ){ |
| 565 | @ <li><p> |
| 566 | @ The <a href="admin_log">Administrative Log</a> is disabled. |
| 567 | @ The administrative log provides a record of configuration changes |
| 568 | @ and is useful for security monitoring. |
| 569 | } |
| @@ -804,37 +804,59 @@ | |
| 804 | @ </pre></blockquote> |
| 805 | blob_reset(&fullname); |
| 806 | } |
| 807 | } |
| 808 | |
| 809 | /* |
| 810 | ** The maximum number of bytes of the error log to show by default. |
| 811 | */ |
| 812 | #define MXSHOWLOG 500000 |
| 813 | |
| 814 | /* |
| 815 | ** WEBPAGE: errorlog |
| 816 | ** |
| 817 | ** Show the content of the error log. Only the administrator can view |
| 818 | ** this page. |
| 819 | */ |
| 820 | void errorlog_page(void){ |
| 821 | i64 szFile; |
| 822 | FILE *in; |
| 823 | char *zLog; |
| 824 | char z[10000]; |
| 825 | login_check_credentials(); |
| 826 | if( !g.perm.Admin ){ |
| 827 | login_needed(0); |
| 828 | return; |
| 829 | } |
| 830 | style_header("Server Error Log"); |
| 831 | style_submenu_element("Test", "%R/test-warning"); |
| 832 | style_submenu_element("Refresh", "%R/errorlog"); |
| 833 | style_submenu_element("Log-Menu", "%R/setup-logmenu"); |
| 834 | style_submenu_element("Panics", "%R/paniclog"); |
| 835 | style_submenu_element("Non-Hacks", "%R/hacklog?not"); |
| 836 | |
| 837 | if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){ |
| 838 | no_error_log_available(); |
| 839 | style_finish_page(); |
| 840 | return; |
| @@ -861,163 +883,117 @@ | |
| 861 | return; |
| 862 | } |
| 863 | zLog = file_canonical_name_dup(g.zErrlog); |
| 864 | @ <p>The server error log at "%h(zLog)" is %,lld(szFile) bytes in size. |
| 865 | fossil_free(zLog); |
| 866 | style_submenu_element("Download", "%R/errorlog?download"); |
| 867 | style_submenu_element("Truncate", "%R/errorlog?truncate"); |
| 868 | in = fossil_fopen(g.zErrlog, "rb"); |
| 869 | if( in==0 ){ |
| 870 | @ <p class='generalError'>Unable to open that file for reading!</p> |
| 871 | style_finish_page(); |
| 872 | return; |
| 873 | } |
| 874 | if( szFile>MXSHOWLOG && P("all")==0 ){ |
| 875 | @ <form action="%R/errorlog" method="POST"> |
| 876 | @ <p>Only the last %,d(MXSHOWLOG) bytes are shown. |
| 877 | @ <input type="submit" name="all" value="Show All"> |
| 878 | @ </form> |
| 879 | fseek(in, -MXSHOWLOG, SEEK_END); |
| 880 | } |
| 881 | @ <hr> |
| 882 | @ <pre> |
| 883 | while( fgets(z, sizeof(z), in) ){ |
| 884 | @ %h(z)\ |
| 885 | } |
| 886 | fclose(in); |
| 887 | @ </pre> |
| 888 | style_finish_page(); |
| 889 | } |
| 890 | |
| 891 | /* |
| 892 | ** WEBPAGE: paniclog |
| 893 | ** |
| 894 | ** Scan the error log for panics. Show all panic messages, ignoring all |
| 895 | ** other error log entries. |
| 896 | */ |
| 897 | void paniclog_page(void){ |
| 898 | i64 szFile; |
| 899 | char *zLog; |
| 900 | FILE *in; |
| 901 | int bOutput = 0; |
| 902 | int prevWasTime = 0; |
| 903 | char z[10000]; |
| 904 | char zTime[10000]; |
| 905 | |
| 906 | login_check_credentials(); |
| 907 | if( !g.perm.Admin ){ |
| 908 | login_needed(0); |
| 909 | return; |
| 910 | } |
| 911 | style_header("Server Panic Log"); |
| 912 | style_submenu_element("Log-Menu", "%R/setup-logmenu"); |
| 913 | |
| 914 | if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){ |
| 915 | no_error_log_available(); |
| 916 | style_finish_page(); |
| 917 | return; |
| 918 | } |
| 919 | in = fossil_fopen(g.zErrlog, "rb"); |
| 920 | if( in==0 ){ |
| 921 | @ <p class='generalError'>Unable to open that file for reading!</p> |
| 922 | style_finish_page(); |
| 923 | return; |
| 924 | } |
| 925 | szFile = file_size(g.zErrlog, ExtFILE); |
| 926 | zLog = file_canonical_name_dup(g.zErrlog); |
| 927 | @ Panic messages contained within the %lld(szFile)-byte |
| 928 | @ <a href="%R/errorlog?all">error log</a> found at |
| 929 | @ "%h(zLog)". |
| 930 | fossil_free(zLog); |
| 931 | @ <hr> |
| 932 | @ <pre> |
| 933 | while( fgets(z, sizeof(z), in) ){ |
| 934 | if( prevWasTime |
| 935 | && (strncmp(z,"panic: ", 7)==0 || strstr(z," assertion fault ")!=0) |
| 936 | ){ |
| 937 | @ %h(zTime)\ |
| 938 | bOutput = 1; |
| 939 | } |
| 940 | if( strncmp(z, "--------", 8)==0 ){ |
| 941 | size_t n = strlen(z); |
| 942 | memcpy(zTime, z, n+1); |
| 943 | prevWasTime = 1; |
| 944 | bOutput = 0; |
| 945 | }else{ |
| 946 | prevWasTime = 0; |
| 947 | } |
| 948 | if( bOutput ){ |
| 949 | @ %h(z)\ |
| 950 | } |
| 951 | } |
| 952 | fclose(in); |
| 953 | @ </pre> |
| 954 | style_finish_page(); |
| 955 | } |
| 956 | |
| 957 | /* |
| 958 | ** WEBPAGE: hacklog |
| 959 | ** |
| 960 | ** Scan the error log for "possible hack attempt" entries Show hack |
| 961 | ** attempt messages only, omitting all others. Or if the "not" query |
| 962 | ** parameter is present, show only messages that are not hack attempts. |
| 963 | */ |
| 964 | void hacklog_page(void){ |
| 965 | i64 szFile; |
| 966 | char *zLog; |
| 967 | FILE *in; |
| 968 | int bOutput = 0; |
| 969 | int prevWasTime = 0; |
| 970 | int isNot = P("not")!=0; |
| 971 | char z[10000]; |
| 972 | char zTime[10000]; |
| 973 | |
| 974 | login_check_credentials(); |
| 975 | if( !g.perm.Admin ){ |
| 976 | login_needed(0); |
| 977 | return; |
| 978 | } |
| 979 | style_header("Server Hack Log"); |
| 980 | style_submenu_element("Log-Menu", "%R/setup-logmenu"); |
| 981 | |
| 982 | if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){ |
| 983 | no_error_log_available(); |
| 984 | style_finish_page(); |
| 985 | return; |
| 986 | } |
| 987 | in = fossil_fopen(g.zErrlog, "rb"); |
| 988 | if( in==0 ){ |
| 989 | @ <p class='generalError'>Unable to open that file for reading!</p> |
| 990 | style_finish_page(); |
| 991 | return; |
| 992 | } |
| 993 | szFile = file_size(g.zErrlog, ExtFILE); |
| 994 | zLog = file_canonical_name_dup(g.zErrlog); |
| 995 | @ %s(isNot?"Non-hack":"Hack") messages contained within the %lld(szFile)-byte |
| 996 | @ <a href="%R/errorlog?all">error log</a> found at |
| 997 | @ "%h(zLog)". |
| 998 | fossil_free(zLog); |
| 999 | @ <hr> |
| 1000 | @ <pre> |
| 1001 | while( fgets(z, sizeof(z), in) ){ |
| 1002 | if( prevWasTime |
| 1003 | && ((strncmp(z,"possible hack attempt - 418 ", 27)==0) ^ isNot) |
| 1004 | ){ |
| 1005 | @ %h(zTime)\ |
| 1006 | bOutput = 1; |
| 1007 | } |
| 1008 | if( strncmp(z, "--------", 8)==0 ){ |
| 1009 | size_t n = strlen(z); |
| 1010 | memcpy(zTime, z, n+1); |
| 1011 | prevWasTime = 1; |
| 1012 | bOutput = 0; |
| 1013 | }else{ |
| 1014 | prevWasTime = 0; |
| 1015 | } |
| 1016 | if( bOutput ){ |
| 1017 | @ %h(z)\ |
| 1018 | } |
| 1019 | } |
| 1020 | fclose(in); |
| 1021 | @ </pre> |
| 1022 | style_finish_page(); |
| 1023 | } |
| 1024 |
| --- src/security_audit.c | |
| +++ src/security_audit.c | |
| @@ -553,17 +553,17 @@ | |
| 553 | @ checkbox on the <a href="setup_access">Access Control</a> page. |
| 554 | } |
| 555 | |
| 556 | /* Logging should be turned on |
| 557 | */ |
| 558 | if( db_get_boolean("access-log",1)==0 ){ |
| 559 | @ <li><p> |
| 560 | @ The <a href="access_log">User Log</a> is disabled. The user log |
| 561 | @ keeps a record of successful and unsuccessful login attempts and is |
| 562 | @ useful for security monitoring. |
| 563 | } |
| 564 | if( db_get_boolean("admin-log",1)==0 ){ |
| 565 | @ <li><p> |
| 566 | @ The <a href="admin_log">Administrative Log</a> is disabled. |
| 567 | @ The administrative log provides a record of configuration changes |
| 568 | @ and is useful for security monitoring. |
| 569 | } |
| @@ -804,37 +804,59 @@ | |
| 804 | @ </pre></blockquote> |
| 805 | blob_reset(&fullname); |
| 806 | } |
| 807 | } |
| 808 | |
| 809 | /* |
| 810 | ** WEBPAGE: errorlog |
| 811 | ** |
| 812 | ** Show the content of the error log. Only the administrator can view |
| 813 | ** this page. |
| 814 | ** |
| 815 | ** y=0x01 Show only hack attempts |
| 816 | ** y=0x02 Show only panics and assertion faults |
| 817 | ** y=0x04 Show hung backoffice processes |
| 818 | ** y=0x08 Show POST requests from a different origin |
| 819 | ** y=0x40 Show other uncategorized messages |
| 820 | ** |
| 821 | ** If y is omitted or is zero, a count of the various message types is |
| 822 | ** shown. |
| 823 | */ |
| 824 | void errorlog_page(void){ |
| 825 | i64 szFile; |
| 826 | FILE *in; |
| 827 | char *zLog; |
| 828 | const char *zType = P("y"); |
| 829 | static const int eAllTypes = 0x4f; |
| 830 | long eType = 0; |
| 831 | int bOutput = 0; |
| 832 | int prevWasTime = 0; |
| 833 | int nHack = 0; |
| 834 | int nPanic = 0; |
| 835 | int nOther = 0; |
| 836 | int nHang = 0; |
| 837 | int nXPost = 0; |
| 838 | char z[10000]; |
| 839 | char zTime[10000]; |
| 840 | |
| 841 | login_check_credentials(); |
| 842 | if( !g.perm.Admin ){ |
| 843 | login_needed(0); |
| 844 | return; |
| 845 | } |
| 846 | if( zType ){ |
| 847 | eType = strtol(zType,0,0) & eAllTypes; |
| 848 | } |
| 849 | style_header("Server Error Log"); |
| 850 | style_submenu_element("Test", "%R/test-warning"); |
| 851 | style_submenu_element("Refresh", "%R/errorlog"); |
| 852 | style_submenu_element("Download", "%R/errorlog?download"); |
| 853 | style_submenu_element("Truncate", "%R/errorlog?truncate"); |
| 854 | style_submenu_element("Log-Menu", "%R/setup-logmenu"); |
| 855 | if( eType ){ |
| 856 | style_submenu_element("Summary", "%R/errorlog"); |
| 857 | } |
| 858 | |
| 859 | if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){ |
| 860 | no_error_log_available(); |
| 861 | style_finish_page(); |
| 862 | return; |
| @@ -861,163 +883,117 @@ | |
| 883 | return; |
| 884 | } |
| 885 | zLog = file_canonical_name_dup(g.zErrlog); |
| 886 | @ <p>The server error log at "%h(zLog)" is %,lld(szFile) bytes in size. |
| 887 | fossil_free(zLog); |
| 888 | in = fossil_fopen(g.zErrlog, "rb"); |
| 889 | if( in==0 ){ |
| 890 | @ <p class='generalError'>Unable to open that file for reading!</p> |
| 891 | style_finish_page(); |
| 892 | return; |
| 893 | } |
| 894 | if( eType==0 ){ |
| 895 | /* will do a summary */ |
| 896 | }else if( (eType&eAllTypes)!=eAllTypes ){ |
| 897 | @ Only the following types of messages displayed: |
| 898 | @ <ul> |
| 899 | if( eType & 0x01 ){ |
| 900 | @ <li>Hack attempts |
| 901 | } |
| 902 | if( eType & 0x02 ){ |
| 903 | @ <li>Panics and assertion faults |
| 904 | } |
| 905 | if( eType & 0x04 ){ |
| 906 | @ <li>Hung backoffice processes |
| 907 | } |
| 908 | if( eType & 0x08 ){ |
| 909 | @ <li>POST requests from different origin |
| 910 | } |
| 911 | if( eType & 0x40 ){ |
| 912 | @ <li>Other uncategorized messages |
| 913 | } |
| 914 | @ </ul> |
| 915 | } |
| 916 | @ <hr> |
| 917 | if( eType ){ |
| 918 | @ <pre> |
| 919 | } |
| 920 | while( fgets(z, sizeof(z), in) ){ |
| 921 | if( prevWasTime ){ |
| 922 | if( strncmp(z,"possible hack attempt - 418 ", 27)==0 ){ |
| 923 | bOutput = (eType & 0x01)!=0; |
| 924 | nHack++; |
| 925 | }else |
| 926 | if( (strncmp(z,"panic: ", 7)==0 || strstr(z," assertion fault ")!=0) ){ |
| 927 | bOutput = (eType & 0x02)!=0; |
| 928 | nPanic++; |
| 929 | }else |
| 930 | if( sqlite3_strglob("warning: backoffice process * still *",z)==0 ){ |
| 931 | bOutput = (eType & 0x04)!=0; |
| 932 | nHang++; |
| 933 | }else |
| 934 | if( sqlite3_strglob("warning: POST from different origin*",z)==0 ){ |
| 935 | bOutput = (eType & 0x08)!=0; |
| 936 | nXPost++; |
| 937 | }else |
| 938 | { |
| 939 | bOutput = (eType & 0x40)!=0; |
| 940 | nOther++; |
| 941 | } |
| 942 | if( bOutput ){ |
| 943 | @ %h(zTime)\ |
| 944 | } |
| 945 | } |
| 946 | if( strncmp(z, "--------", 8)==0 ){ |
| 947 | size_t n = strlen(z); |
| 948 | memcpy(zTime, z, n+1); |
| 949 | prevWasTime = 1; |
| 950 | bOutput = 0; |
| 951 | }else{ |
| 952 | prevWasTime = 0; |
| 953 | } |
| 954 | if( bOutput && eType ){ |
| 955 | @ %h(z)\ |
| 956 | } |
| 957 | } |
| 958 | fclose(in); |
| 959 | if( eType ){ |
| 960 | @ </pre> |
| 961 | } |
| 962 | if( eType==0 ){ |
| 963 | int nNonHack = nPanic + nHang + nOther; |
| 964 | int nTotal = nNonHack + nHack + nXPost; |
| 965 | @ <p><table border="a" cellspacing="0" cellpadding="5"> |
| 966 | if( nPanic>0 ){ |
| 967 | @ <tr><td align="right">%d(nPanic)</td> |
| 968 | @ <td><a href="./errorlog?y=2">Panics</a></td> |
| 969 | } |
| 970 | if( nHack>0 ){ |
| 971 | @ <tr><td align="right">%d(nHack)</td> |
| 972 | @ <td><a href="./errorlog?y=1">Hack Attempts</a></td> |
| 973 | } |
| 974 | if( nHang>0 ){ |
| 975 | @ <tr><td align="right">%d(nHang)</td> |
| 976 | @ <td><a href="./errorlog?y=4/">Hung Backoffice</a></td> |
| 977 | } |
| 978 | if( nXPost>0 ){ |
| 979 | @ <tr><td align="right">%d(nXPost)</td> |
| 980 | @ <td><a href="./errorlog?y=8/">POSTs from different origin</a></td> |
| 981 | } |
| 982 | if( nOther>0 ){ |
| 983 | @ <tr><td align="right">%d(nOther)</td> |
| 984 | @ <td><a href="./errorlog?y=64/">Other</a></td> |
| 985 | } |
| 986 | if( nHack+nXPost>0 && nNonHack>0 ){ |
| 987 | @ <tr><td align="right">%d(nNonHack)</td> |
| 988 | @ <td><a href="%R/errorlog?y=70">Other than hack attempts</a></td> |
| 989 | } |
| 990 | @ <tr><td align="right">%d(nTotal)</td> |
| 991 | if( nTotal>0 ){ |
| 992 | @ <td><a href="./errorlog?y=255">All Messages</a></td> |
| 993 | }else{ |
| 994 | @ <td>All Messages</td> |
| 995 | } |
| 996 | @ </table> |
| 997 | } |
| 998 | style_finish_page(); |
| 999 | } |
| 1000 |
+36
-37
| --- src/setup.c | ||
| +++ src/setup.c | ||
| @@ -183,11 +183,11 @@ | ||
| 183 | 183 | |
| 184 | 184 | |
| 185 | 185 | /* |
| 186 | 186 | ** WEBPAGE: setup-logmenu |
| 187 | 187 | ** |
| 188 | -** Show a menu of available log renderings accessible to an administrator, | |
| 188 | +** Show a menu of available log renderings accessible to an administrator, | |
| 189 | 189 | ** together with a succinct explanation of each. |
| 190 | 190 | ** |
| 191 | 191 | ** This page is only accessible by administrators. |
| 192 | 192 | */ |
| 193 | 193 | void setup_logmenu_page(void){ |
| @@ -202,11 +202,11 @@ | ||
| 202 | 202 | return; |
| 203 | 203 | } |
| 204 | 204 | style_header("Log Menu"); |
| 205 | 205 | @ <table border="0" cellspacing="3"> |
| 206 | 206 | |
| 207 | - if( db_get_boolean("admin-log",0)==0 ){ | |
| 207 | + if( db_get_boolean("admin-log",1)==0 ){ | |
| 208 | 208 | blob_appendf(&desc, |
| 209 | 209 | "The admin log records configuration changes to the repository.\n" |
| 210 | 210 | "<b>Disabled</b>: Turn on the " |
| 211 | 211 | " <a href='%R/setup_settings'>admin-log setting</a> to enable." |
| 212 | 212 | ); |
| @@ -220,11 +220,11 @@ | ||
| 220 | 220 | } |
| 221 | 221 | setup_menu_entry("Artifact Log", "rcvfromlist", |
| 222 | 222 | "The artifact log records when new content is added in the\n" |
| 223 | 223 | "\"rcvfrom\" table.\n" |
| 224 | 224 | ); |
| 225 | - if( db_get_boolean("access-log",0) ){ | |
| 225 | + if( db_get_boolean("access-log",1) ){ | |
| 226 | 226 | setup_menu_entry("User Log", "user_log", |
| 227 | 227 | "Login attempts recorded in the \"accesslog\" table." |
| 228 | 228 | ); |
| 229 | 229 | }else{ |
| 230 | 230 | blob_appendf(&desc, |
| @@ -264,27 +264,10 @@ | ||
| 264 | 264 | bErrLog = 1; |
| 265 | 265 | } |
| 266 | 266 | setup_menu_entry("Error Log", bErrLog ? "errorlog" : 0, blob_str(&desc)); |
| 267 | 267 | blob_reset(&desc); |
| 268 | 268 | |
| 269 | - @ <tr><td><td><td> | |
| 270 | - @ —— | |
| 271 | - @ <i>The remaining links are subsets of the Error Log</i> | |
| 272 | - @ —— | |
| 273 | - @ </td> | |
| 274 | - | |
| 275 | - setup_menu_entry("Panic Log", bErrLog ? "paniclog" : 0, | |
| 276 | - "Only the most important messages in the Error Log:\n" | |
| 277 | - "assertion faults, segmentation faults, and similar malfunctions.\n" | |
| 278 | - ); | |
| 279 | - setup_menu_entry("Hack Log", bErrLog ? "hacklog" : 0, | |
| 280 | - "All code-418 hack attempts in the Error Log" | |
| 281 | - ); | |
| 282 | - setup_menu_entry("Non-Hack Log", bErrLog ? "hacklog?not" : 0, | |
| 283 | - "All log messages that are not code-418 hack attempts" | |
| 284 | - ); | |
| 285 | - | |
| 286 | 269 | @ </table> |
| 287 | 270 | style_finish_page(); |
| 288 | 271 | } |
| 289 | 272 | |
| 290 | 273 | /* |
| @@ -952,11 +935,11 @@ | ||
| 952 | 935 | @ of project-name. This description can be edited in the second entry |
| 953 | 936 | @ box on the <a href="./setup_config">Setup/Configuration page</a>. |
| 954 | 937 | @ |
| 955 | 938 | @ <li><p><b>project-name</b> → |
| 956 | 939 | @ The human-readable name for the project. The project-name can be |
| 957 | - @ modified in the first entry on the | |
| 940 | + @ modified in the first entry on the | |
| 958 | 941 | @ <a href="./setup_config">Setup/Configuration page</a>. |
| 959 | 942 | @ |
| 960 | 943 | @ <li><p><b>peer-repo-<i>CODE</i></b> → |
| 961 | 944 | @ <i>CODE</i> is 16-character prefix of the project-code for another |
| 962 | 945 | @ repository that is part of the same login-group. The value is the |
| @@ -998,17 +981,10 @@ | ||
| 998 | 981 | db_begin_transaction(); |
| 999 | 982 | @ <form action="%R/setup_timeline" method="post"><div> |
| 1000 | 983 | login_insert_csrf_secret(); |
| 1001 | 984 | @ <p><input type="submit" name="submit" value="Apply Changes"></p> |
| 1002 | 985 | |
| 1003 | - @ <hr> | |
| 1004 | - onoff_attribute("Allow block-markup in timeline", | |
| 1005 | - "timeline-block-markup", "tbm", 0, 0); | |
| 1006 | - @ <p>In timeline displays, check-in comments can be displayed with or | |
| 1007 | - @ without block markup such as paragraphs, tables, etc. | |
| 1008 | - @ (Property: "timeline-block-markup")</p> | |
| 1009 | - | |
| 1010 | 986 | @ <hr> |
| 1011 | 987 | onoff_attribute("Plaintext comments on timelines", |
| 1012 | 988 | "timeline-plaintext", "tpt", 0, 0); |
| 1013 | 989 | @ <p>In timeline displays, check-in comments are displayed literally, |
| 1014 | 990 | @ without any wiki or HTML interpretation. Use CSS to change |
| @@ -1126,10 +1102,11 @@ | ||
| 1126 | 1102 | */ |
| 1127 | 1103 | void setup_settings(void){ |
| 1128 | 1104 | int nSetting; |
| 1129 | 1105 | int i; |
| 1130 | 1106 | Setting const *pSet; |
| 1107 | + int bIfChng = P("all")==0; | |
| 1131 | 1108 | const Setting *aSetting = setting_info(&nSetting); |
| 1132 | 1109 | |
| 1133 | 1110 | login_check_credentials(); |
| 1134 | 1111 | if( !g.perm.Setup ){ |
| 1135 | 1112 | login_needed(0); |
| @@ -1142,23 +1119,36 @@ | ||
| 1142 | 1119 | /* Provide read-only access to versioned settings, |
| 1143 | 1120 | but only if no repo file was explicitly provided. */ |
| 1144 | 1121 | db_open_local(0); |
| 1145 | 1122 | } |
| 1146 | 1123 | db_begin_transaction(); |
| 1124 | + if( bIfChng ){ | |
| 1125 | + @ <p>Only settings whose value is different from the default are shown. | |
| 1126 | + @ Click the "All" button above to set all settings. | |
| 1127 | + } | |
| 1147 | 1128 | @ <p>Settings marked with (v) are "versionable" and will be overridden |
| 1148 | 1129 | @ by the contents of managed files named |
| 1149 | 1130 | @ "<tt>.fossil-settings/</tt><i>SETTING-NAME</i>". |
| 1150 | 1131 | @ If the file for a versionable setting exists, the value cannot be |
| 1151 | 1132 | @ changed on this screen.</p><hr><p> |
| 1152 | 1133 | @ |
| 1153 | 1134 | @ <form action="%R/setup_settings" method="post"><div> |
| 1135 | + if( bIfChng ){ | |
| 1136 | + style_submenu_element("All", "%R/setup_settings?all"); | |
| 1137 | + }else{ | |
| 1138 | + @ <input type="hidden" name="all" value="1"> | |
| 1139 | + style_submenu_element("Changes-Only", "%R/setup_settings"); | |
| 1140 | + } | |
| 1154 | 1141 | @ <table border="0"><tr><td valign="top"> |
| 1155 | 1142 | login_insert_csrf_secret(); |
| 1156 | 1143 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| 1157 | 1144 | if( pSet->width==0 ){ |
| 1158 | 1145 | int hasVersionableValue = pSet->versionable && |
| 1159 | - (db_get_versioned(pSet->name, NULL)!=0); | |
| 1146 | + (db_get_versioned(pSet->name, NULL, NULL)!=0); | |
| 1147 | + if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){ | |
| 1148 | + continue; | |
| 1149 | + } | |
| 1160 | 1150 | onoff_attribute("", pSet->name, |
| 1161 | 1151 | pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/, |
| 1162 | 1152 | is_truth(pSet->def), hasVersionableValue); |
| 1163 | 1153 | @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a> |
| 1164 | 1154 | if( pSet->versionable ){ |
| @@ -1172,11 +1162,14 @@ | ||
| 1172 | 1162 | @ </td><td style="width:50px;"></td><td valign="top"> |
| 1173 | 1163 | @ <table> |
| 1174 | 1164 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| 1175 | 1165 | if( pSet->width>0 && !pSet->forceTextArea ){ |
| 1176 | 1166 | int hasVersionableValue = pSet->versionable && |
| 1177 | - (db_get_versioned(pSet->name, NULL)!=0); | |
| 1167 | + (db_get_versioned(pSet->name, NULL, NULL)!=0); | |
| 1168 | + if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){ | |
| 1169 | + continue; | |
| 1170 | + } | |
| 1178 | 1171 | @ <tr><td> |
| 1179 | 1172 | @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a> |
| 1180 | 1173 | if( pSet->versionable ){ |
| 1181 | 1174 | @ (v) |
| 1182 | 1175 | } else { |
| @@ -1191,11 +1184,14 @@ | ||
| 1191 | 1184 | } |
| 1192 | 1185 | @</table> |
| 1193 | 1186 | @ </td><td style="width:50px;"></td><td valign="top"> |
| 1194 | 1187 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| 1195 | 1188 | if( pSet->width>0 && pSet->forceTextArea ){ |
| 1196 | - int hasVersionableValue = db_get_versioned(pSet->name, NULL)!=0; | |
| 1189 | + int hasVersionableValue = db_get_versioned(pSet->name, NULL, NULL)!=0; | |
| 1190 | + if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){ | |
| 1191 | + continue; | |
| 1192 | + } | |
| 1197 | 1193 | @ <a href='%R/help?cmd=%s(pSet->name)'>%s(pSet->name)</a> |
| 1198 | 1194 | if( pSet->versionable ){ |
| 1199 | 1195 | @ (v)<br> |
| 1200 | 1196 | } else { |
| 1201 | 1197 | @ <br> |
| @@ -1428,21 +1424,22 @@ | ||
| 1428 | 1424 | db_begin_transaction(); |
| 1429 | 1425 | @ <form action="%R/setup_wiki" method="post"><div> |
| 1430 | 1426 | login_insert_csrf_secret(); |
| 1431 | 1427 | @ <input type="submit" name="submit" value="Apply Changes"></p> |
| 1432 | 1428 | @ <hr> |
| 1433 | - onoff_attribute("Associate Wiki Pages With Branches, Tags, or Checkins", | |
| 1429 | + onoff_attribute("Associate Wiki Pages With Branches, Tags, Tickets, or Checkins", | |
| 1434 | 1430 | "wiki-about", "wiki-about", 1, 0); |
| 1435 | 1431 | @ <p> |
| 1436 | - @ Associate wiki pages with branches, tags, or checkins, based on | |
| 1437 | - @ the wiki page name. Wiki pages that begin with "branch/", "checkin/" | |
| 1438 | - @ or "tag/" and which continue with the name of an existing branch, check-in | |
| 1439 | - @ or tag are treated specially when this feature is enabled. | |
| 1432 | + @ Associate wiki pages with branches, tags, tickets, or checkins, based on | |
| 1433 | + @ the wiki page name. Wiki pages that begin with "branch/", "checkin/", | |
| 1434 | + @ "tag/" or "ticket" and which continue with the name of an existing branch, | |
| 1435 | + @ check-in, tag or ticket are treated specially when this feature is enabled. | |
| 1440 | 1436 | @ <ul> |
| 1441 | 1437 | @ <li> <b>branch/</b><i>branch-name</i> |
| 1442 | 1438 | @ <li> <b>checkin/</b><i>full-check-in-hash</i> |
| 1443 | 1439 | @ <li> <b>tag/</b><i>tag-name</i> |
| 1440 | + @ <li> <b>ticket/</b><i>full-ticket-hash</i> | |
| 1444 | 1441 | @ </ul> |
| 1445 | 1442 | @ (Property: "wiki-about")</p> |
| 1446 | 1443 | @ <hr> |
| 1447 | 1444 | entry_attribute("Allow Unsafe HTML In Markdown", 6, |
| 1448 | 1445 | "safe-html", "safe-html", "", 0); |
| @@ -2137,11 +2134,11 @@ | ||
| 2137 | 2134 | style_header("Admin Log"); |
| 2138 | 2135 | style_submenu_element("Log-Menu", "setup-logmenu"); |
| 2139 | 2136 | create_admin_log_table(); |
| 2140 | 2137 | limit = atoi(PD("n","200")); |
| 2141 | 2138 | ofst = atoi(PD("x","0")); |
| 2142 | - fLogEnabled = db_get_boolean("admin-log", 0); | |
| 2139 | + fLogEnabled = db_get_boolean("admin-log", 1); | |
| 2143 | 2140 | @ <div>Admin logging is %s(fLogEnabled?"on":"off"). |
| 2144 | 2141 | @ (Change this on the <a href="setup_settings">settings</a> page.)</div> |
| 2145 | 2142 | |
| 2146 | 2143 | if( ofst>0 ){ |
| 2147 | 2144 | int prevx = ofst - limit; |
| @@ -2251,10 +2248,12 @@ | ||
| 2251 | 2248 | onoff_attribute("Search Wiki", "search-wiki", "sw", 0, 0); |
| 2252 | 2249 | @ <br> |
| 2253 | 2250 | onoff_attribute("Search Tech Notes", "search-technote", "se", 0, 0); |
| 2254 | 2251 | @ <br> |
| 2255 | 2252 | onoff_attribute("Search Forum", "search-forum", "sf", 0, 0); |
| 2253 | + @ <br> | |
| 2254 | + onoff_attribute("Search Built-in Help Text", "search-help", "sh", 0, 0); | |
| 2256 | 2255 | @ <hr> |
| 2257 | 2256 | @ <p><input type="submit" name="submit" value="Apply Changes"></p> |
| 2258 | 2257 | @ <hr> |
| 2259 | 2258 | if( P("fts0") ){ |
| 2260 | 2259 | search_drop_index(); |
| 2261 | 2260 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -183,11 +183,11 @@ | |
| 183 | |
| 184 | |
| 185 | /* |
| 186 | ** WEBPAGE: setup-logmenu |
| 187 | ** |
| 188 | ** Show a menu of available log renderings accessible to an administrator, |
| 189 | ** together with a succinct explanation of each. |
| 190 | ** |
| 191 | ** This page is only accessible by administrators. |
| 192 | */ |
| 193 | void setup_logmenu_page(void){ |
| @@ -202,11 +202,11 @@ | |
| 202 | return; |
| 203 | } |
| 204 | style_header("Log Menu"); |
| 205 | @ <table border="0" cellspacing="3"> |
| 206 | |
| 207 | if( db_get_boolean("admin-log",0)==0 ){ |
| 208 | blob_appendf(&desc, |
| 209 | "The admin log records configuration changes to the repository.\n" |
| 210 | "<b>Disabled</b>: Turn on the " |
| 211 | " <a href='%R/setup_settings'>admin-log setting</a> to enable." |
| 212 | ); |
| @@ -220,11 +220,11 @@ | |
| 220 | } |
| 221 | setup_menu_entry("Artifact Log", "rcvfromlist", |
| 222 | "The artifact log records when new content is added in the\n" |
| 223 | "\"rcvfrom\" table.\n" |
| 224 | ); |
| 225 | if( db_get_boolean("access-log",0) ){ |
| 226 | setup_menu_entry("User Log", "user_log", |
| 227 | "Login attempts recorded in the \"accesslog\" table." |
| 228 | ); |
| 229 | }else{ |
| 230 | blob_appendf(&desc, |
| @@ -264,27 +264,10 @@ | |
| 264 | bErrLog = 1; |
| 265 | } |
| 266 | setup_menu_entry("Error Log", bErrLog ? "errorlog" : 0, blob_str(&desc)); |
| 267 | blob_reset(&desc); |
| 268 | |
| 269 | @ <tr><td><td><td> |
| 270 | @ —— |
| 271 | @ <i>The remaining links are subsets of the Error Log</i> |
| 272 | @ —— |
| 273 | @ </td> |
| 274 | |
| 275 | setup_menu_entry("Panic Log", bErrLog ? "paniclog" : 0, |
| 276 | "Only the most important messages in the Error Log:\n" |
| 277 | "assertion faults, segmentation faults, and similar malfunctions.\n" |
| 278 | ); |
| 279 | setup_menu_entry("Hack Log", bErrLog ? "hacklog" : 0, |
| 280 | "All code-418 hack attempts in the Error Log" |
| 281 | ); |
| 282 | setup_menu_entry("Non-Hack Log", bErrLog ? "hacklog?not" : 0, |
| 283 | "All log messages that are not code-418 hack attempts" |
| 284 | ); |
| 285 | |
| 286 | @ </table> |
| 287 | style_finish_page(); |
| 288 | } |
| 289 | |
| 290 | /* |
| @@ -952,11 +935,11 @@ | |
| 952 | @ of project-name. This description can be edited in the second entry |
| 953 | @ box on the <a href="./setup_config">Setup/Configuration page</a>. |
| 954 | @ |
| 955 | @ <li><p><b>project-name</b> → |
| 956 | @ The human-readable name for the project. The project-name can be |
| 957 | @ modified in the first entry on the |
| 958 | @ <a href="./setup_config">Setup/Configuration page</a>. |
| 959 | @ |
| 960 | @ <li><p><b>peer-repo-<i>CODE</i></b> → |
| 961 | @ <i>CODE</i> is 16-character prefix of the project-code for another |
| 962 | @ repository that is part of the same login-group. The value is the |
| @@ -998,17 +981,10 @@ | |
| 998 | db_begin_transaction(); |
| 999 | @ <form action="%R/setup_timeline" method="post"><div> |
| 1000 | login_insert_csrf_secret(); |
| 1001 | @ <p><input type="submit" name="submit" value="Apply Changes"></p> |
| 1002 | |
| 1003 | @ <hr> |
| 1004 | onoff_attribute("Allow block-markup in timeline", |
| 1005 | "timeline-block-markup", "tbm", 0, 0); |
| 1006 | @ <p>In timeline displays, check-in comments can be displayed with or |
| 1007 | @ without block markup such as paragraphs, tables, etc. |
| 1008 | @ (Property: "timeline-block-markup")</p> |
| 1009 | |
| 1010 | @ <hr> |
| 1011 | onoff_attribute("Plaintext comments on timelines", |
| 1012 | "timeline-plaintext", "tpt", 0, 0); |
| 1013 | @ <p>In timeline displays, check-in comments are displayed literally, |
| 1014 | @ without any wiki or HTML interpretation. Use CSS to change |
| @@ -1126,10 +1102,11 @@ | |
| 1126 | */ |
| 1127 | void setup_settings(void){ |
| 1128 | int nSetting; |
| 1129 | int i; |
| 1130 | Setting const *pSet; |
| 1131 | const Setting *aSetting = setting_info(&nSetting); |
| 1132 | |
| 1133 | login_check_credentials(); |
| 1134 | if( !g.perm.Setup ){ |
| 1135 | login_needed(0); |
| @@ -1142,23 +1119,36 @@ | |
| 1142 | /* Provide read-only access to versioned settings, |
| 1143 | but only if no repo file was explicitly provided. */ |
| 1144 | db_open_local(0); |
| 1145 | } |
| 1146 | db_begin_transaction(); |
| 1147 | @ <p>Settings marked with (v) are "versionable" and will be overridden |
| 1148 | @ by the contents of managed files named |
| 1149 | @ "<tt>.fossil-settings/</tt><i>SETTING-NAME</i>". |
| 1150 | @ If the file for a versionable setting exists, the value cannot be |
| 1151 | @ changed on this screen.</p><hr><p> |
| 1152 | @ |
| 1153 | @ <form action="%R/setup_settings" method="post"><div> |
| 1154 | @ <table border="0"><tr><td valign="top"> |
| 1155 | login_insert_csrf_secret(); |
| 1156 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| 1157 | if( pSet->width==0 ){ |
| 1158 | int hasVersionableValue = pSet->versionable && |
| 1159 | (db_get_versioned(pSet->name, NULL)!=0); |
| 1160 | onoff_attribute("", pSet->name, |
| 1161 | pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/, |
| 1162 | is_truth(pSet->def), hasVersionableValue); |
| 1163 | @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a> |
| 1164 | if( pSet->versionable ){ |
| @@ -1172,11 +1162,14 @@ | |
| 1172 | @ </td><td style="width:50px;"></td><td valign="top"> |
| 1173 | @ <table> |
| 1174 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| 1175 | if( pSet->width>0 && !pSet->forceTextArea ){ |
| 1176 | int hasVersionableValue = pSet->versionable && |
| 1177 | (db_get_versioned(pSet->name, NULL)!=0); |
| 1178 | @ <tr><td> |
| 1179 | @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a> |
| 1180 | if( pSet->versionable ){ |
| 1181 | @ (v) |
| 1182 | } else { |
| @@ -1191,11 +1184,14 @@ | |
| 1191 | } |
| 1192 | @</table> |
| 1193 | @ </td><td style="width:50px;"></td><td valign="top"> |
| 1194 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| 1195 | if( pSet->width>0 && pSet->forceTextArea ){ |
| 1196 | int hasVersionableValue = db_get_versioned(pSet->name, NULL)!=0; |
| 1197 | @ <a href='%R/help?cmd=%s(pSet->name)'>%s(pSet->name)</a> |
| 1198 | if( pSet->versionable ){ |
| 1199 | @ (v)<br> |
| 1200 | } else { |
| 1201 | @ <br> |
| @@ -1428,21 +1424,22 @@ | |
| 1428 | db_begin_transaction(); |
| 1429 | @ <form action="%R/setup_wiki" method="post"><div> |
| 1430 | login_insert_csrf_secret(); |
| 1431 | @ <input type="submit" name="submit" value="Apply Changes"></p> |
| 1432 | @ <hr> |
| 1433 | onoff_attribute("Associate Wiki Pages With Branches, Tags, or Checkins", |
| 1434 | "wiki-about", "wiki-about", 1, 0); |
| 1435 | @ <p> |
| 1436 | @ Associate wiki pages with branches, tags, or checkins, based on |
| 1437 | @ the wiki page name. Wiki pages that begin with "branch/", "checkin/" |
| 1438 | @ or "tag/" and which continue with the name of an existing branch, check-in |
| 1439 | @ or tag are treated specially when this feature is enabled. |
| 1440 | @ <ul> |
| 1441 | @ <li> <b>branch/</b><i>branch-name</i> |
| 1442 | @ <li> <b>checkin/</b><i>full-check-in-hash</i> |
| 1443 | @ <li> <b>tag/</b><i>tag-name</i> |
| 1444 | @ </ul> |
| 1445 | @ (Property: "wiki-about")</p> |
| 1446 | @ <hr> |
| 1447 | entry_attribute("Allow Unsafe HTML In Markdown", 6, |
| 1448 | "safe-html", "safe-html", "", 0); |
| @@ -2137,11 +2134,11 @@ | |
| 2137 | style_header("Admin Log"); |
| 2138 | style_submenu_element("Log-Menu", "setup-logmenu"); |
| 2139 | create_admin_log_table(); |
| 2140 | limit = atoi(PD("n","200")); |
| 2141 | ofst = atoi(PD("x","0")); |
| 2142 | fLogEnabled = db_get_boolean("admin-log", 0); |
| 2143 | @ <div>Admin logging is %s(fLogEnabled?"on":"off"). |
| 2144 | @ (Change this on the <a href="setup_settings">settings</a> page.)</div> |
| 2145 | |
| 2146 | if( ofst>0 ){ |
| 2147 | int prevx = ofst - limit; |
| @@ -2251,10 +2248,12 @@ | |
| 2251 | onoff_attribute("Search Wiki", "search-wiki", "sw", 0, 0); |
| 2252 | @ <br> |
| 2253 | onoff_attribute("Search Tech Notes", "search-technote", "se", 0, 0); |
| 2254 | @ <br> |
| 2255 | onoff_attribute("Search Forum", "search-forum", "sf", 0, 0); |
| 2256 | @ <hr> |
| 2257 | @ <p><input type="submit" name="submit" value="Apply Changes"></p> |
| 2258 | @ <hr> |
| 2259 | if( P("fts0") ){ |
| 2260 | search_drop_index(); |
| 2261 |
| --- src/setup.c | |
| +++ src/setup.c | |
| @@ -183,11 +183,11 @@ | |
| 183 | |
| 184 | |
| 185 | /* |
| 186 | ** WEBPAGE: setup-logmenu |
| 187 | ** |
| 188 | ** Show a menu of available log renderings accessible to an administrator, |
| 189 | ** together with a succinct explanation of each. |
| 190 | ** |
| 191 | ** This page is only accessible by administrators. |
| 192 | */ |
| 193 | void setup_logmenu_page(void){ |
| @@ -202,11 +202,11 @@ | |
| 202 | return; |
| 203 | } |
| 204 | style_header("Log Menu"); |
| 205 | @ <table border="0" cellspacing="3"> |
| 206 | |
| 207 | if( db_get_boolean("admin-log",1)==0 ){ |
| 208 | blob_appendf(&desc, |
| 209 | "The admin log records configuration changes to the repository.\n" |
| 210 | "<b>Disabled</b>: Turn on the " |
| 211 | " <a href='%R/setup_settings'>admin-log setting</a> to enable." |
| 212 | ); |
| @@ -220,11 +220,11 @@ | |
| 220 | } |
| 221 | setup_menu_entry("Artifact Log", "rcvfromlist", |
| 222 | "The artifact log records when new content is added in the\n" |
| 223 | "\"rcvfrom\" table.\n" |
| 224 | ); |
| 225 | if( db_get_boolean("access-log",1) ){ |
| 226 | setup_menu_entry("User Log", "user_log", |
| 227 | "Login attempts recorded in the \"accesslog\" table." |
| 228 | ); |
| 229 | }else{ |
| 230 | blob_appendf(&desc, |
| @@ -264,27 +264,10 @@ | |
| 264 | bErrLog = 1; |
| 265 | } |
| 266 | setup_menu_entry("Error Log", bErrLog ? "errorlog" : 0, blob_str(&desc)); |
| 267 | blob_reset(&desc); |
| 268 | |
| 269 | @ </table> |
| 270 | style_finish_page(); |
| 271 | } |
| 272 | |
| 273 | /* |
| @@ -952,11 +935,11 @@ | |
| 935 | @ of project-name. This description can be edited in the second entry |
| 936 | @ box on the <a href="./setup_config">Setup/Configuration page</a>. |
| 937 | @ |
| 938 | @ <li><p><b>project-name</b> → |
| 939 | @ The human-readable name for the project. The project-name can be |
| 940 | @ modified in the first entry on the |
| 941 | @ <a href="./setup_config">Setup/Configuration page</a>. |
| 942 | @ |
| 943 | @ <li><p><b>peer-repo-<i>CODE</i></b> → |
| 944 | @ <i>CODE</i> is 16-character prefix of the project-code for another |
| 945 | @ repository that is part of the same login-group. The value is the |
| @@ -998,17 +981,10 @@ | |
| 981 | db_begin_transaction(); |
| 982 | @ <form action="%R/setup_timeline" method="post"><div> |
| 983 | login_insert_csrf_secret(); |
| 984 | @ <p><input type="submit" name="submit" value="Apply Changes"></p> |
| 985 | |
| 986 | @ <hr> |
| 987 | onoff_attribute("Plaintext comments on timelines", |
| 988 | "timeline-plaintext", "tpt", 0, 0); |
| 989 | @ <p>In timeline displays, check-in comments are displayed literally, |
| 990 | @ without any wiki or HTML interpretation. Use CSS to change |
| @@ -1126,10 +1102,11 @@ | |
| 1102 | */ |
| 1103 | void setup_settings(void){ |
| 1104 | int nSetting; |
| 1105 | int i; |
| 1106 | Setting const *pSet; |
| 1107 | int bIfChng = P("all")==0; |
| 1108 | const Setting *aSetting = setting_info(&nSetting); |
| 1109 | |
| 1110 | login_check_credentials(); |
| 1111 | if( !g.perm.Setup ){ |
| 1112 | login_needed(0); |
| @@ -1142,23 +1119,36 @@ | |
| 1119 | /* Provide read-only access to versioned settings, |
| 1120 | but only if no repo file was explicitly provided. */ |
| 1121 | db_open_local(0); |
| 1122 | } |
| 1123 | db_begin_transaction(); |
| 1124 | if( bIfChng ){ |
| 1125 | @ <p>Only settings whose value is different from the default are shown. |
| 1126 | @ Click the "All" button above to set all settings. |
| 1127 | } |
| 1128 | @ <p>Settings marked with (v) are "versionable" and will be overridden |
| 1129 | @ by the contents of managed files named |
| 1130 | @ "<tt>.fossil-settings/</tt><i>SETTING-NAME</i>". |
| 1131 | @ If the file for a versionable setting exists, the value cannot be |
| 1132 | @ changed on this screen.</p><hr><p> |
| 1133 | @ |
| 1134 | @ <form action="%R/setup_settings" method="post"><div> |
| 1135 | if( bIfChng ){ |
| 1136 | style_submenu_element("All", "%R/setup_settings?all"); |
| 1137 | }else{ |
| 1138 | @ <input type="hidden" name="all" value="1"> |
| 1139 | style_submenu_element("Changes-Only", "%R/setup_settings"); |
| 1140 | } |
| 1141 | @ <table border="0"><tr><td valign="top"> |
| 1142 | login_insert_csrf_secret(); |
| 1143 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| 1144 | if( pSet->width==0 ){ |
| 1145 | int hasVersionableValue = pSet->versionable && |
| 1146 | (db_get_versioned(pSet->name, NULL, NULL)!=0); |
| 1147 | if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){ |
| 1148 | continue; |
| 1149 | } |
| 1150 | onoff_attribute("", pSet->name, |
| 1151 | pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/, |
| 1152 | is_truth(pSet->def), hasVersionableValue); |
| 1153 | @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a> |
| 1154 | if( pSet->versionable ){ |
| @@ -1172,11 +1162,14 @@ | |
| 1162 | @ </td><td style="width:50px;"></td><td valign="top"> |
| 1163 | @ <table> |
| 1164 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| 1165 | if( pSet->width>0 && !pSet->forceTextArea ){ |
| 1166 | int hasVersionableValue = pSet->versionable && |
| 1167 | (db_get_versioned(pSet->name, NULL, NULL)!=0); |
| 1168 | if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){ |
| 1169 | continue; |
| 1170 | } |
| 1171 | @ <tr><td> |
| 1172 | @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a> |
| 1173 | if( pSet->versionable ){ |
| 1174 | @ (v) |
| 1175 | } else { |
| @@ -1191,11 +1184,14 @@ | |
| 1184 | } |
| 1185 | @</table> |
| 1186 | @ </td><td style="width:50px;"></td><td valign="top"> |
| 1187 | for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){ |
| 1188 | if( pSet->width>0 && pSet->forceTextArea ){ |
| 1189 | int hasVersionableValue = db_get_versioned(pSet->name, NULL, NULL)!=0; |
| 1190 | if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){ |
| 1191 | continue; |
| 1192 | } |
| 1193 | @ <a href='%R/help?cmd=%s(pSet->name)'>%s(pSet->name)</a> |
| 1194 | if( pSet->versionable ){ |
| 1195 | @ (v)<br> |
| 1196 | } else { |
| 1197 | @ <br> |
| @@ -1428,21 +1424,22 @@ | |
| 1424 | db_begin_transaction(); |
| 1425 | @ <form action="%R/setup_wiki" method="post"><div> |
| 1426 | login_insert_csrf_secret(); |
| 1427 | @ <input type="submit" name="submit" value="Apply Changes"></p> |
| 1428 | @ <hr> |
| 1429 | onoff_attribute("Associate Wiki Pages With Branches, Tags, Tickets, or Checkins", |
| 1430 | "wiki-about", "wiki-about", 1, 0); |
| 1431 | @ <p> |
| 1432 | @ Associate wiki pages with branches, tags, tickets, or checkins, based on |
| 1433 | @ the wiki page name. Wiki pages that begin with "branch/", "checkin/", |
| 1434 | @ "tag/" or "ticket" and which continue with the name of an existing branch, |
| 1435 | @ check-in, tag or ticket are treated specially when this feature is enabled. |
| 1436 | @ <ul> |
| 1437 | @ <li> <b>branch/</b><i>branch-name</i> |
| 1438 | @ <li> <b>checkin/</b><i>full-check-in-hash</i> |
| 1439 | @ <li> <b>tag/</b><i>tag-name</i> |
| 1440 | @ <li> <b>ticket/</b><i>full-ticket-hash</i> |
| 1441 | @ </ul> |
| 1442 | @ (Property: "wiki-about")</p> |
| 1443 | @ <hr> |
| 1444 | entry_attribute("Allow Unsafe HTML In Markdown", 6, |
| 1445 | "safe-html", "safe-html", "", 0); |
| @@ -2137,11 +2134,11 @@ | |
| 2134 | style_header("Admin Log"); |
| 2135 | style_submenu_element("Log-Menu", "setup-logmenu"); |
| 2136 | create_admin_log_table(); |
| 2137 | limit = atoi(PD("n","200")); |
| 2138 | ofst = atoi(PD("x","0")); |
| 2139 | fLogEnabled = db_get_boolean("admin-log", 1); |
| 2140 | @ <div>Admin logging is %s(fLogEnabled?"on":"off"). |
| 2141 | @ (Change this on the <a href="setup_settings">settings</a> page.)</div> |
| 2142 | |
| 2143 | if( ofst>0 ){ |
| 2144 | int prevx = ofst - limit; |
| @@ -2251,10 +2248,12 @@ | |
| 2248 | onoff_attribute("Search Wiki", "search-wiki", "sw", 0, 0); |
| 2249 | @ <br> |
| 2250 | onoff_attribute("Search Tech Notes", "search-technote", "se", 0, 0); |
| 2251 | @ <br> |
| 2252 | onoff_attribute("Search Forum", "search-forum", "sf", 0, 0); |
| 2253 | @ <br> |
| 2254 | onoff_attribute("Search Built-in Help Text", "search-help", "sh", 0, 0); |
| 2255 | @ <hr> |
| 2256 | @ <p><input type="submit" name="submit" value="Apply Changes"></p> |
| 2257 | @ <hr> |
| 2258 | if( P("fts0") ){ |
| 2259 | search_drop_index(); |
| 2260 |
+124
-20
| --- src/setupuser.c | ||
| +++ src/setupuser.c | ||
| @@ -302,10 +302,101 @@ | ||
| 302 | 302 | if( zPw==0 ) return 0; |
| 303 | 303 | if( zPw[0]==0 ) return 1; |
| 304 | 304 | while( zPw[0]=='*' ){ zPw++; } |
| 305 | 305 | return zPw[0]!=0; |
| 306 | 306 | } |
| 307 | + | |
| 308 | +/* | |
| 309 | +** Return true if user capability string zNew contains any capability | |
| 310 | +** letter which is not in user capability string zOrig, else 0. This | |
| 311 | +** does not take inherited permissions into account. Either argument | |
| 312 | +** may be NULL. | |
| 313 | +*/ | |
| 314 | +static int userHasNewCaps(const char *zOrig, const char *zNew){ | |
| 315 | + for( ; zNew && *zNew; ++zNew ){ | |
| 316 | + if( !zOrig || strchr(zOrig,*zNew)==0 ){ | |
| 317 | + return *zNew; | |
| 318 | + } | |
| 319 | + } | |
| 320 | + return 0; | |
| 321 | +} | |
| 322 | + | |
| 323 | +/* | |
| 324 | +** Sends notification of user permission elevation changes to all | |
| 325 | +** subscribers with a "u" subscription. This is a no-op if alerts are | |
| 326 | +** not enabled. | |
| 327 | +** | |
| 328 | +** These subscriptions differ from most, in that: | |
| 329 | +** | |
| 330 | +** - They currently lack an "unsubscribe" link. | |
| 331 | +** | |
| 332 | +** - Only an admin can assign this subscription, but if a non-admin | |
| 333 | +** edits their subscriptions after an admin assigns them this one, | |
| 334 | +** this particular one will be lost. "Feature or bug?" is unclear, | |
| 335 | +** but it would be odd for a non-admin to be assigned this | |
| 336 | +** capability. | |
| 337 | +*/ | |
| 338 | +static void alert_user_elevation(const char *zLogin, /*Affected user*/ | |
| 339 | + int uid, /*[user].uid*/ | |
| 340 | + int bIsNew, /*true if new user*/ | |
| 341 | + const char *zOrigCaps,/*Old caps*/ | |
| 342 | + const char *zNewCaps /*New caps*/){ | |
| 343 | + Blob hdr, body; | |
| 344 | + Stmt q; | |
| 345 | + int nBody; | |
| 346 | + AlertSender *pSender; | |
| 347 | + char *zSubname; | |
| 348 | + char *zURL; | |
| 349 | + char * zSubject; | |
| 350 | + | |
| 351 | + if( !alert_enabled() ) return; | |
| 352 | + zSubject = bIsNew | |
| 353 | + ? mprintf("New user created: [%q]", zLogin) | |
| 354 | + : mprintf("User [%q] permissions elevated", zLogin); | |
| 355 | + zURL = db_get("email-url",0); | |
| 356 | + zSubname = db_get("email-subname", "[Fossil Repo]"); | |
| 357 | + blob_init(&body, 0, 0); | |
| 358 | + blob_init(&hdr, 0, 0); | |
| 359 | + if( bIsNew ){ | |
| 360 | + blob_appendf(&body, "User [%q] was created by with " | |
| 361 | + "permissions [%q] by user [%q].\n", | |
| 362 | + zLogin, zNewCaps, g.zLogin); | |
| 363 | + } else { | |
| 364 | + blob_appendf(&body, "Permissions for user [%q] where elevated " | |
| 365 | + "from [%q] to [%q] by user [%q].\n", | |
| 366 | + zLogin, zOrigCaps, zNewCaps, g.zLogin); | |
| 367 | + } | |
| 368 | + if( zURL ){ | |
| 369 | + blob_appendf(&body, "\nUser editor: %s/setup_uedit?uid=%d\n", zURL, uid); | |
| 370 | + } | |
| 371 | + nBody = blob_size(&body); | |
| 372 | + pSender = alert_sender_new(0, 0); | |
| 373 | + db_prepare(&q, | |
| 374 | + "SELECT semail, hex(subscriberCode)" | |
| 375 | + " FROM subscriber, user " | |
| 376 | + " WHERE sverified AND NOT sdonotcall" | |
| 377 | + " AND suname=login" | |
| 378 | + " AND ssub GLOB '*u*'"); | |
| 379 | + while( !pSender->zErr && db_step(&q)==SQLITE_ROW ){ | |
| 380 | + const char *zTo = db_column_text(&q, 0); | |
| 381 | + blob_truncate(&hdr, 0); | |
| 382 | + blob_appendf(&hdr, "To: <%s>\r\nSubject: %s %s\r\n", | |
| 383 | + zTo, zSubname, zSubject); | |
| 384 | + if( zURL ){ | |
| 385 | + const char *zCode = db_column_text(&q, 1); | |
| 386 | + blob_truncate(&body, nBody); | |
| 387 | + blob_appendf(&body,"\n-- \nSubscription info: %s/alerts/%s\n", | |
| 388 | + zURL, zCode); | |
| 389 | + } | |
| 390 | + alert_send(pSender, &hdr, &body, 0); | |
| 391 | + } | |
| 392 | + db_finalize(&q); | |
| 393 | + alert_sender_free(pSender); | |
| 394 | + fossil_free(zURL); | |
| 395 | + fossil_free(zSubname); | |
| 396 | + fossil_free(zSubject); | |
| 397 | +} | |
| 307 | 398 | |
| 308 | 399 | /* |
| 309 | 400 | ** WEBPAGE: setup_uedit |
| 310 | 401 | ** |
| 311 | 402 | ** Edit information about a user or create a new user. |
| @@ -314,10 +405,11 @@ | ||
| 314 | 405 | void user_edit(void){ |
| 315 | 406 | const char *zId, *zLogin, *zInfo, *zCap, *zPw; |
| 316 | 407 | const char *zGroup; |
| 317 | 408 | const char *zOldLogin; |
| 318 | 409 | int uid, i; |
| 410 | + char *zOldCaps = 0; /* Capabilities before edit */ | |
| 319 | 411 | char *zDeleteVerify = 0; /* Delete user verification text */ |
| 320 | 412 | int higherUser = 0; /* True if user being edited is SETUP and the */ |
| 321 | 413 | /* user doing the editing is ADMIN. Disallow editing */ |
| 322 | 414 | const char *inherit[128]; |
| 323 | 415 | int a[128]; |
| @@ -331,14 +423,15 @@ | ||
| 331 | 423 | /* Check to see if an ADMIN user is trying to edit a SETUP account. |
| 332 | 424 | ** Don't allow that. |
| 333 | 425 | */ |
| 334 | 426 | zId = PD("id", "0"); |
| 335 | 427 | uid = atoi(zId); |
| 336 | - if( zId && !g.perm.Setup && uid>0 ){ | |
| 337 | - char *zOldCaps; | |
| 338 | - zOldCaps = db_text(0, "SELECT cap FROM user WHERE uid=%d",uid); | |
| 339 | - higherUser = zOldCaps && strchr(zOldCaps,'s'); | |
| 428 | + if( uid>0 ){ | |
| 429 | + zOldCaps = db_text("", "SELECT cap FROM user WHERE uid=%d",uid); | |
| 430 | + if( zId && !g.perm.Setup ){ | |
| 431 | + higherUser = zOldCaps && strchr(zOldCaps,'s'); | |
| 432 | + } | |
| 340 | 433 | } |
| 341 | 434 | |
| 342 | 435 | if( P("can") ){ |
| 343 | 436 | /* User pressed the cancel button */ |
| 344 | 437 | cgi_redirect(cgi_referer("setup_ulist")); |
| @@ -393,30 +486,33 @@ | ||
| 393 | 486 | }else if( !cgi_csrf_safe(2) ){ |
| 394 | 487 | /* This might be a cross-site request forgery, so ignore it */ |
| 395 | 488 | }else{ |
| 396 | 489 | /* We have all the information we need to make the change to the user */ |
| 397 | 490 | char c; |
| 398 | - char zCap[70], zNm[4]; | |
| 491 | + int bHasNewCaps = 0 /* 1 if user's permissions are increased */; | |
| 492 | + const int bIsNew = uid<=0; | |
| 493 | + char aCap[70], zNm[4]; | |
| 399 | 494 | zNm[0] = 'a'; |
| 400 | 495 | zNm[2] = 0; |
| 401 | 496 | for(i=0, c='a'; c<='z'; c++){ |
| 402 | 497 | zNm[1] = c; |
| 403 | 498 | a[c&0x7f] = ((c!='s' && c!='y') || g.perm.Setup) && P(zNm)!=0; |
| 404 | - if( a[c&0x7f] ) zCap[i++] = c; | |
| 499 | + if( a[c&0x7f] ) aCap[i++] = c; | |
| 405 | 500 | } |
| 406 | 501 | for(c='0'; c<='9'; c++){ |
| 407 | 502 | zNm[1] = c; |
| 408 | 503 | a[c&0x7f] = P(zNm)!=0; |
| 409 | - if( a[c&0x7f] ) zCap[i++] = c; | |
| 504 | + if( a[c&0x7f] ) aCap[i++] = c; | |
| 410 | 505 | } |
| 411 | 506 | for(c='A'; c<='Z'; c++){ |
| 412 | 507 | zNm[1] = c; |
| 413 | 508 | a[c&0x7f] = P(zNm)!=0; |
| 414 | - if( a[c&0x7f] ) zCap[i++] = c; | |
| 509 | + if( a[c&0x7f] ) aCap[i++] = c; | |
| 415 | 510 | } |
| 416 | 511 | |
| 417 | - zCap[i] = 0; | |
| 512 | + aCap[i] = 0; | |
| 513 | + bHasNewCaps = bIsNew || userHasNewCaps(zOldCaps, &aCap[0]); | |
| 418 | 514 | zPw = P("pw"); |
| 419 | 515 | zLogin = P("login"); |
| 420 | 516 | if( strlen(zLogin)==0 ){ |
| 421 | 517 | const char *zRef = cgi_referer("setup_ulist"); |
| 422 | 518 | style_header("User Creation Error"); |
| @@ -444,15 +540,16 @@ | ||
| 444 | 540 | style_finish_page(); |
| 445 | 541 | return; |
| 446 | 542 | } |
| 447 | 543 | cgi_csrf_verify(); |
| 448 | 544 | db_unprotect(PROTECT_USER); |
| 449 | - db_multi_exec( | |
| 450 | - "REPLACE INTO user(uid,login,info,pw,cap,mtime) " | |
| 451 | - "VALUES(nullif(%d,0),%Q,%Q,%Q,%Q,now())", | |
| 452 | - uid, zLogin, P("info"), zPw, zCap | |
| 453 | - ); | |
| 545 | + uid = db_int(0, | |
| 546 | + "REPLACE INTO user(uid,login,info,pw,cap,mtime) " | |
| 547 | + "VALUES(nullif(%d,0),%Q,%Q,%Q,%Q,now()) " | |
| 548 | + "RETURNING uid", | |
| 549 | + uid, zLogin, P("info"), zPw, &aCap[0]); | |
| 550 | + assert( uid>0 ); | |
| 454 | 551 | if( zOldLogin && fossil_strcmp(zLogin, zOldLogin)!=0 ){ |
| 455 | 552 | if( alert_tables_exist() ){ |
| 456 | 553 | /* Rename matching subscriber entry, else the user cannot |
| 457 | 554 | re-subscribe with their same email address. */ |
| 458 | 555 | db_multi_exec("UPDATE subscriber SET suname=%Q WHERE suname=%Q", |
| @@ -460,12 +557,13 @@ | ||
| 460 | 557 | } |
| 461 | 558 | admin_log( "Renamed user [%q] to [%q].", zOldLogin, zLogin ); |
| 462 | 559 | } |
| 463 | 560 | db_protect_pop(); |
| 464 | 561 | setup_incr_cfgcnt(); |
| 465 | - admin_log( "Updated user [%q] with capabilities [%q].", | |
| 466 | - zLogin, zCap ); | |
| 562 | + admin_log( "%s user [%q] with capabilities [%q].", | |
| 563 | + bIsNew ? "Added" : "Updated", | |
| 564 | + zLogin, &aCap[0] ); | |
| 467 | 565 | if( atoi(PD("all","0"))>0 ){ |
| 468 | 566 | Blob sql; |
| 469 | 567 | char *zErr = 0; |
| 470 | 568 | blob_zero(&sql); |
| 471 | 569 | if( zOldLogin==0 ){ |
| @@ -496,49 +594,55 @@ | ||
| 496 | 594 | "(SELECT value FROM config WHERE name='project-code')),pw)," |
| 497 | 595 | " info=%Q," |
| 498 | 596 | " cap=%Q," |
| 499 | 597 | " mtime=now()" |
| 500 | 598 | " WHERE login=%Q;", |
| 501 | - zLogin, P("pw"), zLogin, P("info"), zCap, | |
| 599 | + zLogin, P("pw"), zLogin, P("info"), &aCap[0], | |
| 502 | 600 | zOldLogin |
| 503 | 601 | ); |
| 504 | 602 | db_unprotect(PROTECT_USER); |
| 505 | 603 | login_group_sql(blob_str(&sql), "<li> ", " </li>\n", &zErr); |
| 506 | 604 | db_protect_pop(); |
| 507 | 605 | blob_reset(&sql); |
| 508 | 606 | admin_log( "Updated user [%q] in all login groups " |
| 509 | 607 | "with capabilities [%q].", |
| 510 | - zLogin, zCap ); | |
| 608 | + zLogin, &aCap[0] ); | |
| 511 | 609 | if( zErr ){ |
| 512 | 610 | const char *zRef = cgi_referer("setup_ulist"); |
| 513 | 611 | style_header("User Change Error"); |
| 514 | 612 | admin_log( "Error updating user '%q': %s'.", zLogin, zErr ); |
| 515 | 613 | @ <span class="loginError">%h(zErr)</span> |
| 516 | 614 | @ |
| 517 | 615 | @ <p><a href="setup_uedit?id=%d(uid)&referer=%T(zRef)"> |
| 518 | 616 | @ [Bummer]</a></p> |
| 519 | 617 | style_finish_page(); |
| 618 | + if( bHasNewCaps ){ | |
| 619 | + alert_user_elevation(zLogin, uid, bIsNew, zOldCaps, &aCap[0]); | |
| 620 | + } | |
| 520 | 621 | return; |
| 521 | 622 | } |
| 522 | 623 | } |
| 624 | + if( bHasNewCaps ){ | |
| 625 | + alert_user_elevation(zLogin, uid, bIsNew, zOldCaps, &aCap[0]); | |
| 626 | + } | |
| 523 | 627 | cgi_redirect(cgi_referer("setup_ulist")); |
| 524 | 628 | return; |
| 525 | 629 | } |
| 526 | 630 | |
| 527 | 631 | /* Load the existing information about the user, if any |
| 528 | 632 | */ |
| 529 | 633 | zLogin = ""; |
| 530 | 634 | zInfo = ""; |
| 531 | - zCap = ""; | |
| 635 | + zCap = zOldCaps; | |
| 532 | 636 | zPw = ""; |
| 533 | 637 | for(i='a'; i<='z'; i++) oa[i] = ""; |
| 534 | 638 | for(i='0'; i<='9'; i++) oa[i] = ""; |
| 535 | 639 | for(i='A'; i<='Z'; i++) oa[i] = ""; |
| 536 | 640 | if( uid ){ |
| 641 | + assert( zCap ); | |
| 537 | 642 | zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid); |
| 538 | 643 | zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid); |
| 539 | - zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid); | |
| 540 | 644 | zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid); |
| 541 | 645 | for(i=0; zCap[i]; i++){ |
| 542 | 646 | char c = zCap[i]; |
| 543 | 647 | if( (c>='a' && c<='z') || (c>='0' && c<='9') || (c>='A' && c<='Z') ){ |
| 544 | 648 | oa[c&0x7f] = " checked=\"checked\""; |
| 545 | 649 |
| --- src/setupuser.c | |
| +++ src/setupuser.c | |
| @@ -302,10 +302,101 @@ | |
| 302 | if( zPw==0 ) return 0; |
| 303 | if( zPw[0]==0 ) return 1; |
| 304 | while( zPw[0]=='*' ){ zPw++; } |
| 305 | return zPw[0]!=0; |
| 306 | } |
| 307 | |
| 308 | /* |
| 309 | ** WEBPAGE: setup_uedit |
| 310 | ** |
| 311 | ** Edit information about a user or create a new user. |
| @@ -314,10 +405,11 @@ | |
| 314 | void user_edit(void){ |
| 315 | const char *zId, *zLogin, *zInfo, *zCap, *zPw; |
| 316 | const char *zGroup; |
| 317 | const char *zOldLogin; |
| 318 | int uid, i; |
| 319 | char *zDeleteVerify = 0; /* Delete user verification text */ |
| 320 | int higherUser = 0; /* True if user being edited is SETUP and the */ |
| 321 | /* user doing the editing is ADMIN. Disallow editing */ |
| 322 | const char *inherit[128]; |
| 323 | int a[128]; |
| @@ -331,14 +423,15 @@ | |
| 331 | /* Check to see if an ADMIN user is trying to edit a SETUP account. |
| 332 | ** Don't allow that. |
| 333 | */ |
| 334 | zId = PD("id", "0"); |
| 335 | uid = atoi(zId); |
| 336 | if( zId && !g.perm.Setup && uid>0 ){ |
| 337 | char *zOldCaps; |
| 338 | zOldCaps = db_text(0, "SELECT cap FROM user WHERE uid=%d",uid); |
| 339 | higherUser = zOldCaps && strchr(zOldCaps,'s'); |
| 340 | } |
| 341 | |
| 342 | if( P("can") ){ |
| 343 | /* User pressed the cancel button */ |
| 344 | cgi_redirect(cgi_referer("setup_ulist")); |
| @@ -393,30 +486,33 @@ | |
| 393 | }else if( !cgi_csrf_safe(2) ){ |
| 394 | /* This might be a cross-site request forgery, so ignore it */ |
| 395 | }else{ |
| 396 | /* We have all the information we need to make the change to the user */ |
| 397 | char c; |
| 398 | char zCap[70], zNm[4]; |
| 399 | zNm[0] = 'a'; |
| 400 | zNm[2] = 0; |
| 401 | for(i=0, c='a'; c<='z'; c++){ |
| 402 | zNm[1] = c; |
| 403 | a[c&0x7f] = ((c!='s' && c!='y') || g.perm.Setup) && P(zNm)!=0; |
| 404 | if( a[c&0x7f] ) zCap[i++] = c; |
| 405 | } |
| 406 | for(c='0'; c<='9'; c++){ |
| 407 | zNm[1] = c; |
| 408 | a[c&0x7f] = P(zNm)!=0; |
| 409 | if( a[c&0x7f] ) zCap[i++] = c; |
| 410 | } |
| 411 | for(c='A'; c<='Z'; c++){ |
| 412 | zNm[1] = c; |
| 413 | a[c&0x7f] = P(zNm)!=0; |
| 414 | if( a[c&0x7f] ) zCap[i++] = c; |
| 415 | } |
| 416 | |
| 417 | zCap[i] = 0; |
| 418 | zPw = P("pw"); |
| 419 | zLogin = P("login"); |
| 420 | if( strlen(zLogin)==0 ){ |
| 421 | const char *zRef = cgi_referer("setup_ulist"); |
| 422 | style_header("User Creation Error"); |
| @@ -444,15 +540,16 @@ | |
| 444 | style_finish_page(); |
| 445 | return; |
| 446 | } |
| 447 | cgi_csrf_verify(); |
| 448 | db_unprotect(PROTECT_USER); |
| 449 | db_multi_exec( |
| 450 | "REPLACE INTO user(uid,login,info,pw,cap,mtime) " |
| 451 | "VALUES(nullif(%d,0),%Q,%Q,%Q,%Q,now())", |
| 452 | uid, zLogin, P("info"), zPw, zCap |
| 453 | ); |
| 454 | if( zOldLogin && fossil_strcmp(zLogin, zOldLogin)!=0 ){ |
| 455 | if( alert_tables_exist() ){ |
| 456 | /* Rename matching subscriber entry, else the user cannot |
| 457 | re-subscribe with their same email address. */ |
| 458 | db_multi_exec("UPDATE subscriber SET suname=%Q WHERE suname=%Q", |
| @@ -460,12 +557,13 @@ | |
| 460 | } |
| 461 | admin_log( "Renamed user [%q] to [%q].", zOldLogin, zLogin ); |
| 462 | } |
| 463 | db_protect_pop(); |
| 464 | setup_incr_cfgcnt(); |
| 465 | admin_log( "Updated user [%q] with capabilities [%q].", |
| 466 | zLogin, zCap ); |
| 467 | if( atoi(PD("all","0"))>0 ){ |
| 468 | Blob sql; |
| 469 | char *zErr = 0; |
| 470 | blob_zero(&sql); |
| 471 | if( zOldLogin==0 ){ |
| @@ -496,49 +594,55 @@ | |
| 496 | "(SELECT value FROM config WHERE name='project-code')),pw)," |
| 497 | " info=%Q," |
| 498 | " cap=%Q," |
| 499 | " mtime=now()" |
| 500 | " WHERE login=%Q;", |
| 501 | zLogin, P("pw"), zLogin, P("info"), zCap, |
| 502 | zOldLogin |
| 503 | ); |
| 504 | db_unprotect(PROTECT_USER); |
| 505 | login_group_sql(blob_str(&sql), "<li> ", " </li>\n", &zErr); |
| 506 | db_protect_pop(); |
| 507 | blob_reset(&sql); |
| 508 | admin_log( "Updated user [%q] in all login groups " |
| 509 | "with capabilities [%q].", |
| 510 | zLogin, zCap ); |
| 511 | if( zErr ){ |
| 512 | const char *zRef = cgi_referer("setup_ulist"); |
| 513 | style_header("User Change Error"); |
| 514 | admin_log( "Error updating user '%q': %s'.", zLogin, zErr ); |
| 515 | @ <span class="loginError">%h(zErr)</span> |
| 516 | @ |
| 517 | @ <p><a href="setup_uedit?id=%d(uid)&referer=%T(zRef)"> |
| 518 | @ [Bummer]</a></p> |
| 519 | style_finish_page(); |
| 520 | return; |
| 521 | } |
| 522 | } |
| 523 | cgi_redirect(cgi_referer("setup_ulist")); |
| 524 | return; |
| 525 | } |
| 526 | |
| 527 | /* Load the existing information about the user, if any |
| 528 | */ |
| 529 | zLogin = ""; |
| 530 | zInfo = ""; |
| 531 | zCap = ""; |
| 532 | zPw = ""; |
| 533 | for(i='a'; i<='z'; i++) oa[i] = ""; |
| 534 | for(i='0'; i<='9'; i++) oa[i] = ""; |
| 535 | for(i='A'; i<='Z'; i++) oa[i] = ""; |
| 536 | if( uid ){ |
| 537 | zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid); |
| 538 | zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid); |
| 539 | zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid); |
| 540 | zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid); |
| 541 | for(i=0; zCap[i]; i++){ |
| 542 | char c = zCap[i]; |
| 543 | if( (c>='a' && c<='z') || (c>='0' && c<='9') || (c>='A' && c<='Z') ){ |
| 544 | oa[c&0x7f] = " checked=\"checked\""; |
| 545 |
| --- src/setupuser.c | |
| +++ src/setupuser.c | |
| @@ -302,10 +302,101 @@ | |
| 302 | if( zPw==0 ) return 0; |
| 303 | if( zPw[0]==0 ) return 1; |
| 304 | while( zPw[0]=='*' ){ zPw++; } |
| 305 | return zPw[0]!=0; |
| 306 | } |
| 307 | |
| 308 | /* |
| 309 | ** Return true if user capability string zNew contains any capability |
| 310 | ** letter which is not in user capability string zOrig, else 0. This |
| 311 | ** does not take inherited permissions into account. Either argument |
| 312 | ** may be NULL. |
| 313 | */ |
| 314 | static int userHasNewCaps(const char *zOrig, const char *zNew){ |
| 315 | for( ; zNew && *zNew; ++zNew ){ |
| 316 | if( !zOrig || strchr(zOrig,*zNew)==0 ){ |
| 317 | return *zNew; |
| 318 | } |
| 319 | } |
| 320 | return 0; |
| 321 | } |
| 322 | |
| 323 | /* |
| 324 | ** Sends notification of user permission elevation changes to all |
| 325 | ** subscribers with a "u" subscription. This is a no-op if alerts are |
| 326 | ** not enabled. |
| 327 | ** |
| 328 | ** These subscriptions differ from most, in that: |
| 329 | ** |
| 330 | ** - They currently lack an "unsubscribe" link. |
| 331 | ** |
| 332 | ** - Only an admin can assign this subscription, but if a non-admin |
| 333 | ** edits their subscriptions after an admin assigns them this one, |
| 334 | ** this particular one will be lost. "Feature or bug?" is unclear, |
| 335 | ** but it would be odd for a non-admin to be assigned this |
| 336 | ** capability. |
| 337 | */ |
| 338 | static void alert_user_elevation(const char *zLogin, /*Affected user*/ |
| 339 | int uid, /*[user].uid*/ |
| 340 | int bIsNew, /*true if new user*/ |
| 341 | const char *zOrigCaps,/*Old caps*/ |
| 342 | const char *zNewCaps /*New caps*/){ |
| 343 | Blob hdr, body; |
| 344 | Stmt q; |
| 345 | int nBody; |
| 346 | AlertSender *pSender; |
| 347 | char *zSubname; |
| 348 | char *zURL; |
| 349 | char * zSubject; |
| 350 | |
| 351 | if( !alert_enabled() ) return; |
| 352 | zSubject = bIsNew |
| 353 | ? mprintf("New user created: [%q]", zLogin) |
| 354 | : mprintf("User [%q] permissions elevated", zLogin); |
| 355 | zURL = db_get("email-url",0); |
| 356 | zSubname = db_get("email-subname", "[Fossil Repo]"); |
| 357 | blob_init(&body, 0, 0); |
| 358 | blob_init(&hdr, 0, 0); |
| 359 | if( bIsNew ){ |
| 360 | blob_appendf(&body, "User [%q] was created by with " |
| 361 | "permissions [%q] by user [%q].\n", |
| 362 | zLogin, zNewCaps, g.zLogin); |
| 363 | } else { |
| 364 | blob_appendf(&body, "Permissions for user [%q] where elevated " |
| 365 | "from [%q] to [%q] by user [%q].\n", |
| 366 | zLogin, zOrigCaps, zNewCaps, g.zLogin); |
| 367 | } |
| 368 | if( zURL ){ |
| 369 | blob_appendf(&body, "\nUser editor: %s/setup_uedit?uid=%d\n", zURL, uid); |
| 370 | } |
| 371 | nBody = blob_size(&body); |
| 372 | pSender = alert_sender_new(0, 0); |
| 373 | db_prepare(&q, |
| 374 | "SELECT semail, hex(subscriberCode)" |
| 375 | " FROM subscriber, user " |
| 376 | " WHERE sverified AND NOT sdonotcall" |
| 377 | " AND suname=login" |
| 378 | " AND ssub GLOB '*u*'"); |
| 379 | while( !pSender->zErr && db_step(&q)==SQLITE_ROW ){ |
| 380 | const char *zTo = db_column_text(&q, 0); |
| 381 | blob_truncate(&hdr, 0); |
| 382 | blob_appendf(&hdr, "To: <%s>\r\nSubject: %s %s\r\n", |
| 383 | zTo, zSubname, zSubject); |
| 384 | if( zURL ){ |
| 385 | const char *zCode = db_column_text(&q, 1); |
| 386 | blob_truncate(&body, nBody); |
| 387 | blob_appendf(&body,"\n-- \nSubscription info: %s/alerts/%s\n", |
| 388 | zURL, zCode); |
| 389 | } |
| 390 | alert_send(pSender, &hdr, &body, 0); |
| 391 | } |
| 392 | db_finalize(&q); |
| 393 | alert_sender_free(pSender); |
| 394 | fossil_free(zURL); |
| 395 | fossil_free(zSubname); |
| 396 | fossil_free(zSubject); |
| 397 | } |
| 398 | |
| 399 | /* |
| 400 | ** WEBPAGE: setup_uedit |
| 401 | ** |
| 402 | ** Edit information about a user or create a new user. |
| @@ -314,10 +405,11 @@ | |
| 405 | void user_edit(void){ |
| 406 | const char *zId, *zLogin, *zInfo, *zCap, *zPw; |
| 407 | const char *zGroup; |
| 408 | const char *zOldLogin; |
| 409 | int uid, i; |
| 410 | char *zOldCaps = 0; /* Capabilities before edit */ |
| 411 | char *zDeleteVerify = 0; /* Delete user verification text */ |
| 412 | int higherUser = 0; /* True if user being edited is SETUP and the */ |
| 413 | /* user doing the editing is ADMIN. Disallow editing */ |
| 414 | const char *inherit[128]; |
| 415 | int a[128]; |
| @@ -331,14 +423,15 @@ | |
| 423 | /* Check to see if an ADMIN user is trying to edit a SETUP account. |
| 424 | ** Don't allow that. |
| 425 | */ |
| 426 | zId = PD("id", "0"); |
| 427 | uid = atoi(zId); |
| 428 | if( uid>0 ){ |
| 429 | zOldCaps = db_text("", "SELECT cap FROM user WHERE uid=%d",uid); |
| 430 | if( zId && !g.perm.Setup ){ |
| 431 | higherUser = zOldCaps && strchr(zOldCaps,'s'); |
| 432 | } |
| 433 | } |
| 434 | |
| 435 | if( P("can") ){ |
| 436 | /* User pressed the cancel button */ |
| 437 | cgi_redirect(cgi_referer("setup_ulist")); |
| @@ -393,30 +486,33 @@ | |
| 486 | }else if( !cgi_csrf_safe(2) ){ |
| 487 | /* This might be a cross-site request forgery, so ignore it */ |
| 488 | }else{ |
| 489 | /* We have all the information we need to make the change to the user */ |
| 490 | char c; |
| 491 | int bHasNewCaps = 0 /* 1 if user's permissions are increased */; |
| 492 | const int bIsNew = uid<=0; |
| 493 | char aCap[70], zNm[4]; |
| 494 | zNm[0] = 'a'; |
| 495 | zNm[2] = 0; |
| 496 | for(i=0, c='a'; c<='z'; c++){ |
| 497 | zNm[1] = c; |
| 498 | a[c&0x7f] = ((c!='s' && c!='y') || g.perm.Setup) && P(zNm)!=0; |
| 499 | if( a[c&0x7f] ) aCap[i++] = c; |
| 500 | } |
| 501 | for(c='0'; c<='9'; c++){ |
| 502 | zNm[1] = c; |
| 503 | a[c&0x7f] = P(zNm)!=0; |
| 504 | if( a[c&0x7f] ) aCap[i++] = c; |
| 505 | } |
| 506 | for(c='A'; c<='Z'; c++){ |
| 507 | zNm[1] = c; |
| 508 | a[c&0x7f] = P(zNm)!=0; |
| 509 | if( a[c&0x7f] ) aCap[i++] = c; |
| 510 | } |
| 511 | |
| 512 | aCap[i] = 0; |
| 513 | bHasNewCaps = bIsNew || userHasNewCaps(zOldCaps, &aCap[0]); |
| 514 | zPw = P("pw"); |
| 515 | zLogin = P("login"); |
| 516 | if( strlen(zLogin)==0 ){ |
| 517 | const char *zRef = cgi_referer("setup_ulist"); |
| 518 | style_header("User Creation Error"); |
| @@ -444,15 +540,16 @@ | |
| 540 | style_finish_page(); |
| 541 | return; |
| 542 | } |
| 543 | cgi_csrf_verify(); |
| 544 | db_unprotect(PROTECT_USER); |
| 545 | uid = db_int(0, |
| 546 | "REPLACE INTO user(uid,login,info,pw,cap,mtime) " |
| 547 | "VALUES(nullif(%d,0),%Q,%Q,%Q,%Q,now()) " |
| 548 | "RETURNING uid", |
| 549 | uid, zLogin, P("info"), zPw, &aCap[0]); |
| 550 | assert( uid>0 ); |
| 551 | if( zOldLogin && fossil_strcmp(zLogin, zOldLogin)!=0 ){ |
| 552 | if( alert_tables_exist() ){ |
| 553 | /* Rename matching subscriber entry, else the user cannot |
| 554 | re-subscribe with their same email address. */ |
| 555 | db_multi_exec("UPDATE subscriber SET suname=%Q WHERE suname=%Q", |
| @@ -460,12 +557,13 @@ | |
| 557 | } |
| 558 | admin_log( "Renamed user [%q] to [%q].", zOldLogin, zLogin ); |
| 559 | } |
| 560 | db_protect_pop(); |
| 561 | setup_incr_cfgcnt(); |
| 562 | admin_log( "%s user [%q] with capabilities [%q].", |
| 563 | bIsNew ? "Added" : "Updated", |
| 564 | zLogin, &aCap[0] ); |
| 565 | if( atoi(PD("all","0"))>0 ){ |
| 566 | Blob sql; |
| 567 | char *zErr = 0; |
| 568 | blob_zero(&sql); |
| 569 | if( zOldLogin==0 ){ |
| @@ -496,49 +594,55 @@ | |
| 594 | "(SELECT value FROM config WHERE name='project-code')),pw)," |
| 595 | " info=%Q," |
| 596 | " cap=%Q," |
| 597 | " mtime=now()" |
| 598 | " WHERE login=%Q;", |
| 599 | zLogin, P("pw"), zLogin, P("info"), &aCap[0], |
| 600 | zOldLogin |
| 601 | ); |
| 602 | db_unprotect(PROTECT_USER); |
| 603 | login_group_sql(blob_str(&sql), "<li> ", " </li>\n", &zErr); |
| 604 | db_protect_pop(); |
| 605 | blob_reset(&sql); |
| 606 | admin_log( "Updated user [%q] in all login groups " |
| 607 | "with capabilities [%q].", |
| 608 | zLogin, &aCap[0] ); |
| 609 | if( zErr ){ |
| 610 | const char *zRef = cgi_referer("setup_ulist"); |
| 611 | style_header("User Change Error"); |
| 612 | admin_log( "Error updating user '%q': %s'.", zLogin, zErr ); |
| 613 | @ <span class="loginError">%h(zErr)</span> |
| 614 | @ |
| 615 | @ <p><a href="setup_uedit?id=%d(uid)&referer=%T(zRef)"> |
| 616 | @ [Bummer]</a></p> |
| 617 | style_finish_page(); |
| 618 | if( bHasNewCaps ){ |
| 619 | alert_user_elevation(zLogin, uid, bIsNew, zOldCaps, &aCap[0]); |
| 620 | } |
| 621 | return; |
| 622 | } |
| 623 | } |
| 624 | if( bHasNewCaps ){ |
| 625 | alert_user_elevation(zLogin, uid, bIsNew, zOldCaps, &aCap[0]); |
| 626 | } |
| 627 | cgi_redirect(cgi_referer("setup_ulist")); |
| 628 | return; |
| 629 | } |
| 630 | |
| 631 | /* Load the existing information about the user, if any |
| 632 | */ |
| 633 | zLogin = ""; |
| 634 | zInfo = ""; |
| 635 | zCap = zOldCaps; |
| 636 | zPw = ""; |
| 637 | for(i='a'; i<='z'; i++) oa[i] = ""; |
| 638 | for(i='0'; i<='9'; i++) oa[i] = ""; |
| 639 | for(i='A'; i<='Z'; i++) oa[i] = ""; |
| 640 | if( uid ){ |
| 641 | assert( zCap ); |
| 642 | zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid); |
| 643 | zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid); |
| 644 | zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid); |
| 645 | for(i=0; zCap[i]; i++){ |
| 646 | char c = zCap[i]; |
| 647 | if( (c>='a' && c<='z') || (c>='0' && c<='9') || (c>='A' && c<='Z') ){ |
| 648 | oa[c&0x7f] = " checked=\"checked\""; |
| 649 |
+1
| --- src/sitemap.c | ||
| +++ src/sitemap.c | ||
| @@ -293,10 +293,11 @@ | ||
| 293 | 293 | @ <li>%z(href("%R/test-rename-list"))List of file renames</a></li> |
| 294 | 294 | } |
| 295 | 295 | @ <li>%z(href("%R/test-builtin-files"))List of built-in files</a></li> |
| 296 | 296 | @ <li>%z(href("%R/mimetype_list"))List of MIME types</a></li> |
| 297 | 297 | @ <li>%z(href("%R/hash-color-test"))Hash color test</a> |
| 298 | + @ <li>%z(href("%R/test-bgcolor"))Background color test</a> | |
| 298 | 299 | if( g.perm.Admin ){ |
| 299 | 300 | @ <li>%z(href("%R/test-backlinks"))List of backlinks</a></li> |
| 300 | 301 | @ <li>%z(href("%R/test-backlink-timeline"))Backlink timeline</a></li> |
| 301 | 302 | @ <li>%z(href("%R/phantoms"))List of phantom artifacts</a></li> |
| 302 | 303 | @ <li>%z(href("%R/test-warning"))Error Log test page</a></li> |
| 303 | 304 |
| --- src/sitemap.c | |
| +++ src/sitemap.c | |
| @@ -293,10 +293,11 @@ | |
| 293 | @ <li>%z(href("%R/test-rename-list"))List of file renames</a></li> |
| 294 | } |
| 295 | @ <li>%z(href("%R/test-builtin-files"))List of built-in files</a></li> |
| 296 | @ <li>%z(href("%R/mimetype_list"))List of MIME types</a></li> |
| 297 | @ <li>%z(href("%R/hash-color-test"))Hash color test</a> |
| 298 | if( g.perm.Admin ){ |
| 299 | @ <li>%z(href("%R/test-backlinks"))List of backlinks</a></li> |
| 300 | @ <li>%z(href("%R/test-backlink-timeline"))Backlink timeline</a></li> |
| 301 | @ <li>%z(href("%R/phantoms"))List of phantom artifacts</a></li> |
| 302 | @ <li>%z(href("%R/test-warning"))Error Log test page</a></li> |
| 303 |
| --- src/sitemap.c | |
| +++ src/sitemap.c | |
| @@ -293,10 +293,11 @@ | |
| 293 | @ <li>%z(href("%R/test-rename-list"))List of file renames</a></li> |
| 294 | } |
| 295 | @ <li>%z(href("%R/test-builtin-files"))List of built-in files</a></li> |
| 296 | @ <li>%z(href("%R/mimetype_list"))List of MIME types</a></li> |
| 297 | @ <li>%z(href("%R/hash-color-test"))Hash color test</a> |
| 298 | @ <li>%z(href("%R/test-bgcolor"))Background color test</a> |
| 299 | if( g.perm.Admin ){ |
| 300 | @ <li>%z(href("%R/test-backlinks"))List of backlinks</a></li> |
| 301 | @ <li>%z(href("%R/test-backlink-timeline"))Backlink timeline</a></li> |
| 302 | @ <li>%z(href("%R/phantoms"))List of phantom artifacts</a></li> |
| 303 | @ <li>%z(href("%R/test-warning"))Error Log test page</a></li> |
| 304 |
+1
| --- src/sqlcmd.c | ||
| +++ src/sqlcmd.c | ||
| @@ -235,10 +235,11 @@ | ||
| 235 | 235 | char *zSql = sqlite3_mprintf("ATTACH %Q AS 'configdb' KEY ''", |
| 236 | 236 | g.zConfigDbName); |
| 237 | 237 | sqlite3_exec(db, zSql, 0, 0, 0); |
| 238 | 238 | sqlite3_free(zSql); |
| 239 | 239 | } |
| 240 | + (void)timeline_query_for_tty(); /* Registers wiki_to_text() as side-effect */ | |
| 240 | 241 | /* Arrange to trace close operations so that static prepared statements |
| 241 | 242 | ** will get cleaned up when the shell closes the database connection */ |
| 242 | 243 | if( g.fSqlTrace ) mTrace |= SQLITE_TRACE_PROFILE; |
| 243 | 244 | sqlite3_trace_v2(db, mTrace, db_sql_trace, 0); |
| 244 | 245 | db_protect_only(PROTECT_NONE); |
| 245 | 246 |
| --- src/sqlcmd.c | |
| +++ src/sqlcmd.c | |
| @@ -235,10 +235,11 @@ | |
| 235 | char *zSql = sqlite3_mprintf("ATTACH %Q AS 'configdb' KEY ''", |
| 236 | g.zConfigDbName); |
| 237 | sqlite3_exec(db, zSql, 0, 0, 0); |
| 238 | sqlite3_free(zSql); |
| 239 | } |
| 240 | /* Arrange to trace close operations so that static prepared statements |
| 241 | ** will get cleaned up when the shell closes the database connection */ |
| 242 | if( g.fSqlTrace ) mTrace |= SQLITE_TRACE_PROFILE; |
| 243 | sqlite3_trace_v2(db, mTrace, db_sql_trace, 0); |
| 244 | db_protect_only(PROTECT_NONE); |
| 245 |
| --- src/sqlcmd.c | |
| +++ src/sqlcmd.c | |
| @@ -235,10 +235,11 @@ | |
| 235 | char *zSql = sqlite3_mprintf("ATTACH %Q AS 'configdb' KEY ''", |
| 236 | g.zConfigDbName); |
| 237 | sqlite3_exec(db, zSql, 0, 0, 0); |
| 238 | sqlite3_free(zSql); |
| 239 | } |
| 240 | (void)timeline_query_for_tty(); /* Registers wiki_to_text() as side-effect */ |
| 241 | /* Arrange to trace close operations so that static prepared statements |
| 242 | ** will get cleaned up when the shell closes the database connection */ |
| 243 | if( g.fSqlTrace ) mTrace |= SQLITE_TRACE_PROFILE; |
| 244 | sqlite3_trace_v2(db, mTrace, db_sql_trace, 0); |
| 245 | db_protect_only(PROTECT_NONE); |
| 246 |
+1
-1
| --- src/stash.c | ||
| +++ src/stash.c | ||
| @@ -756,11 +756,11 @@ | ||
| 756 | 756 | DiffConfig DCfg; |
| 757 | 757 | |
| 758 | 758 | if( strstr(zCmd,"show")!=0 || strstr(zCmd,"cat")!=0 ){ |
| 759 | 759 | fBaseline = 1; |
| 760 | 760 | } |
| 761 | - if( find_option("tk",0,0)!=0 ){ | |
| 761 | + if( find_option("tk",0,0)!=0 || gdiff_using_tk(zCmd[0]=='g') ){ | |
| 762 | 762 | db_close(0); |
| 763 | 763 | diff_tk(fBaseline ? "stash show" : "stash diff", 3); |
| 764 | 764 | return; |
| 765 | 765 | } |
| 766 | 766 | diff_options(&DCfg, zCmd[0]=='g', 0); |
| 767 | 767 |
| --- src/stash.c | |
| +++ src/stash.c | |
| @@ -756,11 +756,11 @@ | |
| 756 | DiffConfig DCfg; |
| 757 | |
| 758 | if( strstr(zCmd,"show")!=0 || strstr(zCmd,"cat")!=0 ){ |
| 759 | fBaseline = 1; |
| 760 | } |
| 761 | if( find_option("tk",0,0)!=0 ){ |
| 762 | db_close(0); |
| 763 | diff_tk(fBaseline ? "stash show" : "stash diff", 3); |
| 764 | return; |
| 765 | } |
| 766 | diff_options(&DCfg, zCmd[0]=='g', 0); |
| 767 |
| --- src/stash.c | |
| +++ src/stash.c | |
| @@ -756,11 +756,11 @@ | |
| 756 | DiffConfig DCfg; |
| 757 | |
| 758 | if( strstr(zCmd,"show")!=0 || strstr(zCmd,"cat")!=0 ){ |
| 759 | fBaseline = 1; |
| 760 | } |
| 761 | if( find_option("tk",0,0)!=0 || gdiff_using_tk(zCmd[0]=='g') ){ |
| 762 | db_close(0); |
| 763 | diff_tk(fBaseline ? "stash show" : "stash diff", 3); |
| 764 | return; |
| 765 | } |
| 766 | diff_options(&DCfg, zCmd[0]=='g', 0); |
| 767 |
+5
-3
| --- src/statrep.c | ||
| +++ src/statrep.c | ||
| @@ -318,15 +318,16 @@ | ||
| 318 | 318 | && nMaxEvents>0 |
| 319 | 319 | ){ |
| 320 | 320 | /* If the timespan covered by this row contains "now", then project |
| 321 | 321 | ** the number of changes until the completion of the timespan and |
| 322 | 322 | ** show a dashed box of that projection. */ |
| 323 | + int nProj = (int)(((double)nCount)/rNowFraction); | |
| 323 | 324 | int nExtra = (int)(((double)nCount)/rNowFraction) - nCount; |
| 324 | 325 | int nXSize = (100 * nExtra)/nMaxEvents; |
| 325 | 326 | @ <span class='statistics-report-graph-line' \ |
| 326 | 327 | @ style='display:inline-block;min-width:%d(nSize)%%;'> </span>\ |
| 327 | - @ <span class='statistics-report-graph-extra' \ | |
| 328 | + @ <span class='statistics-report-graph-extra' title='%d(nProj)' \ | |
| 328 | 329 | @ style='display:inline-block;min-width:%d(nXSize)%%;'> </span>\ |
| 329 | 330 | }else{ |
| 330 | 331 | @ <div class='statistics-report-graph-line' \ |
| 331 | 332 | @ style='width:%d(nSize)%%;'> </div> \ |
| 332 | 333 | } |
| @@ -747,18 +748,19 @@ | ||
| 747 | 748 | if( zCurrentWeek!=0 |
| 748 | 749 | && strcmp(zWeek, zCurrentWeek)==0 |
| 749 | 750 | && rNowFraction>0.05 |
| 750 | 751 | && nMaxEvents>0 |
| 751 | 752 | ){ |
| 752 | - /* If the covered covered by this row contains "now", then project | |
| 753 | + /* If the timespan covered by this row contains "now", then project | |
| 753 | 754 | ** the number of changes until the completion of the week and |
| 754 | 755 | ** show a dashed box of that projection. */ |
| 756 | + int nProj = (int)(((double)nCount)/rNowFraction); | |
| 755 | 757 | int nExtra = (int)(((double)nCount)/rNowFraction) - nCount; |
| 756 | 758 | int nXSize = (100 * nExtra)/nMaxEvents; |
| 757 | 759 | @ <span class='statistics-report-graph-line' \ |
| 758 | 760 | @ style='display:inline-block;min-width:%d(nSize)%%;'> </span>\ |
| 759 | - @ <span class='statistics-report-graph-extra' \ | |
| 761 | + @ <span class='statistics-report-graph-extra' title='%d(nProj)' \ | |
| 760 | 762 | @ style='display:inline-block;min-width:%d(nXSize)%%;'> </span>\ |
| 761 | 763 | }else{ |
| 762 | 764 | @ <div class='statistics-report-graph-line' \ |
| 763 | 765 | @ style='width:%d(nSize)%%;'> </div> \ |
| 764 | 766 | } |
| 765 | 767 |
| --- src/statrep.c | |
| +++ src/statrep.c | |
| @@ -318,15 +318,16 @@ | |
| 318 | && nMaxEvents>0 |
| 319 | ){ |
| 320 | /* If the timespan covered by this row contains "now", then project |
| 321 | ** the number of changes until the completion of the timespan and |
| 322 | ** show a dashed box of that projection. */ |
| 323 | int nExtra = (int)(((double)nCount)/rNowFraction) - nCount; |
| 324 | int nXSize = (100 * nExtra)/nMaxEvents; |
| 325 | @ <span class='statistics-report-graph-line' \ |
| 326 | @ style='display:inline-block;min-width:%d(nSize)%%;'> </span>\ |
| 327 | @ <span class='statistics-report-graph-extra' \ |
| 328 | @ style='display:inline-block;min-width:%d(nXSize)%%;'> </span>\ |
| 329 | }else{ |
| 330 | @ <div class='statistics-report-graph-line' \ |
| 331 | @ style='width:%d(nSize)%%;'> </div> \ |
| 332 | } |
| @@ -747,18 +748,19 @@ | |
| 747 | if( zCurrentWeek!=0 |
| 748 | && strcmp(zWeek, zCurrentWeek)==0 |
| 749 | && rNowFraction>0.05 |
| 750 | && nMaxEvents>0 |
| 751 | ){ |
| 752 | /* If the covered covered by this row contains "now", then project |
| 753 | ** the number of changes until the completion of the week and |
| 754 | ** show a dashed box of that projection. */ |
| 755 | int nExtra = (int)(((double)nCount)/rNowFraction) - nCount; |
| 756 | int nXSize = (100 * nExtra)/nMaxEvents; |
| 757 | @ <span class='statistics-report-graph-line' \ |
| 758 | @ style='display:inline-block;min-width:%d(nSize)%%;'> </span>\ |
| 759 | @ <span class='statistics-report-graph-extra' \ |
| 760 | @ style='display:inline-block;min-width:%d(nXSize)%%;'> </span>\ |
| 761 | }else{ |
| 762 | @ <div class='statistics-report-graph-line' \ |
| 763 | @ style='width:%d(nSize)%%;'> </div> \ |
| 764 | } |
| 765 |
| --- src/statrep.c | |
| +++ src/statrep.c | |
| @@ -318,15 +318,16 @@ | |
| 318 | && nMaxEvents>0 |
| 319 | ){ |
| 320 | /* If the timespan covered by this row contains "now", then project |
| 321 | ** the number of changes until the completion of the timespan and |
| 322 | ** show a dashed box of that projection. */ |
| 323 | int nProj = (int)(((double)nCount)/rNowFraction); |
| 324 | int nExtra = (int)(((double)nCount)/rNowFraction) - nCount; |
| 325 | int nXSize = (100 * nExtra)/nMaxEvents; |
| 326 | @ <span class='statistics-report-graph-line' \ |
| 327 | @ style='display:inline-block;min-width:%d(nSize)%%;'> </span>\ |
| 328 | @ <span class='statistics-report-graph-extra' title='%d(nProj)' \ |
| 329 | @ style='display:inline-block;min-width:%d(nXSize)%%;'> </span>\ |
| 330 | }else{ |
| 331 | @ <div class='statistics-report-graph-line' \ |
| 332 | @ style='width:%d(nSize)%%;'> </div> \ |
| 333 | } |
| @@ -747,18 +748,19 @@ | |
| 748 | if( zCurrentWeek!=0 |
| 749 | && strcmp(zWeek, zCurrentWeek)==0 |
| 750 | && rNowFraction>0.05 |
| 751 | && nMaxEvents>0 |
| 752 | ){ |
| 753 | /* If the timespan covered by this row contains "now", then project |
| 754 | ** the number of changes until the completion of the week and |
| 755 | ** show a dashed box of that projection. */ |
| 756 | int nProj = (int)(((double)nCount)/rNowFraction); |
| 757 | int nExtra = (int)(((double)nCount)/rNowFraction) - nCount; |
| 758 | int nXSize = (100 * nExtra)/nMaxEvents; |
| 759 | @ <span class='statistics-report-graph-line' \ |
| 760 | @ style='display:inline-block;min-width:%d(nSize)%%;'> </span>\ |
| 761 | @ <span class='statistics-report-graph-extra' title='%d(nProj)' \ |
| 762 | @ style='display:inline-block;min-width:%d(nXSize)%%;'> </span>\ |
| 763 | }else{ |
| 764 | @ <div class='statistics-report-graph-line' \ |
| 765 | @ style='width:%d(nSize)%%;'> </div> \ |
| 766 | } |
| 767 |
+33
| --- src/style.c | ||
| +++ src/style.c | ||
| @@ -1340,10 +1340,43 @@ | ||
| 1340 | 1340 | && !login_has_capability(&c, 1, 0) ) zCap[i++] = c; |
| 1341 | 1341 | } |
| 1342 | 1342 | zCap[i] = 0; |
| 1343 | 1343 | return zCap; |
| 1344 | 1344 | } |
| 1345 | + | |
| 1346 | +/* | |
| 1347 | +** WEBPAGE: test-title | |
| 1348 | +** | |
| 1349 | +** Render a test page in which the page title is set by the "title" | |
| 1350 | +** query parameter. This can be used to show that HTML or Javascript | |
| 1351 | +** content in the title does not leak through into generated page, resulting | |
| 1352 | +** in an XSS issue. | |
| 1353 | +** | |
| 1354 | +** Due to the potential for abuse, this webpage is only available to | |
| 1355 | +** administrators. | |
| 1356 | +*/ | |
| 1357 | +void page_test_title(void){ | |
| 1358 | + const char *zTitle; | |
| 1359 | + login_check_credentials(); | |
| 1360 | + if( !g.perm.Admin ){ | |
| 1361 | + login_needed(0); | |
| 1362 | + } | |
| 1363 | + zTitle = P("title"); | |
| 1364 | + if( zTitle==0 ){ | |
| 1365 | + zTitle = "(No Title)"; | |
| 1366 | + } | |
| 1367 | + style_header("%s", zTitle); | |
| 1368 | + @ <p> | |
| 1369 | + @ This page sets its title to the value of the "title" query parameter. | |
| 1370 | + @ The form below is a convenient way to set the title query parameter: | |
| 1371 | + @ | |
| 1372 | + @ <form method="GET"> | |
| 1373 | + @ Title: <input type="text" size="50" name="title" value="%h(zTitle)"> | |
| 1374 | + @ <input type="submit" value="Submit"> | |
| 1375 | + @ </form> | |
| 1376 | + style_finish_page(); | |
| 1377 | +} | |
| 1345 | 1378 | |
| 1346 | 1379 | /* |
| 1347 | 1380 | ** WEBPAGE: test_env |
| 1348 | 1381 | ** |
| 1349 | 1382 | ** Display CGI-variables and other aspects of the run-time |
| 1350 | 1383 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -1340,10 +1340,43 @@ | |
| 1340 | && !login_has_capability(&c, 1, 0) ) zCap[i++] = c; |
| 1341 | } |
| 1342 | zCap[i] = 0; |
| 1343 | return zCap; |
| 1344 | } |
| 1345 | |
| 1346 | /* |
| 1347 | ** WEBPAGE: test_env |
| 1348 | ** |
| 1349 | ** Display CGI-variables and other aspects of the run-time |
| 1350 |
| --- src/style.c | |
| +++ src/style.c | |
| @@ -1340,10 +1340,43 @@ | |
| 1340 | && !login_has_capability(&c, 1, 0) ) zCap[i++] = c; |
| 1341 | } |
| 1342 | zCap[i] = 0; |
| 1343 | return zCap; |
| 1344 | } |
| 1345 | |
| 1346 | /* |
| 1347 | ** WEBPAGE: test-title |
| 1348 | ** |
| 1349 | ** Render a test page in which the page title is set by the "title" |
| 1350 | ** query parameter. This can be used to show that HTML or Javascript |
| 1351 | ** content in the title does not leak through into generated page, resulting |
| 1352 | ** in an XSS issue. |
| 1353 | ** |
| 1354 | ** Due to the potential for abuse, this webpage is only available to |
| 1355 | ** administrators. |
| 1356 | */ |
| 1357 | void page_test_title(void){ |
| 1358 | const char *zTitle; |
| 1359 | login_check_credentials(); |
| 1360 | if( !g.perm.Admin ){ |
| 1361 | login_needed(0); |
| 1362 | } |
| 1363 | zTitle = P("title"); |
| 1364 | if( zTitle==0 ){ |
| 1365 | zTitle = "(No Title)"; |
| 1366 | } |
| 1367 | style_header("%s", zTitle); |
| 1368 | @ <p> |
| 1369 | @ This page sets its title to the value of the "title" query parameter. |
| 1370 | @ The form below is a convenient way to set the title query parameter: |
| 1371 | @ |
| 1372 | @ <form method="GET"> |
| 1373 | @ Title: <input type="text" size="50" name="title" value="%h(zTitle)"> |
| 1374 | @ <input type="submit" value="Submit"> |
| 1375 | @ </form> |
| 1376 | style_finish_page(); |
| 1377 | } |
| 1378 | |
| 1379 | /* |
| 1380 | ** WEBPAGE: test_env |
| 1381 | ** |
| 1382 | ** Display CGI-variables and other aspects of the run-time |
| 1383 |
+1
-1
| --- src/tar.c | ||
| +++ src/tar.c | ||
| @@ -499,11 +499,11 @@ | ||
| 499 | 499 | pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0); |
| 500 | 500 | if( pManifest ){ |
| 501 | 501 | int flg, eflg = 0; |
| 502 | 502 | mTime = (unsigned)((pManifest->rDate - 2440587.5)*86400.0); |
| 503 | 503 | if( pTar ) tar_begin(mTime); |
| 504 | - flg = db_get_manifest_setting(); | |
| 504 | + flg = db_get_manifest_setting(blob_str(&hash)); | |
| 505 | 505 | if( flg ){ |
| 506 | 506 | /* eflg is the effective flags, taking include/exclude into account */ |
| 507 | 507 | if( (pInclude==0 || glob_match(pInclude, "manifest")) |
| 508 | 508 | && !glob_match(pExclude, "manifest") |
| 509 | 509 | && (flg & MFESTFLG_RAW) ){ |
| 510 | 510 |
| --- src/tar.c | |
| +++ src/tar.c | |
| @@ -499,11 +499,11 @@ | |
| 499 | pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0); |
| 500 | if( pManifest ){ |
| 501 | int flg, eflg = 0; |
| 502 | mTime = (unsigned)((pManifest->rDate - 2440587.5)*86400.0); |
| 503 | if( pTar ) tar_begin(mTime); |
| 504 | flg = db_get_manifest_setting(); |
| 505 | if( flg ){ |
| 506 | /* eflg is the effective flags, taking include/exclude into account */ |
| 507 | if( (pInclude==0 || glob_match(pInclude, "manifest")) |
| 508 | && !glob_match(pExclude, "manifest") |
| 509 | && (flg & MFESTFLG_RAW) ){ |
| 510 |
| --- src/tar.c | |
| +++ src/tar.c | |
| @@ -499,11 +499,11 @@ | |
| 499 | pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0); |
| 500 | if( pManifest ){ |
| 501 | int flg, eflg = 0; |
| 502 | mTime = (unsigned)((pManifest->rDate - 2440587.5)*86400.0); |
| 503 | if( pTar ) tar_begin(mTime); |
| 504 | flg = db_get_manifest_setting(blob_str(&hash)); |
| 505 | if( flg ){ |
| 506 | /* eflg is the effective flags, taking include/exclude into account */ |
| 507 | if( (pInclude==0 || glob_match(pInclude, "manifest")) |
| 508 | && !glob_match(pExclude, "manifest") |
| 509 | && (flg & MFESTFLG_RAW) ){ |
| 510 |
+64
-3
| --- src/terminal.c | ||
| +++ src/terminal.c | ||
| @@ -60,31 +60,40 @@ | ||
| 60 | 60 | memset(t, 0, sizeof(*t)); |
| 61 | 61 | |
| 62 | 62 | #if defined(TIOCGSIZE) |
| 63 | 63 | { |
| 64 | 64 | struct ttysize ts; |
| 65 | - if( ioctl(STDIN_FILENO, TIOCGSIZE, &ts)!=-1 ){ | |
| 65 | + if( ioctl(STDIN_FILENO, TIOCGSIZE, &ts)>=0 | |
| 66 | + || ioctl(STDOUT_FILENO, TIOCGSIZE, &ts)>=0 | |
| 67 | + || ioctl(STDERR_FILENO, TIOCGSIZE, &ts)>=0 | |
| 68 | + ){ | |
| 66 | 69 | t->nColumns = ts.ts_cols; |
| 67 | 70 | t->nLines = ts.ts_lines; |
| 68 | 71 | return 1; |
| 69 | 72 | } |
| 70 | 73 | return 0; |
| 71 | 74 | } |
| 72 | 75 | #elif defined(TIOCGWINSZ) |
| 73 | 76 | { |
| 74 | 77 | struct winsize ws; |
| 75 | - if( ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)!=-1 ){ | |
| 78 | + if( ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)>=0 | |
| 79 | + || ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws)>=0 | |
| 80 | + || ioctl(STDERR_FILENO, TIOCGWINSZ, &ws)>=0 | |
| 81 | + ){ | |
| 76 | 82 | t->nColumns = ws.ws_col; |
| 77 | 83 | t->nLines = ws.ws_row; |
| 78 | 84 | return 1; |
| 79 | 85 | } |
| 80 | 86 | return 0; |
| 81 | 87 | } |
| 82 | 88 | #elif defined(_WIN32) |
| 83 | 89 | { |
| 84 | 90 | CONSOLE_SCREEN_BUFFER_INFO csbi; |
| 85 | - if( GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi) ){ | |
| 91 | + if( GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi) | |
| 92 | + || GetConsoleScreenBufferInfo(GetStdHandle(STD_ERROR_HANDLE), &csbi) | |
| 93 | + || GetConsoleScreenBufferInfo(GetStdHandle(STD_INPUT_HANDLE), &csbi) | |
| 94 | + ){ | |
| 86 | 95 | t->nColumns = csbi.srWindow.Right - csbi.srWindow.Left + 1; |
| 87 | 96 | t->nLines = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; |
| 88 | 97 | return 1; |
| 89 | 98 | } |
| 90 | 99 | return 0; |
| @@ -129,5 +138,57 @@ | ||
| 129 | 138 | void test_terminal_size_cmd(void){ |
| 130 | 139 | TerminalSize ts; |
| 131 | 140 | terminal_get_size(&ts); |
| 132 | 141 | fossil_print("%d %d\n", ts.nColumns, ts.nLines); |
| 133 | 142 | } |
| 143 | + | |
| 144 | +/* | |
| 145 | +** Return true if it is reasonable is emit VT100 escape codes. | |
| 146 | +*/ | |
| 147 | +int terminal_is_vt100(void){ | |
| 148 | + char *zNoColor; | |
| 149 | +#ifdef _WIN32 | |
| 150 | + if( !win32_terminal_is_vt100(1) ) return 0; | |
| 151 | +#endif /* _WIN32 */ | |
| 152 | + if( !fossil_isatty(1) ) return 0; | |
| 153 | + zNoColor =fossil_getenv("NO_COLOR"); | |
| 154 | + if( zNoColor==0 ) return 1; | |
| 155 | + if( zNoColor[0]==0 ) return 1; | |
| 156 | + if( is_false(zNoColor) ) return 1; | |
| 157 | + return 0; | |
| 158 | +} | |
| 159 | + | |
| 160 | +#ifdef _WIN32 | |
| 161 | +/* | |
| 162 | +** Return true if the Windows console supports VT100 escape codes. | |
| 163 | +** | |
| 164 | +** Support for VT100 escape codes is enabled by default in Windows Terminal | |
| 165 | +** on Windows 10 and Windows 11, and disabled by default in Legacy Consoles | |
| 166 | +** and on older versions of Windows. Programs can turn on VT100 support for | |
| 167 | +** Legacy Consoles using the ENABLE_VIRTUAL_TERMINAL_PROCESSING flag. | |
| 168 | +** | |
| 169 | +** NOTE: If this function needs to be called in more complex scenarios with | |
| 170 | +** reassigned stdout and stderr streams, the following CRT calls are useful | |
| 171 | +** to translate from CRT streams to file descriptors and to Win32 handles: | |
| 172 | +** | |
| 173 | +** HANDLE hOutputHandle = (HANDLE)_get_osfhandle(_fileno(<FILE*>)); | |
| 174 | +*/ | |
| 175 | +#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING | |
| 176 | +#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 | |
| 177 | +#endif | |
| 178 | +int win32_terminal_is_vt100(int fd){ | |
| 179 | + HANDLE hConsole = NULL; | |
| 180 | + DWORD dwConsoleMode; | |
| 181 | + switch( fd ){ | |
| 182 | + case 1: | |
| 183 | + hConsole = GetStdHandle(STD_OUTPUT_HANDLE); | |
| 184 | + break; | |
| 185 | + case 2: | |
| 186 | + hConsole = GetStdHandle(STD_ERROR_HANDLE); | |
| 187 | + break; | |
| 188 | + } | |
| 189 | + if( GetConsoleMode(hConsole,&dwConsoleMode) ){ | |
| 190 | + return (dwConsoleMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)!=0; | |
| 191 | + } | |
| 192 | + return 0; | |
| 193 | +} | |
| 194 | +#endif /* _WIN32 */ | |
| 134 | 195 |
| --- src/terminal.c | |
| +++ src/terminal.c | |
| @@ -60,31 +60,40 @@ | |
| 60 | memset(t, 0, sizeof(*t)); |
| 61 | |
| 62 | #if defined(TIOCGSIZE) |
| 63 | { |
| 64 | struct ttysize ts; |
| 65 | if( ioctl(STDIN_FILENO, TIOCGSIZE, &ts)!=-1 ){ |
| 66 | t->nColumns = ts.ts_cols; |
| 67 | t->nLines = ts.ts_lines; |
| 68 | return 1; |
| 69 | } |
| 70 | return 0; |
| 71 | } |
| 72 | #elif defined(TIOCGWINSZ) |
| 73 | { |
| 74 | struct winsize ws; |
| 75 | if( ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)!=-1 ){ |
| 76 | t->nColumns = ws.ws_col; |
| 77 | t->nLines = ws.ws_row; |
| 78 | return 1; |
| 79 | } |
| 80 | return 0; |
| 81 | } |
| 82 | #elif defined(_WIN32) |
| 83 | { |
| 84 | CONSOLE_SCREEN_BUFFER_INFO csbi; |
| 85 | if( GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi) ){ |
| 86 | t->nColumns = csbi.srWindow.Right - csbi.srWindow.Left + 1; |
| 87 | t->nLines = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; |
| 88 | return 1; |
| 89 | } |
| 90 | return 0; |
| @@ -129,5 +138,57 @@ | |
| 129 | void test_terminal_size_cmd(void){ |
| 130 | TerminalSize ts; |
| 131 | terminal_get_size(&ts); |
| 132 | fossil_print("%d %d\n", ts.nColumns, ts.nLines); |
| 133 | } |
| 134 |
| --- src/terminal.c | |
| +++ src/terminal.c | |
| @@ -60,31 +60,40 @@ | |
| 60 | memset(t, 0, sizeof(*t)); |
| 61 | |
| 62 | #if defined(TIOCGSIZE) |
| 63 | { |
| 64 | struct ttysize ts; |
| 65 | if( ioctl(STDIN_FILENO, TIOCGSIZE, &ts)>=0 |
| 66 | || ioctl(STDOUT_FILENO, TIOCGSIZE, &ts)>=0 |
| 67 | || ioctl(STDERR_FILENO, TIOCGSIZE, &ts)>=0 |
| 68 | ){ |
| 69 | t->nColumns = ts.ts_cols; |
| 70 | t->nLines = ts.ts_lines; |
| 71 | return 1; |
| 72 | } |
| 73 | return 0; |
| 74 | } |
| 75 | #elif defined(TIOCGWINSZ) |
| 76 | { |
| 77 | struct winsize ws; |
| 78 | if( ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)>=0 |
| 79 | || ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws)>=0 |
| 80 | || ioctl(STDERR_FILENO, TIOCGWINSZ, &ws)>=0 |
| 81 | ){ |
| 82 | t->nColumns = ws.ws_col; |
| 83 | t->nLines = ws.ws_row; |
| 84 | return 1; |
| 85 | } |
| 86 | return 0; |
| 87 | } |
| 88 | #elif defined(_WIN32) |
| 89 | { |
| 90 | CONSOLE_SCREEN_BUFFER_INFO csbi; |
| 91 | if( GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi) |
| 92 | || GetConsoleScreenBufferInfo(GetStdHandle(STD_ERROR_HANDLE), &csbi) |
| 93 | || GetConsoleScreenBufferInfo(GetStdHandle(STD_INPUT_HANDLE), &csbi) |
| 94 | ){ |
| 95 | t->nColumns = csbi.srWindow.Right - csbi.srWindow.Left + 1; |
| 96 | t->nLines = csbi.srWindow.Bottom - csbi.srWindow.Top + 1; |
| 97 | return 1; |
| 98 | } |
| 99 | return 0; |
| @@ -129,5 +138,57 @@ | |
| 138 | void test_terminal_size_cmd(void){ |
| 139 | TerminalSize ts; |
| 140 | terminal_get_size(&ts); |
| 141 | fossil_print("%d %d\n", ts.nColumns, ts.nLines); |
| 142 | } |
| 143 | |
| 144 | /* |
| 145 | ** Return true if it is reasonable is emit VT100 escape codes. |
| 146 | */ |
| 147 | int terminal_is_vt100(void){ |
| 148 | char *zNoColor; |
| 149 | #ifdef _WIN32 |
| 150 | if( !win32_terminal_is_vt100(1) ) return 0; |
| 151 | #endif /* _WIN32 */ |
| 152 | if( !fossil_isatty(1) ) return 0; |
| 153 | zNoColor =fossil_getenv("NO_COLOR"); |
| 154 | if( zNoColor==0 ) return 1; |
| 155 | if( zNoColor[0]==0 ) return 1; |
| 156 | if( is_false(zNoColor) ) return 1; |
| 157 | return 0; |
| 158 | } |
| 159 | |
| 160 | #ifdef _WIN32 |
| 161 | /* |
| 162 | ** Return true if the Windows console supports VT100 escape codes. |
| 163 | ** |
| 164 | ** Support for VT100 escape codes is enabled by default in Windows Terminal |
| 165 | ** on Windows 10 and Windows 11, and disabled by default in Legacy Consoles |
| 166 | ** and on older versions of Windows. Programs can turn on VT100 support for |
| 167 | ** Legacy Consoles using the ENABLE_VIRTUAL_TERMINAL_PROCESSING flag. |
| 168 | ** |
| 169 | ** NOTE: If this function needs to be called in more complex scenarios with |
| 170 | ** reassigned stdout and stderr streams, the following CRT calls are useful |
| 171 | ** to translate from CRT streams to file descriptors and to Win32 handles: |
| 172 | ** |
| 173 | ** HANDLE hOutputHandle = (HANDLE)_get_osfhandle(_fileno(<FILE*>)); |
| 174 | */ |
| 175 | #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING |
| 176 | #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004 |
| 177 | #endif |
| 178 | int win32_terminal_is_vt100(int fd){ |
| 179 | HANDLE hConsole = NULL; |
| 180 | DWORD dwConsoleMode; |
| 181 | switch( fd ){ |
| 182 | case 1: |
| 183 | hConsole = GetStdHandle(STD_OUTPUT_HANDLE); |
| 184 | break; |
| 185 | case 2: |
| 186 | hConsole = GetStdHandle(STD_ERROR_HANDLE); |
| 187 | break; |
| 188 | } |
| 189 | if( GetConsoleMode(hConsole,&dwConsoleMode) ){ |
| 190 | return (dwConsoleMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)!=0; |
| 191 | } |
| 192 | return 0; |
| 193 | } |
| 194 | #endif /* _WIN32 */ |
| 195 |
+22
| --- src/th_main.c | ||
| +++ src/th_main.c | ||
| @@ -696,10 +696,31 @@ | ||
| 696 | 696 | wiki_convert(&src, 0, flags); |
| 697 | 697 | blob_reset(&src); |
| 698 | 698 | } |
| 699 | 699 | return TH_OK; |
| 700 | 700 | } |
| 701 | + | |
| 702 | +/* | |
| 703 | +** TH1 command: wiki_assoc STRING STRING | |
| 704 | +** | |
| 705 | +** Render an associated wiki page. The first string is the namespace | |
| 706 | +** (e.g. "checkin", "branch", "ticket"). The second is the ID of the | |
| 707 | +** associated object. See wiki_render_associated(). | |
| 708 | +*/ | |
| 709 | +static int wikiAssocCmd( | |
| 710 | + Th_Interp *interp, | |
| 711 | + void *p, | |
| 712 | + int argc, | |
| 713 | + const char **argv, | |
| 714 | + int *argl | |
| 715 | +){ | |
| 716 | + if( argc!=3 ){ | |
| 717 | + return Th_WrongNumArgs(interp, "wiki_assoc STRING STRING"); | |
| 718 | + } | |
| 719 | + wiki_render_associated((char*)argv[1], (char*)argv[2], WIKIASSOC_FULL_TITLE); | |
| 720 | + return TH_OK; | |
| 721 | +} | |
| 701 | 722 | |
| 702 | 723 | /* |
| 703 | 724 | ** TH1 command: htmlize STRING |
| 704 | 725 | ** |
| 705 | 726 | ** Escape all characters of STRING which have special meaning in HTML. |
| @@ -2374,10 +2395,11 @@ | ||
| 2374 | 2395 | {"unversioned", unversionedCmd, 0}, |
| 2375 | 2396 | {"utime", utimeCmd, 0}, |
| 2376 | 2397 | {"verifyCsrf", verifyCsrfCmd, 0}, |
| 2377 | 2398 | {"verifyLogin", verifyLoginCmd, 0}, |
| 2378 | 2399 | {"wiki", wikiCmd, (void*)&aFlags[0]}, |
| 2400 | + {"wiki_assoc", wikiAssocCmd, 0}, | |
| 2379 | 2401 | {0, 0, 0} |
| 2380 | 2402 | }; |
| 2381 | 2403 | if( g.thTrace ){ |
| 2382 | 2404 | Th_Trace("th1-init 0x%x => 0x%x<br>\n", g.th1Flags, flags); |
| 2383 | 2405 | } |
| 2384 | 2406 |
| --- src/th_main.c | |
| +++ src/th_main.c | |
| @@ -696,10 +696,31 @@ | |
| 696 | wiki_convert(&src, 0, flags); |
| 697 | blob_reset(&src); |
| 698 | } |
| 699 | return TH_OK; |
| 700 | } |
| 701 | |
| 702 | /* |
| 703 | ** TH1 command: htmlize STRING |
| 704 | ** |
| 705 | ** Escape all characters of STRING which have special meaning in HTML. |
| @@ -2374,10 +2395,11 @@ | |
| 2374 | {"unversioned", unversionedCmd, 0}, |
| 2375 | {"utime", utimeCmd, 0}, |
| 2376 | {"verifyCsrf", verifyCsrfCmd, 0}, |
| 2377 | {"verifyLogin", verifyLoginCmd, 0}, |
| 2378 | {"wiki", wikiCmd, (void*)&aFlags[0]}, |
| 2379 | {0, 0, 0} |
| 2380 | }; |
| 2381 | if( g.thTrace ){ |
| 2382 | Th_Trace("th1-init 0x%x => 0x%x<br>\n", g.th1Flags, flags); |
| 2383 | } |
| 2384 |
| --- src/th_main.c | |
| +++ src/th_main.c | |
| @@ -696,10 +696,31 @@ | |
| 696 | wiki_convert(&src, 0, flags); |
| 697 | blob_reset(&src); |
| 698 | } |
| 699 | return TH_OK; |
| 700 | } |
| 701 | |
| 702 | /* |
| 703 | ** TH1 command: wiki_assoc STRING STRING |
| 704 | ** |
| 705 | ** Render an associated wiki page. The first string is the namespace |
| 706 | ** (e.g. "checkin", "branch", "ticket"). The second is the ID of the |
| 707 | ** associated object. See wiki_render_associated(). |
| 708 | */ |
| 709 | static int wikiAssocCmd( |
| 710 | Th_Interp *interp, |
| 711 | void *p, |
| 712 | int argc, |
| 713 | const char **argv, |
| 714 | int *argl |
| 715 | ){ |
| 716 | if( argc!=3 ){ |
| 717 | return Th_WrongNumArgs(interp, "wiki_assoc STRING STRING"); |
| 718 | } |
| 719 | wiki_render_associated((char*)argv[1], (char*)argv[2], WIKIASSOC_FULL_TITLE); |
| 720 | return TH_OK; |
| 721 | } |
| 722 | |
| 723 | /* |
| 724 | ** TH1 command: htmlize STRING |
| 725 | ** |
| 726 | ** Escape all characters of STRING which have special meaning in HTML. |
| @@ -2374,10 +2395,11 @@ | |
| 2395 | {"unversioned", unversionedCmd, 0}, |
| 2396 | {"utime", utimeCmd, 0}, |
| 2397 | {"verifyCsrf", verifyCsrfCmd, 0}, |
| 2398 | {"verifyLogin", verifyLoginCmd, 0}, |
| 2399 | {"wiki", wikiCmd, (void*)&aFlags[0]}, |
| 2400 | {"wiki_assoc", wikiAssocCmd, 0}, |
| 2401 | {0, 0, 0} |
| 2402 | }; |
| 2403 | if( g.thTrace ){ |
| 2404 | Th_Trace("th1-init 0x%x => 0x%x<br>\n", g.th1Flags, flags); |
| 2405 | } |
| 2406 |
+216
-138
| --- src/timeline.c | ||
| +++ src/timeline.c | ||
| @@ -205,11 +205,10 @@ | ||
| 205 | 205 | char zPrevDate[20]; |
| 206 | 206 | GraphContext *pGraph = 0; |
| 207 | 207 | int prevWasDivider = 0; /* True if previous output row was <hr> */ |
| 208 | 208 | int fchngQueryInit = 0; /* True if fchngQuery is initialized */ |
| 209 | 209 | Stmt fchngQuery; /* Query for file changes on check-ins */ |
| 210 | - static Stmt qbranch; | |
| 211 | 210 | int pendingEndTr = 0; /* True if a </td></tr> is needed */ |
| 212 | 211 | int vid = 0; /* Current check-out version */ |
| 213 | 212 | int dateFormat = 0; /* 0: HH:MM (default) */ |
| 214 | 213 | int bCommentGitStyle = 0; /* Only show comments through first blank line */ |
| 215 | 214 | const char *zStyle; /* Sub-name for classes for the style */ |
| @@ -222,11 +221,28 @@ | ||
| 222 | 221 | vid = db_lget_int("checkout", 0); |
| 223 | 222 | } |
| 224 | 223 | zPrevDate[0] = 0; |
| 225 | 224 | mxWikiLen = db_get_int("timeline-max-comment", 0); |
| 226 | 225 | dateFormat = db_get_int("timeline-date-format", 0); |
| 226 | + /* | |
| 227 | + ** SETTING: timeline-truncate-at-blank boolean default=off | |
| 228 | + ** | |
| 229 | + ** If enabled, check-in comments displayed on the timeline are truncated | |
| 230 | + ** at the first blank line of the comment text. The comment text after | |
| 231 | + ** the first blank line is only seen in the /info or similar pages that | |
| 232 | + ** show details about the check-in. | |
| 233 | + */ | |
| 227 | 234 | bCommentGitStyle = db_get_int("timeline-truncate-at-blank", 0); |
| 235 | + /* | |
| 236 | + ** SETTING: timeline-tslink-info boolean default=off | |
| 237 | + ** | |
| 238 | + ** The hyperlink on the timestamp associated with each timeline entry, | |
| 239 | + ** on the far left-hand side of the screen, normally targets another | |
| 240 | + ** /timeline page that shows the entry in context. However, if this | |
| 241 | + ** option is turned on, that hyperlink targets the /info page showing | |
| 242 | + ** the details of the entry. | |
| 243 | + */ | |
| 228 | 244 | bTimestampLinksToInfo = db_get_boolean("timeline-tslink-info", 0); |
| 229 | 245 | if( (tmFlags & TIMELINE_VIEWS)==0 ){ |
| 230 | 246 | tmFlags |= timeline_ss_cookie(); |
| 231 | 247 | } |
| 232 | 248 | if( tmFlags & TIMELINE_COLUMNAR ){ |
| @@ -243,14 +259,10 @@ | ||
| 243 | 259 | zDateFmt = P("datefmt"); |
| 244 | 260 | if( zDateFmt ) dateFormat = atoi(zDateFmt); |
| 245 | 261 | if( tmFlags & TIMELINE_GRAPH ){ |
| 246 | 262 | pGraph = graph_init(); |
| 247 | 263 | } |
| 248 | - db_static_prepare(&qbranch, | |
| 249 | - "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid", | |
| 250 | - TAG_BRANCH | |
| 251 | - ); | |
| 252 | 264 | if( (tmFlags & TIMELINE_CHPICK)!=0 |
| 253 | 265 | && !db_table_exists("repository","cherrypick") |
| 254 | 266 | ){ |
| 255 | 267 | tmFlags &= ~TIMELINE_CHPICK; |
| 256 | 268 | } |
| @@ -266,11 +278,11 @@ | ||
| 266 | 278 | const char *zType = db_column_text(pQuery, 7); |
| 267 | 279 | const char *zUser = db_column_text(pQuery, 4); |
| 268 | 280 | const char *zTagList = db_column_text(pQuery, 8); |
| 269 | 281 | int tagid = db_column_int(pQuery, 9); |
| 270 | 282 | const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous"; |
| 271 | - const char *zBr = 0; /* Branch */ | |
| 283 | + char *zBr = 0; /* Branch */ | |
| 272 | 284 | int commentColumn = 3; /* Column containing comment text */ |
| 273 | 285 | int modPending; /* Pending moderation */ |
| 274 | 286 | char *zDateLink; /* URL for the link on the timestamp */ |
| 275 | 287 | int drawDetailEllipsis; /* True to show ellipsis in place of detail */ |
| 276 | 288 | int gidx = 0; /* Graph row identifier */ |
| @@ -390,10 +402,12 @@ | ||
| 390 | 402 | zDateLink = mprintf("<a>"); |
| 391 | 403 | } |
| 392 | 404 | @ <td class="timelineTime">%z(zDateLink)%s(zTime)</a></td> |
| 393 | 405 | @ <td class="timelineGraph"> |
| 394 | 406 | if( tmFlags & (TIMELINE_UCOLOR|TIMELINE_DELTA|TIMELINE_NOCOLOR) ){ |
| 407 | + /* Don't use the requested background color. Use the background color | |
| 408 | + ** override from query parameters instead. */ | |
| 395 | 409 | if( tmFlags & TIMELINE_UCOLOR ){ |
| 396 | 410 | zBgClr = zUser ? user_color(zUser) : 0; |
| 397 | 411 | }else if( tmFlags & TIMELINE_NOCOLOR ){ |
| 398 | 412 | zBgClr = 0; |
| 399 | 413 | }else if( zType[0]=='c' ){ |
| @@ -408,22 +422,21 @@ | ||
| 408 | 422 | }else{ |
| 409 | 423 | zBgClr = hash_color("f"); /* delta manifest */ |
| 410 | 424 | } |
| 411 | 425 | db_reset(&qdelta); |
| 412 | 426 | } |
| 427 | + }else{ | |
| 428 | + /* Make sure the user-specified background color is reasonable */ | |
| 429 | + zBgClr = reasonable_bg_color(zBgClr, 0); | |
| 413 | 430 | } |
| 414 | 431 | if( zType[0]=='c' |
| 415 | 432 | && (pGraph || zBgClr==0 || (tmFlags & (TIMELINE_BRCOLOR|TIMELINE_DELTA))!=0) |
| 416 | 433 | ){ |
| 417 | - db_reset(&qbranch); | |
| 418 | - db_bind_int(&qbranch, ":rid", rid); | |
| 419 | - if( db_step(&qbranch)==SQLITE_ROW ){ | |
| 420 | - zBr = db_column_text(&qbranch, 0); | |
| 421 | - }else{ | |
| 422 | - zBr = "trunk"; | |
| 423 | - } | |
| 434 | + zBr = branch_of_rid(rid); | |
| 424 | 435 | if( zBgClr==0 || (tmFlags & TIMELINE_BRCOLOR)!=0 ){ |
| 436 | + /* If no background color is specified, use a color based on the | |
| 437 | + ** branch name */ | |
| 425 | 438 | if( tmFlags & (TIMELINE_DELTA|TIMELINE_NOCOLOR) ){ |
| 426 | 439 | }else if( zBr==0 || strcmp(zBr,"trunk")==0 ){ |
| 427 | 440 | zBgClr = 0; |
| 428 | 441 | }else{ |
| 429 | 442 | zBgClr = hash_color(zBr); |
| @@ -459,19 +472,19 @@ | ||
| 459 | 472 | db_reset(&qcherrypick); |
| 460 | 473 | } |
| 461 | 474 | gidx = graph_add_row(pGraph, rid, nParent, nCherrypick, aParent, |
| 462 | 475 | zBr, zBgClr, zUuid, |
| 463 | 476 | isLeaf ? isLeaf + 2 * has_closed_tag(rid) : 0); |
| 464 | - db_reset(&qbranch); | |
| 465 | 477 | @ <div id="m%d(gidx)" class="tl-nodemark"></div> |
| 466 | 478 | }else if( zType[0]=='e' && pGraph && zBgClr && zBgClr[0] ){ |
| 467 | 479 | /* For technotes, make a graph node with nParent==(-1). This will |
| 468 | 480 | ** not actually draw anything on the graph, but it will set the |
| 469 | 481 | ** background color of the timeline entry */ |
| 470 | 482 | gidx = graph_add_row(pGraph, rid, -1, 0, 0, zBr, zBgClr, zUuid, 0); |
| 471 | 483 | @ <div id="m%d(gidx)" class="tl-nodemark"></div> |
| 472 | 484 | } |
| 485 | + fossil_free(zBr); | |
| 473 | 486 | @</td> |
| 474 | 487 | if( !isSelectedOrCurrent ){ |
| 475 | 488 | @ <td class="timeline%s(zStyle)Cell%s(zExtraClass)" id='mc%d(gidx)'> |
| 476 | 489 | }else{ |
| 477 | 490 | @ <td class="timeline%s(zStyle)Cell%s(zExtraClass)"> |
| @@ -1100,47 +1113,10 @@ | ||
| 1100 | 1113 | @ WHERE blob.rid=event.objid |
| 1101 | 1114 | ; |
| 1102 | 1115 | return zBase; |
| 1103 | 1116 | } |
| 1104 | 1117 | |
| 1105 | -/* | |
| 1106 | -** Convert a symbolic name used as an argument to the a=, b=, or c= | |
| 1107 | -** query parameters of timeline into a julianday mtime value. | |
| 1108 | -*/ | |
| 1109 | -double symbolic_name_to_mtime(const char *z, const char **pzDisplay){ | |
| 1110 | - double mtime; | |
| 1111 | - int rid; | |
| 1112 | - const char *zDate; | |
| 1113 | - if( z==0 ) return -1.0; | |
| 1114 | - if( fossil_isdate(z) ){ | |
| 1115 | - mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", z); | |
| 1116 | - if( mtime>0.0 ) return mtime; | |
| 1117 | - } | |
| 1118 | - zDate = fossil_expand_datetime(z, 1); | |
| 1119 | - if( zDate!=0 ){ | |
| 1120 | - mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", | |
| 1121 | - fossil_roundup_date(zDate)); | |
| 1122 | - if( mtime>0.0 ){ | |
| 1123 | - if( pzDisplay ) *pzDisplay = fossil_strdup(zDate); | |
| 1124 | - return mtime; | |
| 1125 | - } | |
| 1126 | - } | |
| 1127 | - rid = symbolic_name_to_rid(z, "*"); | |
| 1128 | - if( rid ){ | |
| 1129 | - mtime = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid); | |
| 1130 | - }else{ | |
| 1131 | - mtime = db_double(-1.0, | |
| 1132 | - "SELECT max(event.mtime) FROM event, tag, tagxref" | |
| 1133 | - " WHERE tag.tagname GLOB 'event-%q*'" | |
| 1134 | - " AND tagxref.tagid=tag.tagid AND tagxref.tagtype" | |
| 1135 | - " AND event.objid=tagxref.rid", | |
| 1136 | - z | |
| 1137 | - ); | |
| 1138 | - } | |
| 1139 | - return mtime; | |
| 1140 | -} | |
| 1141 | - | |
| 1142 | 1118 | /* |
| 1143 | 1119 | ** zDate is a localtime date. Insert records into the |
| 1144 | 1120 | ** "timeline" table to cause <hr> to be inserted on zDate. |
| 1145 | 1121 | */ |
| 1146 | 1122 | static int timeline_add_divider(double rDate){ |
| @@ -1392,11 +1368,11 @@ | ||
| 1392 | 1368 | ** return 0. |
| 1393 | 1369 | */ |
| 1394 | 1370 | static int timeline_endpoint( |
| 1395 | 1371 | int iFrom, /* Starting point */ |
| 1396 | 1372 | const char *zEnd, /* Tag we are searching for */ |
| 1397 | - int bForward /* 1: forwards in time (descendents) 0: backwards */ | |
| 1373 | + int bForward /* 1: forwards in time (descendants) 0: backwards */ | |
| 1398 | 1374 | ){ |
| 1399 | 1375 | int tagId; |
| 1400 | 1376 | int endId = 0; |
| 1401 | 1377 | Stmt q; |
| 1402 | 1378 | int ans = 0; |
| @@ -1419,11 +1395,12 @@ | ||
| 1419 | 1395 | " AND plink.mtime<=(SELECT max(event.mtime) FROM tagxref, event" |
| 1420 | 1396 | " WHERE tagxref.tagid=%d AND tagxref.tagtype>0" |
| 1421 | 1397 | " AND event.objid=tagxref.rid)" |
| 1422 | 1398 | " ORDER BY plink.mtime)" |
| 1423 | 1399 | "SELECT id FROM dx, tagxref" |
| 1424 | - " WHERE tagid=%d AND tagtype>0 AND rid=id LIMIT 1", | |
| 1400 | + " WHERE tagid=%d AND tagtype>0 AND rid=id" | |
| 1401 | + " ORDER BY dx.mtime LIMIT 1", | |
| 1425 | 1402 | iFrom, iFrom, tagId, tagId |
| 1426 | 1403 | ); |
| 1427 | 1404 | }else{ |
| 1428 | 1405 | db_prepare(&q, |
| 1429 | 1406 | "WITH RECURSIVE dx(id,mtime) AS (" |
| @@ -1450,11 +1427,12 @@ | ||
| 1450 | 1427 | " AND event.mtime>=(SELECT min(event.mtime) FROM tagxref, event" |
| 1451 | 1428 | " WHERE tagxref.tagid=%d AND tagxref.tagtype>0" |
| 1452 | 1429 | " AND event.objid=tagxref.rid)" |
| 1453 | 1430 | " ORDER BY event.mtime DESC)" |
| 1454 | 1431 | "SELECT id FROM dx, tagxref" |
| 1455 | - " WHERE tagid=%d AND tagtype>0 AND rid=id LIMIT 1", | |
| 1432 | + " WHERE tagid=%d AND tagtype>0 AND rid=id" | |
| 1433 | + " ORDER BY dx.mtime DESC LIMIT 1", | |
| 1456 | 1434 | iFrom, iFrom, tagId, tagId |
| 1457 | 1435 | ); |
| 1458 | 1436 | }else{ |
| 1459 | 1437 | db_prepare(&q, |
| 1460 | 1438 | "WITH RECURSIVE dx(id,mtime) AS (" |
| @@ -1522,17 +1500,17 @@ | ||
| 1522 | 1500 | /* |
| 1523 | 1501 | ** COMMAND: test-endpoint |
| 1524 | 1502 | ** |
| 1525 | 1503 | ** Usage: fossil test-endpoint BASE TAG ?OPTIONS? |
| 1526 | 1504 | ** |
| 1527 | -** Show the first check-in with TAG that is a descendent or ancestor | |
| 1528 | -** of BASE. The first descendent checkin is shown by default. Use | |
| 1505 | +** Show the first check-in with TAG that is a descendant or ancestor | |
| 1506 | +** of BASE. The first descendant checkin is shown by default. Use | |
| 1529 | 1507 | ** the --backto to see the first ancestor checkin. |
| 1530 | 1508 | ** |
| 1531 | 1509 | ** Options: |
| 1532 | 1510 | ** |
| 1533 | -** --backto Show ancestor. Others defaults to descendents. | |
| 1511 | +** --backto Show ancestor. Others defaults to descendants. | |
| 1534 | 1512 | */ |
| 1535 | 1513 | void timeline_test_endpoint(void){ |
| 1536 | 1514 | int bForward = find_option("backto",0,0)==0; |
| 1537 | 1515 | int from_rid; |
| 1538 | 1516 | int ans; |
| @@ -1583,14 +1561,14 @@ | ||
| 1583 | 1561 | ** dp=CHECKIN Same as 'd=CHECKIN&p=CHECKIN' |
| 1584 | 1562 | ** dp2=CKIN2 Same as 'd2=CKIN2&p2=CKIN2' |
| 1585 | 1563 | ** df=CHECKIN Same as 'd=CHECKIN&n1=all&nd'. Mnemonic: "Derived From" |
| 1586 | 1564 | ** bt=CHECKIN "Back To". Show ancenstors going back to CHECKIN |
| 1587 | 1565 | ** p=CX ... from CX back to time of CHECKIN |
| 1588 | -** from=CX ... shortest path from CX back to CHECKIN | |
| 1566 | +** from=CX ... path from CX back to CHECKIN | |
| 1589 | 1567 | ** ft=CHECKIN "Forward To": Show decendents forward to CHECKIN |
| 1590 | 1568 | ** d=CX ... from CX up to the time of CHECKIN |
| 1591 | -** from=CX ... shortest path from CX up to CHECKIN | |
| 1569 | +** from=CX ... path from CX up to CHECKIN | |
| 1592 | 1570 | ** t=TAG Show only check-ins with the given TAG |
| 1593 | 1571 | ** r=TAG Same as 't=TAG&rel'. Mnemonic: "Related" |
| 1594 | 1572 | ** tl=TAGLIST Same as 't=TAGLIST&ms=brlist'. Mnemonic: "Tag List" |
| 1595 | 1573 | ** rl=TAGLIST Same as 'r=TAGLIST&ms=brlist'. Mnemonic: "Related List" |
| 1596 | 1574 | ** ml=TAGLIST Same as 'tl=TAGLIST&mionly'. Mnemonic: "Merge-in List" |
| @@ -1611,20 +1589,21 @@ | ||
| 1611 | 1589 | ** nsm Omit the submenu |
| 1612 | 1590 | ** nc Omit all graph colors other than highlights |
| 1613 | 1591 | ** v Show details of files changed |
| 1614 | 1592 | ** vfx Show complete text of forum messages |
| 1615 | 1593 | ** f=CHECKIN Family (immediate parents and children) of CHECKIN |
| 1616 | -** from=CHECKIN Path through common ancestor from... | |
| 1617 | -** to=CHECKIN ... to this | |
| 1618 | -** to2=CHECKIN ... backup name if to= doesn't resolve | |
| 1619 | -** shortest ... show only the shortest path | |
| 1620 | -** rel ... also show related checkins | |
| 1621 | -** bt=PRIOR ... path from CHECKIN back to PRIOR | |
| 1622 | -** ft=LATER ... path from CHECKIN forward to LATER | |
| 1623 | -** me=CHECKIN Most direct path from... | |
| 1624 | -** you=CHECKIN ... to this | |
| 1625 | -** rel ... also show related checkins | |
| 1594 | +** from=CHECKIN Path through common ancestor from CHECKIN... | |
| 1595 | +** to=CHECKIN ... to this | |
| 1596 | +** to2=CHECKIN ... backup name if to= doesn't resolve | |
| 1597 | +** shortest ... pick path with least number of nodes | |
| 1598 | +** rel ... also show related checkins | |
| 1599 | +** min ... hide long sequences along same branch | |
| 1600 | +** bt=PRIOR ... path from CHECKIN back to PRIOR | |
| 1601 | +** ft=LATER ... path from CHECKIN forward to LATER | |
| 1602 | +** me=CHECKIN Most direct path from CHECKIN... | |
| 1603 | +** you=CHECKIN ... to this | |
| 1604 | +** rel ... also show related checkins | |
| 1626 | 1605 | ** uf=FILE_HASH Show only check-ins that contain the given file version |
| 1627 | 1606 | ** All qualifying check-ins are shown unless there is |
| 1628 | 1607 | ** also an n= or n1= query parameter. |
| 1629 | 1608 | ** chng=GLOBLIST Show only check-ins that involve changes to a file whose |
| 1630 | 1609 | ** name matches one of the comma-separate GLOBLIST |
| @@ -1707,15 +1686,15 @@ | ||
| 1707 | 1686 | const char *zThisUser = 0; /* Suppress links to this user */ |
| 1708 | 1687 | HQuery url; /* URL for various branch links */ |
| 1709 | 1688 | int from_rid = name_to_typed_rid(P("from"),"ci"); /* from= for paths */ |
| 1710 | 1689 | const char *zTo2 = 0; |
| 1711 | 1690 | int to_rid = name_choice("to","to2",&zTo2); /* to= for path timelines */ |
| 1712 | - int noMerge = P("shortest")==0; /* Follow merge links if shorter */ | |
| 1691 | + int bShort = P("shortest")!=0; /* shortest possible path */ | |
| 1713 | 1692 | int me_rid = name_to_typed_rid(P("me"),"ci"); /* me= for common ancestory */ |
| 1714 | 1693 | int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */ |
| 1715 | 1694 | int pd_rid; |
| 1716 | - const char *zDPName; /* Value of p=, d=, or dp= params */ | |
| 1695 | + const char *zDPNameP, *zDPNameD; /* Value of p=, d=, or dp= params */ | |
| 1717 | 1696 | double rBefore, rAfter, rCirca; /* Boundary times */ |
| 1718 | 1697 | const char *z; |
| 1719 | 1698 | char *zOlderButton = 0; /* URL for Older button at the bottom */ |
| 1720 | 1699 | char *zOlderButtonLabel = 0; /* Label for the Older Button */ |
| 1721 | 1700 | char *zNewerButton = 0; /* URL for Newer button at the top */ |
| @@ -1728,10 +1707,11 @@ | ||
| 1728 | 1707 | int showCherrypicks = 1; /* True to show cherrypick merges */ |
| 1729 | 1708 | int haveParameterN; /* True if n= query parameter present */ |
| 1730 | 1709 | int from_to_mode = 0; /* 0: from,to. 1: from,ft 2: from,bt */ |
| 1731 | 1710 | int showSql = PB("showsql"); /* True to show the SQL */ |
| 1732 | 1711 | Blob allSql; /* Copy of all SQL text */ |
| 1712 | + int bMin = P("min")!=0; /* True if "min" query parameter used */ | |
| 1733 | 1713 | |
| 1734 | 1714 | login_check_credentials(); |
| 1735 | 1715 | url_initialize(&url, "timeline"); |
| 1736 | 1716 | cgi_query_parameters_to_url(&url); |
| 1737 | 1717 | blob_init(&allSql, 0, 0); |
| @@ -1777,20 +1757,20 @@ | ||
| 1777 | 1757 | }else{ |
| 1778 | 1758 | nEntry = 50; |
| 1779 | 1759 | } |
| 1780 | 1760 | |
| 1781 | 1761 | /* Query parameters d=, p=, and f= and variants */ |
| 1782 | - p_rid = name_choice("p","p2", &zDPName); | |
| 1783 | - d_rid = name_choice("d","d2", &zDPName); | |
| 1762 | + p_rid = name_choice("p","p2", &zDPNameP); | |
| 1763 | + d_rid = name_choice("d","d2", &zDPNameD); | |
| 1784 | 1764 | z = P("f"); |
| 1785 | 1765 | f_rid = z ? name_to_typed_rid(z,"ci") : 0; |
| 1786 | 1766 | z = P("df"); |
| 1787 | 1767 | if( z && (d_rid = name_to_typed_rid(z,"ci"))!=0 ){ |
| 1788 | 1768 | nEntry = 0; |
| 1789 | 1769 | useDividers = 0; |
| 1790 | 1770 | cgi_replace_query_parameter("d",fossil_strdup(z)); |
| 1791 | - zDPName = z; | |
| 1771 | + zDPNameD = zDPNameP = z; | |
| 1792 | 1772 | } |
| 1793 | 1773 | |
| 1794 | 1774 | /* Undocumented query parameter to set JS mode */ |
| 1795 | 1775 | builtin_set_js_delivery_mode(P("jsmode"),1); |
| 1796 | 1776 | |
| @@ -1810,13 +1790,14 @@ | ||
| 1810 | 1790 | showCherrypicks = 0; |
| 1811 | 1791 | } |
| 1812 | 1792 | |
| 1813 | 1793 | /* To view the timeline, must have permission to read project data. |
| 1814 | 1794 | */ |
| 1815 | - pd_rid = name_choice("dp","dp2",&zDPName); | |
| 1795 | + pd_rid = name_choice("dp","dp2",&zDPNameP); | |
| 1816 | 1796 | if( pd_rid ){ |
| 1817 | 1797 | p_rid = d_rid = pd_rid; |
| 1798 | + zDPNameD = zDPNameP; | |
| 1818 | 1799 | } |
| 1819 | 1800 | if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum) |
| 1820 | 1801 | || (bisectLocal && !g.perm.Setup) |
| 1821 | 1802 | ){ |
| 1822 | 1803 | login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki); |
| @@ -2083,20 +2064,22 @@ | ||
| 2083 | 2064 | const char *zTo = 0; |
| 2084 | 2065 | Blob ins; |
| 2085 | 2066 | int nNodeOnPath = 0; |
| 2086 | 2067 | int commonAncs = 0; /* Common ancestors of me_rid and you_rid. */ |
| 2087 | 2068 | int earlierRid = 0, laterRid = 0; |
| 2069 | + int cost = bShort ? 0 : 1; | |
| 2070 | + int nSkip = 0; | |
| 2088 | 2071 | |
| 2089 | 2072 | if( from_rid && to_rid ){ |
| 2090 | 2073 | if( from_to_mode==0 ){ |
| 2091 | - p = path_shortest(from_rid, to_rid, noMerge, 0, 0); | |
| 2074 | + p = path_shortest(from_rid, to_rid, 0, 0, 0, cost); | |
| 2092 | 2075 | }else if( from_to_mode==1 ){ |
| 2093 | - p = path_shortest(from_rid, to_rid, 0, 1, 0); | |
| 2076 | + p = path_shortest(from_rid, to_rid, 0, 1, 0, cost); | |
| 2094 | 2077 | earlierRid = commonAncs = from_rid; |
| 2095 | 2078 | laterRid = to_rid; |
| 2096 | 2079 | }else{ |
| 2097 | - p = path_shortest(to_rid, from_rid, 0, 1, 0); | |
| 2080 | + p = path_shortest(to_rid, from_rid, 0, 1, 0, cost); | |
| 2098 | 2081 | earlierRid = commonAncs = to_rid; |
| 2099 | 2082 | laterRid = from_rid; |
| 2100 | 2083 | } |
| 2101 | 2084 | zFrom = P("from"); |
| 2102 | 2085 | zTo = zTo2 ? zTo2 : P("to"); |
| @@ -2123,20 +2106,25 @@ | ||
| 2123 | 2106 | ); |
| 2124 | 2107 | if( p ){ |
| 2125 | 2108 | int cnt = 4; |
| 2126 | 2109 | blob_init(&ins, 0, 0); |
| 2127 | 2110 | blob_append_sql(&ins, "INSERT INTO pathnode(x) VALUES(%d)", p->rid); |
| 2128 | - p = p->u.pTo; | |
| 2129 | - while( p ){ | |
| 2130 | - if( cnt==8 ){ | |
| 2111 | + if( p->u.pTo==0 ) bMin = 0; | |
| 2112 | + for(p=p->u.pTo; p; p=p->u.pTo){ | |
| 2113 | + if( bMin | |
| 2114 | + && p->u.pTo!=0 | |
| 2115 | + && fossil_strcmp(path_branch(p->pFrom),path_branch(p))==0 | |
| 2116 | + && fossil_strcmp(path_branch(p),path_branch(p->u.pTo))==0 | |
| 2117 | + ){ | |
| 2118 | + nSkip++; | |
| 2119 | + }else if( cnt==8 ){ | |
| 2131 | 2120 | blob_append_sql(&ins, ",\n (%d)", p->rid); |
| 2132 | 2121 | cnt = 0; |
| 2133 | 2122 | }else{ |
| 2134 | 2123 | cnt++; |
| 2135 | 2124 | blob_append_sql(&ins, ",(%d)", p->rid); |
| 2136 | 2125 | } |
| 2137 | - p = p->u.pTo; | |
| 2138 | 2126 | } |
| 2139 | 2127 | } |
| 2140 | 2128 | path_reset(); |
| 2141 | 2129 | db_multi_exec("%s", blob_str(&ins)/*safe-for-%s*/); |
| 2142 | 2130 | blob_reset(&ins); |
| @@ -2196,12 +2184,13 @@ | ||
| 2196 | 2184 | style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0); |
| 2197 | 2185 | } |
| 2198 | 2186 | nNodeOnPath = db_int(0, "SELECT count(*) FROM temp.pathnode"); |
| 2199 | 2187 | if( nNodeOnPath==1 && from_to_mode>0 ){ |
| 2200 | 2188 | blob_appendf(&desc,"Check-in "); |
| 2201 | - }else if( from_to_mode>0 ){ | |
| 2202 | - blob_appendf(&desc, "%d check-ins on the shorted path from ",nNodeOnPath); | |
| 2189 | + }else if( bMin ){ | |
| 2190 | + blob_appendf(&desc, "%d of %d check-ins along the path from ", | |
| 2191 | + nNodeOnPath, nNodeOnPath+nSkip); | |
| 2203 | 2192 | }else{ |
| 2204 | 2193 | blob_appendf(&desc, "%d check-ins going from ", nNodeOnPath); |
| 2205 | 2194 | } |
| 2206 | 2195 | if( from_rid==selectedRid ){ |
| 2207 | 2196 | blob_appendf(&desc, "<span class='timelineSelected'>"); |
| @@ -2225,49 +2214,60 @@ | ||
| 2225 | 2214 | } |
| 2226 | 2215 | } |
| 2227 | 2216 | } |
| 2228 | 2217 | addFileGlobDescription(zChng, &desc); |
| 2229 | 2218 | }else if( (p_rid || d_rid) && g.perm.Read && zTagSql==0 ){ |
| 2230 | - /* If p= or d= is present, ignore all other parameters other than n= */ | |
| 2231 | - char *zUuid; | |
| 2232 | - const char *zCiName; | |
| 2219 | + /* If either p= or d= or both are present, ignore all other parameters | |
| 2220 | + ** other than n=, ft=, and bt= */ | |
| 2221 | + const char *zBaseName = 0; | |
| 2233 | 2222 | int np = 0, nd; |
| 2234 | 2223 | const char *zBackTo = 0; |
| 2235 | 2224 | const char *zFwdTo = 0; |
| 2236 | 2225 | int ridBackTo = 0; |
| 2237 | 2226 | int ridFwdTo = 0; |
| 2227 | + int bBackAdded = 0; /* True if the zBackTo node was added */ | |
| 2228 | + int bFwdAdded = 0; /* True if the zBackTo node was added */ | |
| 2229 | + int bSeparateDandP = 0; /* p_rid & d_rid both exist and are distinct */ | |
| 2238 | 2230 | |
| 2239 | 2231 | tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS; |
| 2240 | - if( p_rid && d_rid ){ | |
| 2241 | - if( p_rid!=d_rid ) p_rid = d_rid; | |
| 2242 | - if( !haveParameterN ) nEntry = 10; | |
| 2232 | + if( p_rid && d_rid && p_rid!=d_rid ){ | |
| 2233 | + bSeparateDandP = 1; | |
| 2234 | + db_multi_exec( | |
| 2235 | + "CREATE TEMP TABLE IF NOT EXISTS ok_d(rid INTEGER PRIMARY KEY)" | |
| 2236 | + ); | |
| 2237 | + }else{ | |
| 2238 | + zBaseName = p_rid ? zDPNameP : zDPNameD; | |
| 2243 | 2239 | } |
| 2244 | 2240 | db_multi_exec( |
| 2245 | - "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)" | |
| 2241 | + "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)" | |
| 2246 | 2242 | ); |
| 2247 | 2243 | add_extra_rids("ok", P("x")); |
| 2248 | - zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", | |
| 2249 | - p_rid ? p_rid : d_rid); | |
| 2250 | - zCiName = zDPName; | |
| 2251 | - if( zCiName==0 ) zCiName = zUuid; | |
| 2252 | 2244 | blob_append_sql(&sql, " AND event.objid IN ok"); |
| 2253 | 2245 | nd = 0; |
| 2254 | 2246 | if( d_rid ){ |
| 2255 | 2247 | double rStopTime = 9e99; |
| 2256 | 2248 | zFwdTo = P("ft"); |
| 2249 | + if( zFwdTo && bSeparateDandP ){ | |
| 2250 | + if( zError==0 ){ | |
| 2251 | + zError = "Cannot use the ft= query parameter when both p= and d= " | |
| 2252 | + "are used and have distinct values."; | |
| 2253 | + } | |
| 2254 | + zFwdTo = 0; | |
| 2255 | + } | |
| 2257 | 2256 | if( zFwdTo ){ |
| 2258 | - double rStartDate = db_double(0.0, | |
| 2259 | - "SELECT mtime FROM event WHERE objid=%d", d_rid); | |
| 2257 | + double rStartDate = mtime_of_rid(d_rid, 0.0); | |
| 2260 | 2258 | ridFwdTo = first_checkin_with_tag_after_date(zFwdTo, rStartDate); |
| 2261 | 2259 | if( ridFwdTo==0 ){ |
| 2262 | 2260 | ridFwdTo = name_to_typed_rid(zBackTo,"ci"); |
| 2263 | 2261 | } |
| 2264 | 2262 | if( ridFwdTo ){ |
| 2265 | 2263 | if( !haveParameterN ) nEntry = 0; |
| 2266 | - rStopTime = db_double(9e99, | |
| 2267 | - "SELECT mtime FROM event WHERE objid=%d", ridFwdTo); | |
| 2264 | + rStopTime = mtime_of_rid(ridFwdTo, 9e99); | |
| 2268 | 2265 | } |
| 2266 | + }else if( bSeparateDandP ){ | |
| 2267 | + rStopTime = mtime_of_rid(p_rid, 9e99); | |
| 2268 | + nEntry = 0; | |
| 2269 | 2269 | } |
| 2270 | 2270 | if( rStopTime<9e99 ){ |
| 2271 | 2271 | rStopTime += 5.8e-6; /* Round up by 1/2 second */ |
| 2272 | 2272 | } |
| 2273 | 2273 | db_multi_exec( |
| @@ -2280,72 +2280,111 @@ | ||
| 2280 | 2280 | " ORDER BY 2\n" |
| 2281 | 2281 | ")\n" |
| 2282 | 2282 | "INSERT OR IGNORE INTO ok SELECT rid FROM dx LIMIT %d", |
| 2283 | 2283 | d_rid, rStopTime<8e99 ? 17 : 2, rStopTime, nEntry<=0 ? -1 : nEntry+1 |
| 2284 | 2284 | ); |
| 2285 | - nd = db_int(0, "SELECT count(*)-1 FROM ok"); | |
| 2286 | - if( nd>=0 ) db_multi_exec("%s", blob_sql_text(&sql)); | |
| 2287 | - if( nd>0 || p_rid==0 ){ | |
| 2288 | - blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s"); | |
| 2285 | + if( ridFwdTo && !db_exists("SELECT 1 FROM ok WHERE rid=%d",ridFwdTo) ){ | |
| 2286 | + db_multi_exec("INSERT OR IGNORE INTO ok VALUES(%d)", ridFwdTo); | |
| 2287 | + bFwdAdded = 1; | |
| 2289 | 2288 | } |
| 2290 | - if( useDividers && !selectedRid ) selectedRid = d_rid; | |
| 2291 | - db_multi_exec("DELETE FROM ok"); | |
| 2289 | + if( bSeparateDandP ){ | |
| 2290 | + db_multi_exec( | |
| 2291 | + "INSERT INTO ok_d SELECT rid FROM ok;" | |
| 2292 | + "DELETE FROM ok;" | |
| 2293 | + ); | |
| 2294 | + }else{ | |
| 2295 | + nd = db_int(0, "SELECT count(*)-1 FROM ok"); | |
| 2296 | + if( nd>=0 ) db_multi_exec("%s", blob_sql_text(&sql)); | |
| 2297 | + if( nd>0 || p_rid==0 ){ | |
| 2298 | + blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s"); | |
| 2299 | + } | |
| 2300 | + if( useDividers && !selectedRid ) selectedRid = d_rid; | |
| 2301 | + db_multi_exec("DELETE FROM ok"); | |
| 2302 | + } | |
| 2292 | 2303 | } |
| 2293 | 2304 | if( p_rid ){ |
| 2294 | 2305 | zBackTo = P("bt"); |
| 2306 | + if( zBackTo && bSeparateDandP ){ | |
| 2307 | + if( zError==0 ){ | |
| 2308 | + zError = "Cannot use the bt= query parameter when both p= and d= " | |
| 2309 | + "are used and have distinct values."; | |
| 2310 | + } | |
| 2311 | + zBackTo = 0; | |
| 2312 | + } | |
| 2295 | 2313 | if( zBackTo ){ |
| 2296 | - double rDateLimit = db_double(0.0, | |
| 2297 | - "SELECT mtime FROM event WHERE objid=%d", p_rid); | |
| 2314 | + double rDateLimit = mtime_of_rid(p_rid, 0.0); | |
| 2298 | 2315 | ridBackTo = last_checkin_with_tag_before_date(zBackTo, rDateLimit); |
| 2299 | 2316 | if( ridBackTo==0 ){ |
| 2300 | 2317 | ridBackTo = name_to_typed_rid(zBackTo,"ci"); |
| 2301 | 2318 | } |
| 2302 | 2319 | if( ridBackTo && !haveParameterN ) nEntry = 0; |
| 2320 | + }else if( bSeparateDandP ){ | |
| 2321 | + ridBackTo = d_rid; | |
| 2322 | + nEntry = 0; | |
| 2303 | 2323 | } |
| 2304 | 2324 | compute_ancestors(p_rid, nEntry==0 ? 0 : nEntry+1, 0, ridBackTo); |
| 2305 | - np = db_int(0, "SELECT count(*)-1 FROM ok"); | |
| 2306 | - if( np>0 || nd==0 ){ | |
| 2307 | - if( nd>0 ) blob_appendf(&desc, " and "); | |
| 2308 | - blob_appendf(&desc, "%d ancestor%s", np, (1==np)?"":"s"); | |
| 2325 | + if( ridBackTo && !db_exists("SELECT 1 FROM ok WHERE rid=%d",ridBackTo) ){ | |
| 2326 | + db_multi_exec("INSERT OR IGNORE INTO ok VALUES(%d)", ridBackTo); | |
| 2327 | + bBackAdded = 1; | |
| 2328 | + } | |
| 2329 | + if( bSeparateDandP ){ | |
| 2330 | + db_multi_exec("DELETE FROM ok WHERE rid NOT IN ok_d;"); | |
| 2309 | 2331 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 2332 | + }else{ | |
| 2333 | + np = db_int(0, "SELECT count(*)-1 FROM ok"); | |
| 2334 | + if( np>0 || nd==0 ){ | |
| 2335 | + if( nd>0 ) blob_appendf(&desc, " and "); | |
| 2336 | + blob_appendf(&desc, "%d ancestor%s", np, (1==np)?"":"s"); | |
| 2337 | + db_multi_exec("%s", blob_sql_text(&sql)); | |
| 2338 | + } | |
| 2339 | + if( useDividers && !selectedRid ) selectedRid = p_rid; | |
| 2310 | 2340 | } |
| 2311 | - if( useDividers && !selectedRid ) selectedRid = p_rid; | |
| 2312 | 2341 | } |
| 2313 | - | |
| 2314 | - blob_appendf(&desc, " of %z%h</a>", | |
| 2315 | - href("%R/info?name=%h", zCiName), zCiName); | |
| 2342 | + if( bSeparateDandP ){ | |
| 2343 | + int n = db_int(0, "SELECT count(*) FROM ok"); | |
| 2344 | + blob_reset(&desc); | |
| 2345 | + blob_appendf(&desc, | |
| 2346 | + "%d check-ins that are both ancestors of %z%h</a>" | |
| 2347 | + " and descendants of %z%h</a>", | |
| 2348 | + n, | |
| 2349 | + href("%R/info?name=%h",zDPNameP),zDPNameP, | |
| 2350 | + href("%R/info?name=%h",zDPNameD),zDPNameD | |
| 2351 | + ); | |
| 2352 | + ridBackTo = 0; | |
| 2353 | + ridFwdTo = 0; | |
| 2354 | + }else{ | |
| 2355 | + blob_appendf(&desc, " of %z%h</a>", | |
| 2356 | + href("%R/info?name=%h", zBaseName), zBaseName); | |
| 2357 | + } | |
| 2316 | 2358 | if( ridBackTo ){ |
| 2317 | 2359 | if( np==0 ){ |
| 2318 | 2360 | blob_reset(&desc); |
| 2319 | 2361 | blob_appendf(&desc, |
| 2320 | - "Check-in %z%h</a> only (%z%h</a> is not an ancestor)", | |
| 2321 | - href("%R/info?name=%h",zCiName), zCiName, | |
| 2362 | + "Check-in %z%h</a> only (%z%h</a> does not precede it)", | |
| 2363 | + href("%R/info?name=%h",zBaseName), zBaseName, | |
| 2322 | 2364 | href("%R/info?name=%h",zBackTo), zBackTo); |
| 2323 | 2365 | }else{ |
| 2324 | - blob_appendf(&desc, " back to %z%h</a>", | |
| 2325 | - href("%R/info?name=%h",zBackTo), zBackTo); | |
| 2366 | + blob_appendf(&desc, " back to %z%h</a>%s", | |
| 2367 | + href("%R/info?name=%h",zBackTo), zBackTo, | |
| 2368 | + bBackAdded ? " (not a direct anscestor)" : ""); | |
| 2326 | 2369 | if( ridFwdTo && zFwdTo ){ |
| 2327 | - blob_appendf(&desc, " and up to %z%h</a>", | |
| 2328 | - href("%R/info?name=%h",zFwdTo), zFwdTo); | |
| 2370 | + blob_appendf(&desc, " and up to %z%h</a>%s", | |
| 2371 | + href("%R/info?name=%h",zFwdTo), zFwdTo, | |
| 2372 | + bFwdAdded ? " (not a direct descendant)" : ""); | |
| 2329 | 2373 | } |
| 2330 | 2374 | } |
| 2331 | 2375 | }else if( ridFwdTo ){ |
| 2332 | 2376 | if( nd==0 ){ |
| 2333 | 2377 | blob_reset(&desc); |
| 2334 | 2378 | blob_appendf(&desc, |
| 2335 | - "Check-in %z%h</a> only (%z%h</a> is not an descendant)", | |
| 2336 | - href("%R/info?name=%h",zCiName), zCiName, | |
| 2379 | + "Check-in %z%h</a> only (%z%h</a> does not follow it)", | |
| 2380 | + href("%R/info?name=%h",zBaseName), zBaseName, | |
| 2337 | 2381 | href("%R/info?name=%h",zFwdTo), zFwdTo); |
| 2338 | 2382 | }else{ |
| 2339 | - blob_appendf(&desc, " up to %z%h</a>", | |
| 2340 | - href("%R/info?name=%h",zFwdTo), zFwdTo); | |
| 2341 | - } | |
| 2342 | - } | |
| 2343 | - if( d_rid ){ | |
| 2344 | - if( p_rid ){ | |
| 2345 | - /* If both p= and d= are set, we don't have the uuid of d yet. */ | |
| 2346 | - zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", d_rid); | |
| 2383 | + blob_appendf(&desc, " up to %z%h</a>%s", | |
| 2384 | + href("%R/info?name=%h",zFwdTo), zFwdTo, | |
| 2385 | + bFwdAdded ? " (not a direct descendant)":""); | |
| 2347 | 2386 | } |
| 2348 | 2387 | } |
| 2349 | 2388 | if( advancedMenu ){ |
| 2350 | 2389 | style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0); |
| 2351 | 2390 | } |
| @@ -2815,13 +2854,13 @@ | ||
| 2815 | 2854 | blob_append_sql(&cond, |
| 2816 | 2855 | " AND (event.comment LIKE '%%%q%%' OR event.brief LIKE '%%%q%%')", |
| 2817 | 2856 | zSearch, zSearch); |
| 2818 | 2857 | } |
| 2819 | 2858 | } |
| 2820 | - rBefore = symbolic_name_to_mtime(zBefore, &zBefore); | |
| 2821 | - rAfter = symbolic_name_to_mtime(zAfter, &zAfter); | |
| 2822 | - rCirca = symbolic_name_to_mtime(zCirca, &zCirca); | |
| 2859 | + rBefore = symbolic_name_to_mtime(zBefore, &zBefore, 1); | |
| 2860 | + rAfter = symbolic_name_to_mtime(zAfter, &zAfter, 0); | |
| 2861 | + rCirca = symbolic_name_to_mtime(zCirca, &zCirca, 0); | |
| 2823 | 2862 | blob_append_sql(&sql, "%s", blob_sql_text(&cond)); |
| 2824 | 2863 | if( rAfter>0.0 ){ |
| 2825 | 2864 | if( rBefore>0.0 ){ |
| 2826 | 2865 | blob_append_sql(&sql, |
| 2827 | 2866 | " AND event.mtime>=%.17g AND event.mtime<=%.17g\n" |
| @@ -2958,11 +2997,11 @@ | ||
| 2958 | 2997 | zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/"); |
| 2959 | 2998 | if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){ |
| 2960 | 2999 | zDate = mprintf("%s", (zAfter ? zAfter : zBefore)); |
| 2961 | 3000 | } |
| 2962 | 3001 | if( zDate ){ |
| 2963 | - rDate = symbolic_name_to_mtime(zDate, 0); | |
| 3002 | + rDate = symbolic_name_to_mtime(zDate, 0, 0); | |
| 2964 | 3003 | if( db_int(0, |
| 2965 | 3004 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 2966 | 3005 | " WHERE blob.rid=event.objid AND mtime<=%.17g%s)", |
| 2967 | 3006 | rDate-ONE_SECOND, blob_sql_text(&cond)) |
| 2968 | 3007 | ){ |
| @@ -2974,11 +3013,11 @@ | ||
| 2974 | 3013 | zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/"); |
| 2975 | 3014 | if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){ |
| 2976 | 3015 | zDate = mprintf("%s", (zBefore ? zBefore : zAfter)); |
| 2977 | 3016 | } |
| 2978 | 3017 | if( zDate ){ |
| 2979 | - rDate = symbolic_name_to_mtime(zDate, 0); | |
| 3018 | + rDate = symbolic_name_to_mtime(zDate, 0, 0); | |
| 2980 | 3019 | if( db_int(0, |
| 2981 | 3020 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 2982 | 3021 | " WHERE blob.rid=event.objid AND mtime>=%.17g%s)", |
| 2983 | 3022 | rDate+ONE_SECOND, blob_sql_text(&cond)) |
| 2984 | 3023 | ){ |
| @@ -3017,11 +3056,11 @@ | ||
| 3017 | 3056 | style_submenu_element("Advanced", "%s", |
| 3018 | 3057 | url_render(&url, "advm", "1", "udc", "1")); |
| 3019 | 3058 | } |
| 3020 | 3059 | if( PB("showid") ) tmFlags |= TIMELINE_SHOWRID; |
| 3021 | 3060 | if( useDividers && zMark && zMark[0] ){ |
| 3022 | - double r = symbolic_name_to_mtime(zMark, 0); | |
| 3061 | + double r = symbolic_name_to_mtime(zMark, 0, 0); | |
| 3023 | 3062 | if( r>0.0 && !selectedRid ) selectedRid = timeline_add_divider(r); |
| 3024 | 3063 | } |
| 3025 | 3064 | blob_zero(&sql); |
| 3026 | 3065 | if( PB("oldestfirst") ){ |
| 3027 | 3066 | db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby ASC /*scan*/"); |
| @@ -3383,22 +3422,56 @@ | ||
| 3383 | 3422 | fossil_print("+++ no more data (%d) +++\n", nEntry); |
| 3384 | 3423 | } |
| 3385 | 3424 | } |
| 3386 | 3425 | if( fchngQueryInit ) db_finalize(&fchngQuery); |
| 3387 | 3426 | } |
| 3427 | + | |
| 3428 | +/* | |
| 3429 | +** wiki_to_text(TEXT) | |
| 3430 | +** | |
| 3431 | +** Return a text rendering of Fossil-Wiki TEXT, intended for display | |
| 3432 | +** on a timeline. The timeline-plaintext and timeline-hard-newlines | |
| 3433 | +** settings are considered when doing this rendering. | |
| 3434 | +*/ | |
| 3435 | +static void wiki_to_text_sqlfunc( | |
| 3436 | + sqlite3_context *context, | |
| 3437 | + int argc, | |
| 3438 | + sqlite3_value **argv | |
| 3439 | +){ | |
| 3440 | + const char *zIn, *zOut; | |
| 3441 | + int nIn, nOut; | |
| 3442 | + Blob in, html, txt; | |
| 3443 | + zIn = (const char*)sqlite3_value_text(argv[0]); | |
| 3444 | + if( zIn==0 ) return; | |
| 3445 | + nIn = sqlite3_value_bytes(argv[0]); | |
| 3446 | + blob_init(&in, zIn, nIn); | |
| 3447 | + blob_init(&html, 0, 0); | |
| 3448 | + wiki_convert(&in, &html, wiki_convert_flags(0)); | |
| 3449 | + blob_reset(&in); | |
| 3450 | + blob_init(&txt, 0, 0); | |
| 3451 | + html_to_plaintext(blob_str(&html), &txt, 0); | |
| 3452 | + blob_reset(&html); | |
| 3453 | + nOut = blob_size(&txt); | |
| 3454 | + zOut = blob_str(&txt); | |
| 3455 | + while( fossil_isspace(zOut[0]) ){ zOut++; nOut--; } | |
| 3456 | + while( nOut>0 && fossil_isspace(zOut[nOut-1]) ){ nOut--; } | |
| 3457 | + sqlite3_result_text(context, zOut, nOut, SQLITE_TRANSIENT); | |
| 3458 | + blob_reset(&txt); | |
| 3459 | +} | |
| 3388 | 3460 | |
| 3389 | 3461 | /* |
| 3390 | 3462 | ** Return a pointer to a static string that forms the basis for |
| 3391 | 3463 | ** a timeline query for display on a TTY. |
| 3392 | 3464 | */ |
| 3393 | 3465 | const char *timeline_query_for_tty(void){ |
| 3466 | + static int once = 0; | |
| 3394 | 3467 | static const char zBaseSql[] = |
| 3395 | 3468 | @ SELECT |
| 3396 | 3469 | @ blob.rid AS rid, |
| 3397 | 3470 | @ uuid, |
| 3398 | 3471 | @ datetime(event.mtime,toLocal()) AS mDateTime, |
| 3399 | - @ coalesce(ecomment,comment) | |
| 3472 | + @ wiki_to_text(coalesce(ecomment,comment)) | |
| 3400 | 3473 | @ || ' (user: ' || coalesce(euser,user,'?') |
| 3401 | 3474 | @ || (SELECT case when length(x)>0 then ' tags: ' || x else '' end |
| 3402 | 3475 | @ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x |
| 3403 | 3476 | @ FROM tag, tagxref |
| 3404 | 3477 | @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid |
| @@ -3422,10 +3495,15 @@ | ||
| 3422 | 3495 | @ AND tagxref.tagtype>0 |
| 3423 | 3496 | @ AND tagxref.rid=blob.rid |
| 3424 | 3497 | @ WHERE blob.rid=event.objid |
| 3425 | 3498 | @ AND tag.tagname='branch' |
| 3426 | 3499 | ; |
| 3500 | + if( !once && g.db ){ | |
| 3501 | + once = 1; | |
| 3502 | + sqlite3_create_function(g.db, "wiki_to_text", 1, SQLITE_UTF8, 0, | |
| 3503 | + wiki_to_text_sqlfunc, 0, 0); | |
| 3504 | + } | |
| 3427 | 3505 | return zBaseSql; |
| 3428 | 3506 | } |
| 3429 | 3507 | |
| 3430 | 3508 | /* |
| 3431 | 3509 | ** Return true if the input string is a date in the ISO 8601 format: |
| 3432 | 3510 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -205,11 +205,10 @@ | |
| 205 | char zPrevDate[20]; |
| 206 | GraphContext *pGraph = 0; |
| 207 | int prevWasDivider = 0; /* True if previous output row was <hr> */ |
| 208 | int fchngQueryInit = 0; /* True if fchngQuery is initialized */ |
| 209 | Stmt fchngQuery; /* Query for file changes on check-ins */ |
| 210 | static Stmt qbranch; |
| 211 | int pendingEndTr = 0; /* True if a </td></tr> is needed */ |
| 212 | int vid = 0; /* Current check-out version */ |
| 213 | int dateFormat = 0; /* 0: HH:MM (default) */ |
| 214 | int bCommentGitStyle = 0; /* Only show comments through first blank line */ |
| 215 | const char *zStyle; /* Sub-name for classes for the style */ |
| @@ -222,11 +221,28 @@ | |
| 222 | vid = db_lget_int("checkout", 0); |
| 223 | } |
| 224 | zPrevDate[0] = 0; |
| 225 | mxWikiLen = db_get_int("timeline-max-comment", 0); |
| 226 | dateFormat = db_get_int("timeline-date-format", 0); |
| 227 | bCommentGitStyle = db_get_int("timeline-truncate-at-blank", 0); |
| 228 | bTimestampLinksToInfo = db_get_boolean("timeline-tslink-info", 0); |
| 229 | if( (tmFlags & TIMELINE_VIEWS)==0 ){ |
| 230 | tmFlags |= timeline_ss_cookie(); |
| 231 | } |
| 232 | if( tmFlags & TIMELINE_COLUMNAR ){ |
| @@ -243,14 +259,10 @@ | |
| 243 | zDateFmt = P("datefmt"); |
| 244 | if( zDateFmt ) dateFormat = atoi(zDateFmt); |
| 245 | if( tmFlags & TIMELINE_GRAPH ){ |
| 246 | pGraph = graph_init(); |
| 247 | } |
| 248 | db_static_prepare(&qbranch, |
| 249 | "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid", |
| 250 | TAG_BRANCH |
| 251 | ); |
| 252 | if( (tmFlags & TIMELINE_CHPICK)!=0 |
| 253 | && !db_table_exists("repository","cherrypick") |
| 254 | ){ |
| 255 | tmFlags &= ~TIMELINE_CHPICK; |
| 256 | } |
| @@ -266,11 +278,11 @@ | |
| 266 | const char *zType = db_column_text(pQuery, 7); |
| 267 | const char *zUser = db_column_text(pQuery, 4); |
| 268 | const char *zTagList = db_column_text(pQuery, 8); |
| 269 | int tagid = db_column_int(pQuery, 9); |
| 270 | const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous"; |
| 271 | const char *zBr = 0; /* Branch */ |
| 272 | int commentColumn = 3; /* Column containing comment text */ |
| 273 | int modPending; /* Pending moderation */ |
| 274 | char *zDateLink; /* URL for the link on the timestamp */ |
| 275 | int drawDetailEllipsis; /* True to show ellipsis in place of detail */ |
| 276 | int gidx = 0; /* Graph row identifier */ |
| @@ -390,10 +402,12 @@ | |
| 390 | zDateLink = mprintf("<a>"); |
| 391 | } |
| 392 | @ <td class="timelineTime">%z(zDateLink)%s(zTime)</a></td> |
| 393 | @ <td class="timelineGraph"> |
| 394 | if( tmFlags & (TIMELINE_UCOLOR|TIMELINE_DELTA|TIMELINE_NOCOLOR) ){ |
| 395 | if( tmFlags & TIMELINE_UCOLOR ){ |
| 396 | zBgClr = zUser ? user_color(zUser) : 0; |
| 397 | }else if( tmFlags & TIMELINE_NOCOLOR ){ |
| 398 | zBgClr = 0; |
| 399 | }else if( zType[0]=='c' ){ |
| @@ -408,22 +422,21 @@ | |
| 408 | }else{ |
| 409 | zBgClr = hash_color("f"); /* delta manifest */ |
| 410 | } |
| 411 | db_reset(&qdelta); |
| 412 | } |
| 413 | } |
| 414 | if( zType[0]=='c' |
| 415 | && (pGraph || zBgClr==0 || (tmFlags & (TIMELINE_BRCOLOR|TIMELINE_DELTA))!=0) |
| 416 | ){ |
| 417 | db_reset(&qbranch); |
| 418 | db_bind_int(&qbranch, ":rid", rid); |
| 419 | if( db_step(&qbranch)==SQLITE_ROW ){ |
| 420 | zBr = db_column_text(&qbranch, 0); |
| 421 | }else{ |
| 422 | zBr = "trunk"; |
| 423 | } |
| 424 | if( zBgClr==0 || (tmFlags & TIMELINE_BRCOLOR)!=0 ){ |
| 425 | if( tmFlags & (TIMELINE_DELTA|TIMELINE_NOCOLOR) ){ |
| 426 | }else if( zBr==0 || strcmp(zBr,"trunk")==0 ){ |
| 427 | zBgClr = 0; |
| 428 | }else{ |
| 429 | zBgClr = hash_color(zBr); |
| @@ -459,19 +472,19 @@ | |
| 459 | db_reset(&qcherrypick); |
| 460 | } |
| 461 | gidx = graph_add_row(pGraph, rid, nParent, nCherrypick, aParent, |
| 462 | zBr, zBgClr, zUuid, |
| 463 | isLeaf ? isLeaf + 2 * has_closed_tag(rid) : 0); |
| 464 | db_reset(&qbranch); |
| 465 | @ <div id="m%d(gidx)" class="tl-nodemark"></div> |
| 466 | }else if( zType[0]=='e' && pGraph && zBgClr && zBgClr[0] ){ |
| 467 | /* For technotes, make a graph node with nParent==(-1). This will |
| 468 | ** not actually draw anything on the graph, but it will set the |
| 469 | ** background color of the timeline entry */ |
| 470 | gidx = graph_add_row(pGraph, rid, -1, 0, 0, zBr, zBgClr, zUuid, 0); |
| 471 | @ <div id="m%d(gidx)" class="tl-nodemark"></div> |
| 472 | } |
| 473 | @</td> |
| 474 | if( !isSelectedOrCurrent ){ |
| 475 | @ <td class="timeline%s(zStyle)Cell%s(zExtraClass)" id='mc%d(gidx)'> |
| 476 | }else{ |
| 477 | @ <td class="timeline%s(zStyle)Cell%s(zExtraClass)"> |
| @@ -1100,47 +1113,10 @@ | |
| 1100 | @ WHERE blob.rid=event.objid |
| 1101 | ; |
| 1102 | return zBase; |
| 1103 | } |
| 1104 | |
| 1105 | /* |
| 1106 | ** Convert a symbolic name used as an argument to the a=, b=, or c= |
| 1107 | ** query parameters of timeline into a julianday mtime value. |
| 1108 | */ |
| 1109 | double symbolic_name_to_mtime(const char *z, const char **pzDisplay){ |
| 1110 | double mtime; |
| 1111 | int rid; |
| 1112 | const char *zDate; |
| 1113 | if( z==0 ) return -1.0; |
| 1114 | if( fossil_isdate(z) ){ |
| 1115 | mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", z); |
| 1116 | if( mtime>0.0 ) return mtime; |
| 1117 | } |
| 1118 | zDate = fossil_expand_datetime(z, 1); |
| 1119 | if( zDate!=0 ){ |
| 1120 | mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", |
| 1121 | fossil_roundup_date(zDate)); |
| 1122 | if( mtime>0.0 ){ |
| 1123 | if( pzDisplay ) *pzDisplay = fossil_strdup(zDate); |
| 1124 | return mtime; |
| 1125 | } |
| 1126 | } |
| 1127 | rid = symbolic_name_to_rid(z, "*"); |
| 1128 | if( rid ){ |
| 1129 | mtime = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid); |
| 1130 | }else{ |
| 1131 | mtime = db_double(-1.0, |
| 1132 | "SELECT max(event.mtime) FROM event, tag, tagxref" |
| 1133 | " WHERE tag.tagname GLOB 'event-%q*'" |
| 1134 | " AND tagxref.tagid=tag.tagid AND tagxref.tagtype" |
| 1135 | " AND event.objid=tagxref.rid", |
| 1136 | z |
| 1137 | ); |
| 1138 | } |
| 1139 | return mtime; |
| 1140 | } |
| 1141 | |
| 1142 | /* |
| 1143 | ** zDate is a localtime date. Insert records into the |
| 1144 | ** "timeline" table to cause <hr> to be inserted on zDate. |
| 1145 | */ |
| 1146 | static int timeline_add_divider(double rDate){ |
| @@ -1392,11 +1368,11 @@ | |
| 1392 | ** return 0. |
| 1393 | */ |
| 1394 | static int timeline_endpoint( |
| 1395 | int iFrom, /* Starting point */ |
| 1396 | const char *zEnd, /* Tag we are searching for */ |
| 1397 | int bForward /* 1: forwards in time (descendents) 0: backwards */ |
| 1398 | ){ |
| 1399 | int tagId; |
| 1400 | int endId = 0; |
| 1401 | Stmt q; |
| 1402 | int ans = 0; |
| @@ -1419,11 +1395,12 @@ | |
| 1419 | " AND plink.mtime<=(SELECT max(event.mtime) FROM tagxref, event" |
| 1420 | " WHERE tagxref.tagid=%d AND tagxref.tagtype>0" |
| 1421 | " AND event.objid=tagxref.rid)" |
| 1422 | " ORDER BY plink.mtime)" |
| 1423 | "SELECT id FROM dx, tagxref" |
| 1424 | " WHERE tagid=%d AND tagtype>0 AND rid=id LIMIT 1", |
| 1425 | iFrom, iFrom, tagId, tagId |
| 1426 | ); |
| 1427 | }else{ |
| 1428 | db_prepare(&q, |
| 1429 | "WITH RECURSIVE dx(id,mtime) AS (" |
| @@ -1450,11 +1427,12 @@ | |
| 1450 | " AND event.mtime>=(SELECT min(event.mtime) FROM tagxref, event" |
| 1451 | " WHERE tagxref.tagid=%d AND tagxref.tagtype>0" |
| 1452 | " AND event.objid=tagxref.rid)" |
| 1453 | " ORDER BY event.mtime DESC)" |
| 1454 | "SELECT id FROM dx, tagxref" |
| 1455 | " WHERE tagid=%d AND tagtype>0 AND rid=id LIMIT 1", |
| 1456 | iFrom, iFrom, tagId, tagId |
| 1457 | ); |
| 1458 | }else{ |
| 1459 | db_prepare(&q, |
| 1460 | "WITH RECURSIVE dx(id,mtime) AS (" |
| @@ -1522,17 +1500,17 @@ | |
| 1522 | /* |
| 1523 | ** COMMAND: test-endpoint |
| 1524 | ** |
| 1525 | ** Usage: fossil test-endpoint BASE TAG ?OPTIONS? |
| 1526 | ** |
| 1527 | ** Show the first check-in with TAG that is a descendent or ancestor |
| 1528 | ** of BASE. The first descendent checkin is shown by default. Use |
| 1529 | ** the --backto to see the first ancestor checkin. |
| 1530 | ** |
| 1531 | ** Options: |
| 1532 | ** |
| 1533 | ** --backto Show ancestor. Others defaults to descendents. |
| 1534 | */ |
| 1535 | void timeline_test_endpoint(void){ |
| 1536 | int bForward = find_option("backto",0,0)==0; |
| 1537 | int from_rid; |
| 1538 | int ans; |
| @@ -1583,14 +1561,14 @@ | |
| 1583 | ** dp=CHECKIN Same as 'd=CHECKIN&p=CHECKIN' |
| 1584 | ** dp2=CKIN2 Same as 'd2=CKIN2&p2=CKIN2' |
| 1585 | ** df=CHECKIN Same as 'd=CHECKIN&n1=all&nd'. Mnemonic: "Derived From" |
| 1586 | ** bt=CHECKIN "Back To". Show ancenstors going back to CHECKIN |
| 1587 | ** p=CX ... from CX back to time of CHECKIN |
| 1588 | ** from=CX ... shortest path from CX back to CHECKIN |
| 1589 | ** ft=CHECKIN "Forward To": Show decendents forward to CHECKIN |
| 1590 | ** d=CX ... from CX up to the time of CHECKIN |
| 1591 | ** from=CX ... shortest path from CX up to CHECKIN |
| 1592 | ** t=TAG Show only check-ins with the given TAG |
| 1593 | ** r=TAG Same as 't=TAG&rel'. Mnemonic: "Related" |
| 1594 | ** tl=TAGLIST Same as 't=TAGLIST&ms=brlist'. Mnemonic: "Tag List" |
| 1595 | ** rl=TAGLIST Same as 'r=TAGLIST&ms=brlist'. Mnemonic: "Related List" |
| 1596 | ** ml=TAGLIST Same as 'tl=TAGLIST&mionly'. Mnemonic: "Merge-in List" |
| @@ -1611,20 +1589,21 @@ | |
| 1611 | ** nsm Omit the submenu |
| 1612 | ** nc Omit all graph colors other than highlights |
| 1613 | ** v Show details of files changed |
| 1614 | ** vfx Show complete text of forum messages |
| 1615 | ** f=CHECKIN Family (immediate parents and children) of CHECKIN |
| 1616 | ** from=CHECKIN Path through common ancestor from... |
| 1617 | ** to=CHECKIN ... to this |
| 1618 | ** to2=CHECKIN ... backup name if to= doesn't resolve |
| 1619 | ** shortest ... show only the shortest path |
| 1620 | ** rel ... also show related checkins |
| 1621 | ** bt=PRIOR ... path from CHECKIN back to PRIOR |
| 1622 | ** ft=LATER ... path from CHECKIN forward to LATER |
| 1623 | ** me=CHECKIN Most direct path from... |
| 1624 | ** you=CHECKIN ... to this |
| 1625 | ** rel ... also show related checkins |
| 1626 | ** uf=FILE_HASH Show only check-ins that contain the given file version |
| 1627 | ** All qualifying check-ins are shown unless there is |
| 1628 | ** also an n= or n1= query parameter. |
| 1629 | ** chng=GLOBLIST Show only check-ins that involve changes to a file whose |
| 1630 | ** name matches one of the comma-separate GLOBLIST |
| @@ -1707,15 +1686,15 @@ | |
| 1707 | const char *zThisUser = 0; /* Suppress links to this user */ |
| 1708 | HQuery url; /* URL for various branch links */ |
| 1709 | int from_rid = name_to_typed_rid(P("from"),"ci"); /* from= for paths */ |
| 1710 | const char *zTo2 = 0; |
| 1711 | int to_rid = name_choice("to","to2",&zTo2); /* to= for path timelines */ |
| 1712 | int noMerge = P("shortest")==0; /* Follow merge links if shorter */ |
| 1713 | int me_rid = name_to_typed_rid(P("me"),"ci"); /* me= for common ancestory */ |
| 1714 | int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */ |
| 1715 | int pd_rid; |
| 1716 | const char *zDPName; /* Value of p=, d=, or dp= params */ |
| 1717 | double rBefore, rAfter, rCirca; /* Boundary times */ |
| 1718 | const char *z; |
| 1719 | char *zOlderButton = 0; /* URL for Older button at the bottom */ |
| 1720 | char *zOlderButtonLabel = 0; /* Label for the Older Button */ |
| 1721 | char *zNewerButton = 0; /* URL for Newer button at the top */ |
| @@ -1728,10 +1707,11 @@ | |
| 1728 | int showCherrypicks = 1; /* True to show cherrypick merges */ |
| 1729 | int haveParameterN; /* True if n= query parameter present */ |
| 1730 | int from_to_mode = 0; /* 0: from,to. 1: from,ft 2: from,bt */ |
| 1731 | int showSql = PB("showsql"); /* True to show the SQL */ |
| 1732 | Blob allSql; /* Copy of all SQL text */ |
| 1733 | |
| 1734 | login_check_credentials(); |
| 1735 | url_initialize(&url, "timeline"); |
| 1736 | cgi_query_parameters_to_url(&url); |
| 1737 | blob_init(&allSql, 0, 0); |
| @@ -1777,20 +1757,20 @@ | |
| 1777 | }else{ |
| 1778 | nEntry = 50; |
| 1779 | } |
| 1780 | |
| 1781 | /* Query parameters d=, p=, and f= and variants */ |
| 1782 | p_rid = name_choice("p","p2", &zDPName); |
| 1783 | d_rid = name_choice("d","d2", &zDPName); |
| 1784 | z = P("f"); |
| 1785 | f_rid = z ? name_to_typed_rid(z,"ci") : 0; |
| 1786 | z = P("df"); |
| 1787 | if( z && (d_rid = name_to_typed_rid(z,"ci"))!=0 ){ |
| 1788 | nEntry = 0; |
| 1789 | useDividers = 0; |
| 1790 | cgi_replace_query_parameter("d",fossil_strdup(z)); |
| 1791 | zDPName = z; |
| 1792 | } |
| 1793 | |
| 1794 | /* Undocumented query parameter to set JS mode */ |
| 1795 | builtin_set_js_delivery_mode(P("jsmode"),1); |
| 1796 | |
| @@ -1810,13 +1790,14 @@ | |
| 1810 | showCherrypicks = 0; |
| 1811 | } |
| 1812 | |
| 1813 | /* To view the timeline, must have permission to read project data. |
| 1814 | */ |
| 1815 | pd_rid = name_choice("dp","dp2",&zDPName); |
| 1816 | if( pd_rid ){ |
| 1817 | p_rid = d_rid = pd_rid; |
| 1818 | } |
| 1819 | if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum) |
| 1820 | || (bisectLocal && !g.perm.Setup) |
| 1821 | ){ |
| 1822 | login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki); |
| @@ -2083,20 +2064,22 @@ | |
| 2083 | const char *zTo = 0; |
| 2084 | Blob ins; |
| 2085 | int nNodeOnPath = 0; |
| 2086 | int commonAncs = 0; /* Common ancestors of me_rid and you_rid. */ |
| 2087 | int earlierRid = 0, laterRid = 0; |
| 2088 | |
| 2089 | if( from_rid && to_rid ){ |
| 2090 | if( from_to_mode==0 ){ |
| 2091 | p = path_shortest(from_rid, to_rid, noMerge, 0, 0); |
| 2092 | }else if( from_to_mode==1 ){ |
| 2093 | p = path_shortest(from_rid, to_rid, 0, 1, 0); |
| 2094 | earlierRid = commonAncs = from_rid; |
| 2095 | laterRid = to_rid; |
| 2096 | }else{ |
| 2097 | p = path_shortest(to_rid, from_rid, 0, 1, 0); |
| 2098 | earlierRid = commonAncs = to_rid; |
| 2099 | laterRid = from_rid; |
| 2100 | } |
| 2101 | zFrom = P("from"); |
| 2102 | zTo = zTo2 ? zTo2 : P("to"); |
| @@ -2123,20 +2106,25 @@ | |
| 2123 | ); |
| 2124 | if( p ){ |
| 2125 | int cnt = 4; |
| 2126 | blob_init(&ins, 0, 0); |
| 2127 | blob_append_sql(&ins, "INSERT INTO pathnode(x) VALUES(%d)", p->rid); |
| 2128 | p = p->u.pTo; |
| 2129 | while( p ){ |
| 2130 | if( cnt==8 ){ |
| 2131 | blob_append_sql(&ins, ",\n (%d)", p->rid); |
| 2132 | cnt = 0; |
| 2133 | }else{ |
| 2134 | cnt++; |
| 2135 | blob_append_sql(&ins, ",(%d)", p->rid); |
| 2136 | } |
| 2137 | p = p->u.pTo; |
| 2138 | } |
| 2139 | } |
| 2140 | path_reset(); |
| 2141 | db_multi_exec("%s", blob_str(&ins)/*safe-for-%s*/); |
| 2142 | blob_reset(&ins); |
| @@ -2196,12 +2184,13 @@ | |
| 2196 | style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0); |
| 2197 | } |
| 2198 | nNodeOnPath = db_int(0, "SELECT count(*) FROM temp.pathnode"); |
| 2199 | if( nNodeOnPath==1 && from_to_mode>0 ){ |
| 2200 | blob_appendf(&desc,"Check-in "); |
| 2201 | }else if( from_to_mode>0 ){ |
| 2202 | blob_appendf(&desc, "%d check-ins on the shorted path from ",nNodeOnPath); |
| 2203 | }else{ |
| 2204 | blob_appendf(&desc, "%d check-ins going from ", nNodeOnPath); |
| 2205 | } |
| 2206 | if( from_rid==selectedRid ){ |
| 2207 | blob_appendf(&desc, "<span class='timelineSelected'>"); |
| @@ -2225,49 +2214,60 @@ | |
| 2225 | } |
| 2226 | } |
| 2227 | } |
| 2228 | addFileGlobDescription(zChng, &desc); |
| 2229 | }else if( (p_rid || d_rid) && g.perm.Read && zTagSql==0 ){ |
| 2230 | /* If p= or d= is present, ignore all other parameters other than n= */ |
| 2231 | char *zUuid; |
| 2232 | const char *zCiName; |
| 2233 | int np = 0, nd; |
| 2234 | const char *zBackTo = 0; |
| 2235 | const char *zFwdTo = 0; |
| 2236 | int ridBackTo = 0; |
| 2237 | int ridFwdTo = 0; |
| 2238 | |
| 2239 | tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS; |
| 2240 | if( p_rid && d_rid ){ |
| 2241 | if( p_rid!=d_rid ) p_rid = d_rid; |
| 2242 | if( !haveParameterN ) nEntry = 10; |
| 2243 | } |
| 2244 | db_multi_exec( |
| 2245 | "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)" |
| 2246 | ); |
| 2247 | add_extra_rids("ok", P("x")); |
| 2248 | zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", |
| 2249 | p_rid ? p_rid : d_rid); |
| 2250 | zCiName = zDPName; |
| 2251 | if( zCiName==0 ) zCiName = zUuid; |
| 2252 | blob_append_sql(&sql, " AND event.objid IN ok"); |
| 2253 | nd = 0; |
| 2254 | if( d_rid ){ |
| 2255 | double rStopTime = 9e99; |
| 2256 | zFwdTo = P("ft"); |
| 2257 | if( zFwdTo ){ |
| 2258 | double rStartDate = db_double(0.0, |
| 2259 | "SELECT mtime FROM event WHERE objid=%d", d_rid); |
| 2260 | ridFwdTo = first_checkin_with_tag_after_date(zFwdTo, rStartDate); |
| 2261 | if( ridFwdTo==0 ){ |
| 2262 | ridFwdTo = name_to_typed_rid(zBackTo,"ci"); |
| 2263 | } |
| 2264 | if( ridFwdTo ){ |
| 2265 | if( !haveParameterN ) nEntry = 0; |
| 2266 | rStopTime = db_double(9e99, |
| 2267 | "SELECT mtime FROM event WHERE objid=%d", ridFwdTo); |
| 2268 | } |
| 2269 | } |
| 2270 | if( rStopTime<9e99 ){ |
| 2271 | rStopTime += 5.8e-6; /* Round up by 1/2 second */ |
| 2272 | } |
| 2273 | db_multi_exec( |
| @@ -2280,72 +2280,111 @@ | |
| 2280 | " ORDER BY 2\n" |
| 2281 | ")\n" |
| 2282 | "INSERT OR IGNORE INTO ok SELECT rid FROM dx LIMIT %d", |
| 2283 | d_rid, rStopTime<8e99 ? 17 : 2, rStopTime, nEntry<=0 ? -1 : nEntry+1 |
| 2284 | ); |
| 2285 | nd = db_int(0, "SELECT count(*)-1 FROM ok"); |
| 2286 | if( nd>=0 ) db_multi_exec("%s", blob_sql_text(&sql)); |
| 2287 | if( nd>0 || p_rid==0 ){ |
| 2288 | blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s"); |
| 2289 | } |
| 2290 | if( useDividers && !selectedRid ) selectedRid = d_rid; |
| 2291 | db_multi_exec("DELETE FROM ok"); |
| 2292 | } |
| 2293 | if( p_rid ){ |
| 2294 | zBackTo = P("bt"); |
| 2295 | if( zBackTo ){ |
| 2296 | double rDateLimit = db_double(0.0, |
| 2297 | "SELECT mtime FROM event WHERE objid=%d", p_rid); |
| 2298 | ridBackTo = last_checkin_with_tag_before_date(zBackTo, rDateLimit); |
| 2299 | if( ridBackTo==0 ){ |
| 2300 | ridBackTo = name_to_typed_rid(zBackTo,"ci"); |
| 2301 | } |
| 2302 | if( ridBackTo && !haveParameterN ) nEntry = 0; |
| 2303 | } |
| 2304 | compute_ancestors(p_rid, nEntry==0 ? 0 : nEntry+1, 0, ridBackTo); |
| 2305 | np = db_int(0, "SELECT count(*)-1 FROM ok"); |
| 2306 | if( np>0 || nd==0 ){ |
| 2307 | if( nd>0 ) blob_appendf(&desc, " and "); |
| 2308 | blob_appendf(&desc, "%d ancestor%s", np, (1==np)?"":"s"); |
| 2309 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 2310 | } |
| 2311 | if( useDividers && !selectedRid ) selectedRid = p_rid; |
| 2312 | } |
| 2313 | |
| 2314 | blob_appendf(&desc, " of %z%h</a>", |
| 2315 | href("%R/info?name=%h", zCiName), zCiName); |
| 2316 | if( ridBackTo ){ |
| 2317 | if( np==0 ){ |
| 2318 | blob_reset(&desc); |
| 2319 | blob_appendf(&desc, |
| 2320 | "Check-in %z%h</a> only (%z%h</a> is not an ancestor)", |
| 2321 | href("%R/info?name=%h",zCiName), zCiName, |
| 2322 | href("%R/info?name=%h",zBackTo), zBackTo); |
| 2323 | }else{ |
| 2324 | blob_appendf(&desc, " back to %z%h</a>", |
| 2325 | href("%R/info?name=%h",zBackTo), zBackTo); |
| 2326 | if( ridFwdTo && zFwdTo ){ |
| 2327 | blob_appendf(&desc, " and up to %z%h</a>", |
| 2328 | href("%R/info?name=%h",zFwdTo), zFwdTo); |
| 2329 | } |
| 2330 | } |
| 2331 | }else if( ridFwdTo ){ |
| 2332 | if( nd==0 ){ |
| 2333 | blob_reset(&desc); |
| 2334 | blob_appendf(&desc, |
| 2335 | "Check-in %z%h</a> only (%z%h</a> is not an descendant)", |
| 2336 | href("%R/info?name=%h",zCiName), zCiName, |
| 2337 | href("%R/info?name=%h",zFwdTo), zFwdTo); |
| 2338 | }else{ |
| 2339 | blob_appendf(&desc, " up to %z%h</a>", |
| 2340 | href("%R/info?name=%h",zFwdTo), zFwdTo); |
| 2341 | } |
| 2342 | } |
| 2343 | if( d_rid ){ |
| 2344 | if( p_rid ){ |
| 2345 | /* If both p= and d= are set, we don't have the uuid of d yet. */ |
| 2346 | zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", d_rid); |
| 2347 | } |
| 2348 | } |
| 2349 | if( advancedMenu ){ |
| 2350 | style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0); |
| 2351 | } |
| @@ -2815,13 +2854,13 @@ | |
| 2815 | blob_append_sql(&cond, |
| 2816 | " AND (event.comment LIKE '%%%q%%' OR event.brief LIKE '%%%q%%')", |
| 2817 | zSearch, zSearch); |
| 2818 | } |
| 2819 | } |
| 2820 | rBefore = symbolic_name_to_mtime(zBefore, &zBefore); |
| 2821 | rAfter = symbolic_name_to_mtime(zAfter, &zAfter); |
| 2822 | rCirca = symbolic_name_to_mtime(zCirca, &zCirca); |
| 2823 | blob_append_sql(&sql, "%s", blob_sql_text(&cond)); |
| 2824 | if( rAfter>0.0 ){ |
| 2825 | if( rBefore>0.0 ){ |
| 2826 | blob_append_sql(&sql, |
| 2827 | " AND event.mtime>=%.17g AND event.mtime<=%.17g\n" |
| @@ -2958,11 +2997,11 @@ | |
| 2958 | zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/"); |
| 2959 | if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){ |
| 2960 | zDate = mprintf("%s", (zAfter ? zAfter : zBefore)); |
| 2961 | } |
| 2962 | if( zDate ){ |
| 2963 | rDate = symbolic_name_to_mtime(zDate, 0); |
| 2964 | if( db_int(0, |
| 2965 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 2966 | " WHERE blob.rid=event.objid AND mtime<=%.17g%s)", |
| 2967 | rDate-ONE_SECOND, blob_sql_text(&cond)) |
| 2968 | ){ |
| @@ -2974,11 +3013,11 @@ | |
| 2974 | zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/"); |
| 2975 | if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){ |
| 2976 | zDate = mprintf("%s", (zBefore ? zBefore : zAfter)); |
| 2977 | } |
| 2978 | if( zDate ){ |
| 2979 | rDate = symbolic_name_to_mtime(zDate, 0); |
| 2980 | if( db_int(0, |
| 2981 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 2982 | " WHERE blob.rid=event.objid AND mtime>=%.17g%s)", |
| 2983 | rDate+ONE_SECOND, blob_sql_text(&cond)) |
| 2984 | ){ |
| @@ -3017,11 +3056,11 @@ | |
| 3017 | style_submenu_element("Advanced", "%s", |
| 3018 | url_render(&url, "advm", "1", "udc", "1")); |
| 3019 | } |
| 3020 | if( PB("showid") ) tmFlags |= TIMELINE_SHOWRID; |
| 3021 | if( useDividers && zMark && zMark[0] ){ |
| 3022 | double r = symbolic_name_to_mtime(zMark, 0); |
| 3023 | if( r>0.0 && !selectedRid ) selectedRid = timeline_add_divider(r); |
| 3024 | } |
| 3025 | blob_zero(&sql); |
| 3026 | if( PB("oldestfirst") ){ |
| 3027 | db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby ASC /*scan*/"); |
| @@ -3383,22 +3422,56 @@ | |
| 3383 | fossil_print("+++ no more data (%d) +++\n", nEntry); |
| 3384 | } |
| 3385 | } |
| 3386 | if( fchngQueryInit ) db_finalize(&fchngQuery); |
| 3387 | } |
| 3388 | |
| 3389 | /* |
| 3390 | ** Return a pointer to a static string that forms the basis for |
| 3391 | ** a timeline query for display on a TTY. |
| 3392 | */ |
| 3393 | const char *timeline_query_for_tty(void){ |
| 3394 | static const char zBaseSql[] = |
| 3395 | @ SELECT |
| 3396 | @ blob.rid AS rid, |
| 3397 | @ uuid, |
| 3398 | @ datetime(event.mtime,toLocal()) AS mDateTime, |
| 3399 | @ coalesce(ecomment,comment) |
| 3400 | @ || ' (user: ' || coalesce(euser,user,'?') |
| 3401 | @ || (SELECT case when length(x)>0 then ' tags: ' || x else '' end |
| 3402 | @ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x |
| 3403 | @ FROM tag, tagxref |
| 3404 | @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid |
| @@ -3422,10 +3495,15 @@ | |
| 3422 | @ AND tagxref.tagtype>0 |
| 3423 | @ AND tagxref.rid=blob.rid |
| 3424 | @ WHERE blob.rid=event.objid |
| 3425 | @ AND tag.tagname='branch' |
| 3426 | ; |
| 3427 | return zBaseSql; |
| 3428 | } |
| 3429 | |
| 3430 | /* |
| 3431 | ** Return true if the input string is a date in the ISO 8601 format: |
| 3432 |
| --- src/timeline.c | |
| +++ src/timeline.c | |
| @@ -205,11 +205,10 @@ | |
| 205 | char zPrevDate[20]; |
| 206 | GraphContext *pGraph = 0; |
| 207 | int prevWasDivider = 0; /* True if previous output row was <hr> */ |
| 208 | int fchngQueryInit = 0; /* True if fchngQuery is initialized */ |
| 209 | Stmt fchngQuery; /* Query for file changes on check-ins */ |
| 210 | int pendingEndTr = 0; /* True if a </td></tr> is needed */ |
| 211 | int vid = 0; /* Current check-out version */ |
| 212 | int dateFormat = 0; /* 0: HH:MM (default) */ |
| 213 | int bCommentGitStyle = 0; /* Only show comments through first blank line */ |
| 214 | const char *zStyle; /* Sub-name for classes for the style */ |
| @@ -222,11 +221,28 @@ | |
| 221 | vid = db_lget_int("checkout", 0); |
| 222 | } |
| 223 | zPrevDate[0] = 0; |
| 224 | mxWikiLen = db_get_int("timeline-max-comment", 0); |
| 225 | dateFormat = db_get_int("timeline-date-format", 0); |
| 226 | /* |
| 227 | ** SETTING: timeline-truncate-at-blank boolean default=off |
| 228 | ** |
| 229 | ** If enabled, check-in comments displayed on the timeline are truncated |
| 230 | ** at the first blank line of the comment text. The comment text after |
| 231 | ** the first blank line is only seen in the /info or similar pages that |
| 232 | ** show details about the check-in. |
| 233 | */ |
| 234 | bCommentGitStyle = db_get_int("timeline-truncate-at-blank", 0); |
| 235 | /* |
| 236 | ** SETTING: timeline-tslink-info boolean default=off |
| 237 | ** |
| 238 | ** The hyperlink on the timestamp associated with each timeline entry, |
| 239 | ** on the far left-hand side of the screen, normally targets another |
| 240 | ** /timeline page that shows the entry in context. However, if this |
| 241 | ** option is turned on, that hyperlink targets the /info page showing |
| 242 | ** the details of the entry. |
| 243 | */ |
| 244 | bTimestampLinksToInfo = db_get_boolean("timeline-tslink-info", 0); |
| 245 | if( (tmFlags & TIMELINE_VIEWS)==0 ){ |
| 246 | tmFlags |= timeline_ss_cookie(); |
| 247 | } |
| 248 | if( tmFlags & TIMELINE_COLUMNAR ){ |
| @@ -243,14 +259,10 @@ | |
| 259 | zDateFmt = P("datefmt"); |
| 260 | if( zDateFmt ) dateFormat = atoi(zDateFmt); |
| 261 | if( tmFlags & TIMELINE_GRAPH ){ |
| 262 | pGraph = graph_init(); |
| 263 | } |
| 264 | if( (tmFlags & TIMELINE_CHPICK)!=0 |
| 265 | && !db_table_exists("repository","cherrypick") |
| 266 | ){ |
| 267 | tmFlags &= ~TIMELINE_CHPICK; |
| 268 | } |
| @@ -266,11 +278,11 @@ | |
| 278 | const char *zType = db_column_text(pQuery, 7); |
| 279 | const char *zUser = db_column_text(pQuery, 4); |
| 280 | const char *zTagList = db_column_text(pQuery, 8); |
| 281 | int tagid = db_column_int(pQuery, 9); |
| 282 | const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous"; |
| 283 | char *zBr = 0; /* Branch */ |
| 284 | int commentColumn = 3; /* Column containing comment text */ |
| 285 | int modPending; /* Pending moderation */ |
| 286 | char *zDateLink; /* URL for the link on the timestamp */ |
| 287 | int drawDetailEllipsis; /* True to show ellipsis in place of detail */ |
| 288 | int gidx = 0; /* Graph row identifier */ |
| @@ -390,10 +402,12 @@ | |
| 402 | zDateLink = mprintf("<a>"); |
| 403 | } |
| 404 | @ <td class="timelineTime">%z(zDateLink)%s(zTime)</a></td> |
| 405 | @ <td class="timelineGraph"> |
| 406 | if( tmFlags & (TIMELINE_UCOLOR|TIMELINE_DELTA|TIMELINE_NOCOLOR) ){ |
| 407 | /* Don't use the requested background color. Use the background color |
| 408 | ** override from query parameters instead. */ |
| 409 | if( tmFlags & TIMELINE_UCOLOR ){ |
| 410 | zBgClr = zUser ? user_color(zUser) : 0; |
| 411 | }else if( tmFlags & TIMELINE_NOCOLOR ){ |
| 412 | zBgClr = 0; |
| 413 | }else if( zType[0]=='c' ){ |
| @@ -408,22 +422,21 @@ | |
| 422 | }else{ |
| 423 | zBgClr = hash_color("f"); /* delta manifest */ |
| 424 | } |
| 425 | db_reset(&qdelta); |
| 426 | } |
| 427 | }else{ |
| 428 | /* Make sure the user-specified background color is reasonable */ |
| 429 | zBgClr = reasonable_bg_color(zBgClr, 0); |
| 430 | } |
| 431 | if( zType[0]=='c' |
| 432 | && (pGraph || zBgClr==0 || (tmFlags & (TIMELINE_BRCOLOR|TIMELINE_DELTA))!=0) |
| 433 | ){ |
| 434 | zBr = branch_of_rid(rid); |
| 435 | if( zBgClr==0 || (tmFlags & TIMELINE_BRCOLOR)!=0 ){ |
| 436 | /* If no background color is specified, use a color based on the |
| 437 | ** branch name */ |
| 438 | if( tmFlags & (TIMELINE_DELTA|TIMELINE_NOCOLOR) ){ |
| 439 | }else if( zBr==0 || strcmp(zBr,"trunk")==0 ){ |
| 440 | zBgClr = 0; |
| 441 | }else{ |
| 442 | zBgClr = hash_color(zBr); |
| @@ -459,19 +472,19 @@ | |
| 472 | db_reset(&qcherrypick); |
| 473 | } |
| 474 | gidx = graph_add_row(pGraph, rid, nParent, nCherrypick, aParent, |
| 475 | zBr, zBgClr, zUuid, |
| 476 | isLeaf ? isLeaf + 2 * has_closed_tag(rid) : 0); |
| 477 | @ <div id="m%d(gidx)" class="tl-nodemark"></div> |
| 478 | }else if( zType[0]=='e' && pGraph && zBgClr && zBgClr[0] ){ |
| 479 | /* For technotes, make a graph node with nParent==(-1). This will |
| 480 | ** not actually draw anything on the graph, but it will set the |
| 481 | ** background color of the timeline entry */ |
| 482 | gidx = graph_add_row(pGraph, rid, -1, 0, 0, zBr, zBgClr, zUuid, 0); |
| 483 | @ <div id="m%d(gidx)" class="tl-nodemark"></div> |
| 484 | } |
| 485 | fossil_free(zBr); |
| 486 | @</td> |
| 487 | if( !isSelectedOrCurrent ){ |
| 488 | @ <td class="timeline%s(zStyle)Cell%s(zExtraClass)" id='mc%d(gidx)'> |
| 489 | }else{ |
| 490 | @ <td class="timeline%s(zStyle)Cell%s(zExtraClass)"> |
| @@ -1100,47 +1113,10 @@ | |
| 1113 | @ WHERE blob.rid=event.objid |
| 1114 | ; |
| 1115 | return zBase; |
| 1116 | } |
| 1117 | |
| 1118 | /* |
| 1119 | ** zDate is a localtime date. Insert records into the |
| 1120 | ** "timeline" table to cause <hr> to be inserted on zDate. |
| 1121 | */ |
| 1122 | static int timeline_add_divider(double rDate){ |
| @@ -1392,11 +1368,11 @@ | |
| 1368 | ** return 0. |
| 1369 | */ |
| 1370 | static int timeline_endpoint( |
| 1371 | int iFrom, /* Starting point */ |
| 1372 | const char *zEnd, /* Tag we are searching for */ |
| 1373 | int bForward /* 1: forwards in time (descendants) 0: backwards */ |
| 1374 | ){ |
| 1375 | int tagId; |
| 1376 | int endId = 0; |
| 1377 | Stmt q; |
| 1378 | int ans = 0; |
| @@ -1419,11 +1395,12 @@ | |
| 1395 | " AND plink.mtime<=(SELECT max(event.mtime) FROM tagxref, event" |
| 1396 | " WHERE tagxref.tagid=%d AND tagxref.tagtype>0" |
| 1397 | " AND event.objid=tagxref.rid)" |
| 1398 | " ORDER BY plink.mtime)" |
| 1399 | "SELECT id FROM dx, tagxref" |
| 1400 | " WHERE tagid=%d AND tagtype>0 AND rid=id" |
| 1401 | " ORDER BY dx.mtime LIMIT 1", |
| 1402 | iFrom, iFrom, tagId, tagId |
| 1403 | ); |
| 1404 | }else{ |
| 1405 | db_prepare(&q, |
| 1406 | "WITH RECURSIVE dx(id,mtime) AS (" |
| @@ -1450,11 +1427,12 @@ | |
| 1427 | " AND event.mtime>=(SELECT min(event.mtime) FROM tagxref, event" |
| 1428 | " WHERE tagxref.tagid=%d AND tagxref.tagtype>0" |
| 1429 | " AND event.objid=tagxref.rid)" |
| 1430 | " ORDER BY event.mtime DESC)" |
| 1431 | "SELECT id FROM dx, tagxref" |
| 1432 | " WHERE tagid=%d AND tagtype>0 AND rid=id" |
| 1433 | " ORDER BY dx.mtime DESC LIMIT 1", |
| 1434 | iFrom, iFrom, tagId, tagId |
| 1435 | ); |
| 1436 | }else{ |
| 1437 | db_prepare(&q, |
| 1438 | "WITH RECURSIVE dx(id,mtime) AS (" |
| @@ -1522,17 +1500,17 @@ | |
| 1500 | /* |
| 1501 | ** COMMAND: test-endpoint |
| 1502 | ** |
| 1503 | ** Usage: fossil test-endpoint BASE TAG ?OPTIONS? |
| 1504 | ** |
| 1505 | ** Show the first check-in with TAG that is a descendant or ancestor |
| 1506 | ** of BASE. The first descendant checkin is shown by default. Use |
| 1507 | ** the --backto to see the first ancestor checkin. |
| 1508 | ** |
| 1509 | ** Options: |
| 1510 | ** |
| 1511 | ** --backto Show ancestor. Others defaults to descendants. |
| 1512 | */ |
| 1513 | void timeline_test_endpoint(void){ |
| 1514 | int bForward = find_option("backto",0,0)==0; |
| 1515 | int from_rid; |
| 1516 | int ans; |
| @@ -1583,14 +1561,14 @@ | |
| 1561 | ** dp=CHECKIN Same as 'd=CHECKIN&p=CHECKIN' |
| 1562 | ** dp2=CKIN2 Same as 'd2=CKIN2&p2=CKIN2' |
| 1563 | ** df=CHECKIN Same as 'd=CHECKIN&n1=all&nd'. Mnemonic: "Derived From" |
| 1564 | ** bt=CHECKIN "Back To". Show ancenstors going back to CHECKIN |
| 1565 | ** p=CX ... from CX back to time of CHECKIN |
| 1566 | ** from=CX ... path from CX back to CHECKIN |
| 1567 | ** ft=CHECKIN "Forward To": Show decendents forward to CHECKIN |
| 1568 | ** d=CX ... from CX up to the time of CHECKIN |
| 1569 | ** from=CX ... path from CX up to CHECKIN |
| 1570 | ** t=TAG Show only check-ins with the given TAG |
| 1571 | ** r=TAG Same as 't=TAG&rel'. Mnemonic: "Related" |
| 1572 | ** tl=TAGLIST Same as 't=TAGLIST&ms=brlist'. Mnemonic: "Tag List" |
| 1573 | ** rl=TAGLIST Same as 'r=TAGLIST&ms=brlist'. Mnemonic: "Related List" |
| 1574 | ** ml=TAGLIST Same as 'tl=TAGLIST&mionly'. Mnemonic: "Merge-in List" |
| @@ -1611,20 +1589,21 @@ | |
| 1589 | ** nsm Omit the submenu |
| 1590 | ** nc Omit all graph colors other than highlights |
| 1591 | ** v Show details of files changed |
| 1592 | ** vfx Show complete text of forum messages |
| 1593 | ** f=CHECKIN Family (immediate parents and children) of CHECKIN |
| 1594 | ** from=CHECKIN Path through common ancestor from CHECKIN... |
| 1595 | ** to=CHECKIN ... to this |
| 1596 | ** to2=CHECKIN ... backup name if to= doesn't resolve |
| 1597 | ** shortest ... pick path with least number of nodes |
| 1598 | ** rel ... also show related checkins |
| 1599 | ** min ... hide long sequences along same branch |
| 1600 | ** bt=PRIOR ... path from CHECKIN back to PRIOR |
| 1601 | ** ft=LATER ... path from CHECKIN forward to LATER |
| 1602 | ** me=CHECKIN Most direct path from CHECKIN... |
| 1603 | ** you=CHECKIN ... to this |
| 1604 | ** rel ... also show related checkins |
| 1605 | ** uf=FILE_HASH Show only check-ins that contain the given file version |
| 1606 | ** All qualifying check-ins are shown unless there is |
| 1607 | ** also an n= or n1= query parameter. |
| 1608 | ** chng=GLOBLIST Show only check-ins that involve changes to a file whose |
| 1609 | ** name matches one of the comma-separate GLOBLIST |
| @@ -1707,15 +1686,15 @@ | |
| 1686 | const char *zThisUser = 0; /* Suppress links to this user */ |
| 1687 | HQuery url; /* URL for various branch links */ |
| 1688 | int from_rid = name_to_typed_rid(P("from"),"ci"); /* from= for paths */ |
| 1689 | const char *zTo2 = 0; |
| 1690 | int to_rid = name_choice("to","to2",&zTo2); /* to= for path timelines */ |
| 1691 | int bShort = P("shortest")!=0; /* shortest possible path */ |
| 1692 | int me_rid = name_to_typed_rid(P("me"),"ci"); /* me= for common ancestory */ |
| 1693 | int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */ |
| 1694 | int pd_rid; |
| 1695 | const char *zDPNameP, *zDPNameD; /* Value of p=, d=, or dp= params */ |
| 1696 | double rBefore, rAfter, rCirca; /* Boundary times */ |
| 1697 | const char *z; |
| 1698 | char *zOlderButton = 0; /* URL for Older button at the bottom */ |
| 1699 | char *zOlderButtonLabel = 0; /* Label for the Older Button */ |
| 1700 | char *zNewerButton = 0; /* URL for Newer button at the top */ |
| @@ -1728,10 +1707,11 @@ | |
| 1707 | int showCherrypicks = 1; /* True to show cherrypick merges */ |
| 1708 | int haveParameterN; /* True if n= query parameter present */ |
| 1709 | int from_to_mode = 0; /* 0: from,to. 1: from,ft 2: from,bt */ |
| 1710 | int showSql = PB("showsql"); /* True to show the SQL */ |
| 1711 | Blob allSql; /* Copy of all SQL text */ |
| 1712 | int bMin = P("min")!=0; /* True if "min" query parameter used */ |
| 1713 | |
| 1714 | login_check_credentials(); |
| 1715 | url_initialize(&url, "timeline"); |
| 1716 | cgi_query_parameters_to_url(&url); |
| 1717 | blob_init(&allSql, 0, 0); |
| @@ -1777,20 +1757,20 @@ | |
| 1757 | }else{ |
| 1758 | nEntry = 50; |
| 1759 | } |
| 1760 | |
| 1761 | /* Query parameters d=, p=, and f= and variants */ |
| 1762 | p_rid = name_choice("p","p2", &zDPNameP); |
| 1763 | d_rid = name_choice("d","d2", &zDPNameD); |
| 1764 | z = P("f"); |
| 1765 | f_rid = z ? name_to_typed_rid(z,"ci") : 0; |
| 1766 | z = P("df"); |
| 1767 | if( z && (d_rid = name_to_typed_rid(z,"ci"))!=0 ){ |
| 1768 | nEntry = 0; |
| 1769 | useDividers = 0; |
| 1770 | cgi_replace_query_parameter("d",fossil_strdup(z)); |
| 1771 | zDPNameD = zDPNameP = z; |
| 1772 | } |
| 1773 | |
| 1774 | /* Undocumented query parameter to set JS mode */ |
| 1775 | builtin_set_js_delivery_mode(P("jsmode"),1); |
| 1776 | |
| @@ -1810,13 +1790,14 @@ | |
| 1790 | showCherrypicks = 0; |
| 1791 | } |
| 1792 | |
| 1793 | /* To view the timeline, must have permission to read project data. |
| 1794 | */ |
| 1795 | pd_rid = name_choice("dp","dp2",&zDPNameP); |
| 1796 | if( pd_rid ){ |
| 1797 | p_rid = d_rid = pd_rid; |
| 1798 | zDPNameD = zDPNameP; |
| 1799 | } |
| 1800 | if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum) |
| 1801 | || (bisectLocal && !g.perm.Setup) |
| 1802 | ){ |
| 1803 | login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki); |
| @@ -2083,20 +2064,22 @@ | |
| 2064 | const char *zTo = 0; |
| 2065 | Blob ins; |
| 2066 | int nNodeOnPath = 0; |
| 2067 | int commonAncs = 0; /* Common ancestors of me_rid and you_rid. */ |
| 2068 | int earlierRid = 0, laterRid = 0; |
| 2069 | int cost = bShort ? 0 : 1; |
| 2070 | int nSkip = 0; |
| 2071 | |
| 2072 | if( from_rid && to_rid ){ |
| 2073 | if( from_to_mode==0 ){ |
| 2074 | p = path_shortest(from_rid, to_rid, 0, 0, 0, cost); |
| 2075 | }else if( from_to_mode==1 ){ |
| 2076 | p = path_shortest(from_rid, to_rid, 0, 1, 0, cost); |
| 2077 | earlierRid = commonAncs = from_rid; |
| 2078 | laterRid = to_rid; |
| 2079 | }else{ |
| 2080 | p = path_shortest(to_rid, from_rid, 0, 1, 0, cost); |
| 2081 | earlierRid = commonAncs = to_rid; |
| 2082 | laterRid = from_rid; |
| 2083 | } |
| 2084 | zFrom = P("from"); |
| 2085 | zTo = zTo2 ? zTo2 : P("to"); |
| @@ -2123,20 +2106,25 @@ | |
| 2106 | ); |
| 2107 | if( p ){ |
| 2108 | int cnt = 4; |
| 2109 | blob_init(&ins, 0, 0); |
| 2110 | blob_append_sql(&ins, "INSERT INTO pathnode(x) VALUES(%d)", p->rid); |
| 2111 | if( p->u.pTo==0 ) bMin = 0; |
| 2112 | for(p=p->u.pTo; p; p=p->u.pTo){ |
| 2113 | if( bMin |
| 2114 | && p->u.pTo!=0 |
| 2115 | && fossil_strcmp(path_branch(p->pFrom),path_branch(p))==0 |
| 2116 | && fossil_strcmp(path_branch(p),path_branch(p->u.pTo))==0 |
| 2117 | ){ |
| 2118 | nSkip++; |
| 2119 | }else if( cnt==8 ){ |
| 2120 | blob_append_sql(&ins, ",\n (%d)", p->rid); |
| 2121 | cnt = 0; |
| 2122 | }else{ |
| 2123 | cnt++; |
| 2124 | blob_append_sql(&ins, ",(%d)", p->rid); |
| 2125 | } |
| 2126 | } |
| 2127 | } |
| 2128 | path_reset(); |
| 2129 | db_multi_exec("%s", blob_str(&ins)/*safe-for-%s*/); |
| 2130 | blob_reset(&ins); |
| @@ -2196,12 +2184,13 @@ | |
| 2184 | style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0); |
| 2185 | } |
| 2186 | nNodeOnPath = db_int(0, "SELECT count(*) FROM temp.pathnode"); |
| 2187 | if( nNodeOnPath==1 && from_to_mode>0 ){ |
| 2188 | blob_appendf(&desc,"Check-in "); |
| 2189 | }else if( bMin ){ |
| 2190 | blob_appendf(&desc, "%d of %d check-ins along the path from ", |
| 2191 | nNodeOnPath, nNodeOnPath+nSkip); |
| 2192 | }else{ |
| 2193 | blob_appendf(&desc, "%d check-ins going from ", nNodeOnPath); |
| 2194 | } |
| 2195 | if( from_rid==selectedRid ){ |
| 2196 | blob_appendf(&desc, "<span class='timelineSelected'>"); |
| @@ -2225,49 +2214,60 @@ | |
| 2214 | } |
| 2215 | } |
| 2216 | } |
| 2217 | addFileGlobDescription(zChng, &desc); |
| 2218 | }else if( (p_rid || d_rid) && g.perm.Read && zTagSql==0 ){ |
| 2219 | /* If either p= or d= or both are present, ignore all other parameters |
| 2220 | ** other than n=, ft=, and bt= */ |
| 2221 | const char *zBaseName = 0; |
| 2222 | int np = 0, nd; |
| 2223 | const char *zBackTo = 0; |
| 2224 | const char *zFwdTo = 0; |
| 2225 | int ridBackTo = 0; |
| 2226 | int ridFwdTo = 0; |
| 2227 | int bBackAdded = 0; /* True if the zBackTo node was added */ |
| 2228 | int bFwdAdded = 0; /* True if the zBackTo node was added */ |
| 2229 | int bSeparateDandP = 0; /* p_rid & d_rid both exist and are distinct */ |
| 2230 | |
| 2231 | tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS; |
| 2232 | if( p_rid && d_rid && p_rid!=d_rid ){ |
| 2233 | bSeparateDandP = 1; |
| 2234 | db_multi_exec( |
| 2235 | "CREATE TEMP TABLE IF NOT EXISTS ok_d(rid INTEGER PRIMARY KEY)" |
| 2236 | ); |
| 2237 | }else{ |
| 2238 | zBaseName = p_rid ? zDPNameP : zDPNameD; |
| 2239 | } |
| 2240 | db_multi_exec( |
| 2241 | "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)" |
| 2242 | ); |
| 2243 | add_extra_rids("ok", P("x")); |
| 2244 | blob_append_sql(&sql, " AND event.objid IN ok"); |
| 2245 | nd = 0; |
| 2246 | if( d_rid ){ |
| 2247 | double rStopTime = 9e99; |
| 2248 | zFwdTo = P("ft"); |
| 2249 | if( zFwdTo && bSeparateDandP ){ |
| 2250 | if( zError==0 ){ |
| 2251 | zError = "Cannot use the ft= query parameter when both p= and d= " |
| 2252 | "are used and have distinct values."; |
| 2253 | } |
| 2254 | zFwdTo = 0; |
| 2255 | } |
| 2256 | if( zFwdTo ){ |
| 2257 | double rStartDate = mtime_of_rid(d_rid, 0.0); |
| 2258 | ridFwdTo = first_checkin_with_tag_after_date(zFwdTo, rStartDate); |
| 2259 | if( ridFwdTo==0 ){ |
| 2260 | ridFwdTo = name_to_typed_rid(zBackTo,"ci"); |
| 2261 | } |
| 2262 | if( ridFwdTo ){ |
| 2263 | if( !haveParameterN ) nEntry = 0; |
| 2264 | rStopTime = mtime_of_rid(ridFwdTo, 9e99); |
| 2265 | } |
| 2266 | }else if( bSeparateDandP ){ |
| 2267 | rStopTime = mtime_of_rid(p_rid, 9e99); |
| 2268 | nEntry = 0; |
| 2269 | } |
| 2270 | if( rStopTime<9e99 ){ |
| 2271 | rStopTime += 5.8e-6; /* Round up by 1/2 second */ |
| 2272 | } |
| 2273 | db_multi_exec( |
| @@ -2280,72 +2280,111 @@ | |
| 2280 | " ORDER BY 2\n" |
| 2281 | ")\n" |
| 2282 | "INSERT OR IGNORE INTO ok SELECT rid FROM dx LIMIT %d", |
| 2283 | d_rid, rStopTime<8e99 ? 17 : 2, rStopTime, nEntry<=0 ? -1 : nEntry+1 |
| 2284 | ); |
| 2285 | if( ridFwdTo && !db_exists("SELECT 1 FROM ok WHERE rid=%d",ridFwdTo) ){ |
| 2286 | db_multi_exec("INSERT OR IGNORE INTO ok VALUES(%d)", ridFwdTo); |
| 2287 | bFwdAdded = 1; |
| 2288 | } |
| 2289 | if( bSeparateDandP ){ |
| 2290 | db_multi_exec( |
| 2291 | "INSERT INTO ok_d SELECT rid FROM ok;" |
| 2292 | "DELETE FROM ok;" |
| 2293 | ); |
| 2294 | }else{ |
| 2295 | nd = db_int(0, "SELECT count(*)-1 FROM ok"); |
| 2296 | if( nd>=0 ) db_multi_exec("%s", blob_sql_text(&sql)); |
| 2297 | if( nd>0 || p_rid==0 ){ |
| 2298 | blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s"); |
| 2299 | } |
| 2300 | if( useDividers && !selectedRid ) selectedRid = d_rid; |
| 2301 | db_multi_exec("DELETE FROM ok"); |
| 2302 | } |
| 2303 | } |
| 2304 | if( p_rid ){ |
| 2305 | zBackTo = P("bt"); |
| 2306 | if( zBackTo && bSeparateDandP ){ |
| 2307 | if( zError==0 ){ |
| 2308 | zError = "Cannot use the bt= query parameter when both p= and d= " |
| 2309 | "are used and have distinct values."; |
| 2310 | } |
| 2311 | zBackTo = 0; |
| 2312 | } |
| 2313 | if( zBackTo ){ |
| 2314 | double rDateLimit = mtime_of_rid(p_rid, 0.0); |
| 2315 | ridBackTo = last_checkin_with_tag_before_date(zBackTo, rDateLimit); |
| 2316 | if( ridBackTo==0 ){ |
| 2317 | ridBackTo = name_to_typed_rid(zBackTo,"ci"); |
| 2318 | } |
| 2319 | if( ridBackTo && !haveParameterN ) nEntry = 0; |
| 2320 | }else if( bSeparateDandP ){ |
| 2321 | ridBackTo = d_rid; |
| 2322 | nEntry = 0; |
| 2323 | } |
| 2324 | compute_ancestors(p_rid, nEntry==0 ? 0 : nEntry+1, 0, ridBackTo); |
| 2325 | if( ridBackTo && !db_exists("SELECT 1 FROM ok WHERE rid=%d",ridBackTo) ){ |
| 2326 | db_multi_exec("INSERT OR IGNORE INTO ok VALUES(%d)", ridBackTo); |
| 2327 | bBackAdded = 1; |
| 2328 | } |
| 2329 | if( bSeparateDandP ){ |
| 2330 | db_multi_exec("DELETE FROM ok WHERE rid NOT IN ok_d;"); |
| 2331 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 2332 | }else{ |
| 2333 | np = db_int(0, "SELECT count(*)-1 FROM ok"); |
| 2334 | if( np>0 || nd==0 ){ |
| 2335 | if( nd>0 ) blob_appendf(&desc, " and "); |
| 2336 | blob_appendf(&desc, "%d ancestor%s", np, (1==np)?"":"s"); |
| 2337 | db_multi_exec("%s", blob_sql_text(&sql)); |
| 2338 | } |
| 2339 | if( useDividers && !selectedRid ) selectedRid = p_rid; |
| 2340 | } |
| 2341 | } |
| 2342 | if( bSeparateDandP ){ |
| 2343 | int n = db_int(0, "SELECT count(*) FROM ok"); |
| 2344 | blob_reset(&desc); |
| 2345 | blob_appendf(&desc, |
| 2346 | "%d check-ins that are both ancestors of %z%h</a>" |
| 2347 | " and descendants of %z%h</a>", |
| 2348 | n, |
| 2349 | href("%R/info?name=%h",zDPNameP),zDPNameP, |
| 2350 | href("%R/info?name=%h",zDPNameD),zDPNameD |
| 2351 | ); |
| 2352 | ridBackTo = 0; |
| 2353 | ridFwdTo = 0; |
| 2354 | }else{ |
| 2355 | blob_appendf(&desc, " of %z%h</a>", |
| 2356 | href("%R/info?name=%h", zBaseName), zBaseName); |
| 2357 | } |
| 2358 | if( ridBackTo ){ |
| 2359 | if( np==0 ){ |
| 2360 | blob_reset(&desc); |
| 2361 | blob_appendf(&desc, |
| 2362 | "Check-in %z%h</a> only (%z%h</a> does not precede it)", |
| 2363 | href("%R/info?name=%h",zBaseName), zBaseName, |
| 2364 | href("%R/info?name=%h",zBackTo), zBackTo); |
| 2365 | }else{ |
| 2366 | blob_appendf(&desc, " back to %z%h</a>%s", |
| 2367 | href("%R/info?name=%h",zBackTo), zBackTo, |
| 2368 | bBackAdded ? " (not a direct anscestor)" : ""); |
| 2369 | if( ridFwdTo && zFwdTo ){ |
| 2370 | blob_appendf(&desc, " and up to %z%h</a>%s", |
| 2371 | href("%R/info?name=%h",zFwdTo), zFwdTo, |
| 2372 | bFwdAdded ? " (not a direct descendant)" : ""); |
| 2373 | } |
| 2374 | } |
| 2375 | }else if( ridFwdTo ){ |
| 2376 | if( nd==0 ){ |
| 2377 | blob_reset(&desc); |
| 2378 | blob_appendf(&desc, |
| 2379 | "Check-in %z%h</a> only (%z%h</a> does not follow it)", |
| 2380 | href("%R/info?name=%h",zBaseName), zBaseName, |
| 2381 | href("%R/info?name=%h",zFwdTo), zFwdTo); |
| 2382 | }else{ |
| 2383 | blob_appendf(&desc, " up to %z%h</a>%s", |
| 2384 | href("%R/info?name=%h",zFwdTo), zFwdTo, |
| 2385 | bFwdAdded ? " (not a direct descendant)":""); |
| 2386 | } |
| 2387 | } |
| 2388 | if( advancedMenu ){ |
| 2389 | style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0); |
| 2390 | } |
| @@ -2815,13 +2854,13 @@ | |
| 2854 | blob_append_sql(&cond, |
| 2855 | " AND (event.comment LIKE '%%%q%%' OR event.brief LIKE '%%%q%%')", |
| 2856 | zSearch, zSearch); |
| 2857 | } |
| 2858 | } |
| 2859 | rBefore = symbolic_name_to_mtime(zBefore, &zBefore, 1); |
| 2860 | rAfter = symbolic_name_to_mtime(zAfter, &zAfter, 0); |
| 2861 | rCirca = symbolic_name_to_mtime(zCirca, &zCirca, 0); |
| 2862 | blob_append_sql(&sql, "%s", blob_sql_text(&cond)); |
| 2863 | if( rAfter>0.0 ){ |
| 2864 | if( rBefore>0.0 ){ |
| 2865 | blob_append_sql(&sql, |
| 2866 | " AND event.mtime>=%.17g AND event.mtime<=%.17g\n" |
| @@ -2958,11 +2997,11 @@ | |
| 2997 | zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/"); |
| 2998 | if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){ |
| 2999 | zDate = mprintf("%s", (zAfter ? zAfter : zBefore)); |
| 3000 | } |
| 3001 | if( zDate ){ |
| 3002 | rDate = symbolic_name_to_mtime(zDate, 0, 0); |
| 3003 | if( db_int(0, |
| 3004 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 3005 | " WHERE blob.rid=event.objid AND mtime<=%.17g%s)", |
| 3006 | rDate-ONE_SECOND, blob_sql_text(&cond)) |
| 3007 | ){ |
| @@ -2974,11 +3013,11 @@ | |
| 3013 | zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/"); |
| 3014 | if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){ |
| 3015 | zDate = mprintf("%s", (zBefore ? zBefore : zAfter)); |
| 3016 | } |
| 3017 | if( zDate ){ |
| 3018 | rDate = symbolic_name_to_mtime(zDate, 0, 0); |
| 3019 | if( db_int(0, |
| 3020 | "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob" |
| 3021 | " WHERE blob.rid=event.objid AND mtime>=%.17g%s)", |
| 3022 | rDate+ONE_SECOND, blob_sql_text(&cond)) |
| 3023 | ){ |
| @@ -3017,11 +3056,11 @@ | |
| 3056 | style_submenu_element("Advanced", "%s", |
| 3057 | url_render(&url, "advm", "1", "udc", "1")); |
| 3058 | } |
| 3059 | if( PB("showid") ) tmFlags |= TIMELINE_SHOWRID; |
| 3060 | if( useDividers && zMark && zMark[0] ){ |
| 3061 | double r = symbolic_name_to_mtime(zMark, 0, 0); |
| 3062 | if( r>0.0 && !selectedRid ) selectedRid = timeline_add_divider(r); |
| 3063 | } |
| 3064 | blob_zero(&sql); |
| 3065 | if( PB("oldestfirst") ){ |
| 3066 | db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby ASC /*scan*/"); |
| @@ -3383,22 +3422,56 @@ | |
| 3422 | fossil_print("+++ no more data (%d) +++\n", nEntry); |
| 3423 | } |
| 3424 | } |
| 3425 | if( fchngQueryInit ) db_finalize(&fchngQuery); |
| 3426 | } |
| 3427 | |
| 3428 | /* |
| 3429 | ** wiki_to_text(TEXT) |
| 3430 | ** |
| 3431 | ** Return a text rendering of Fossil-Wiki TEXT, intended for display |
| 3432 | ** on a timeline. The timeline-plaintext and timeline-hard-newlines |
| 3433 | ** settings are considered when doing this rendering. |
| 3434 | */ |
| 3435 | static void wiki_to_text_sqlfunc( |
| 3436 | sqlite3_context *context, |
| 3437 | int argc, |
| 3438 | sqlite3_value **argv |
| 3439 | ){ |
| 3440 | const char *zIn, *zOut; |
| 3441 | int nIn, nOut; |
| 3442 | Blob in, html, txt; |
| 3443 | zIn = (const char*)sqlite3_value_text(argv[0]); |
| 3444 | if( zIn==0 ) return; |
| 3445 | nIn = sqlite3_value_bytes(argv[0]); |
| 3446 | blob_init(&in, zIn, nIn); |
| 3447 | blob_init(&html, 0, 0); |
| 3448 | wiki_convert(&in, &html, wiki_convert_flags(0)); |
| 3449 | blob_reset(&in); |
| 3450 | blob_init(&txt, 0, 0); |
| 3451 | html_to_plaintext(blob_str(&html), &txt, 0); |
| 3452 | blob_reset(&html); |
| 3453 | nOut = blob_size(&txt); |
| 3454 | zOut = blob_str(&txt); |
| 3455 | while( fossil_isspace(zOut[0]) ){ zOut++; nOut--; } |
| 3456 | while( nOut>0 && fossil_isspace(zOut[nOut-1]) ){ nOut--; } |
| 3457 | sqlite3_result_text(context, zOut, nOut, SQLITE_TRANSIENT); |
| 3458 | blob_reset(&txt); |
| 3459 | } |
| 3460 | |
| 3461 | /* |
| 3462 | ** Return a pointer to a static string that forms the basis for |
| 3463 | ** a timeline query for display on a TTY. |
| 3464 | */ |
| 3465 | const char *timeline_query_for_tty(void){ |
| 3466 | static int once = 0; |
| 3467 | static const char zBaseSql[] = |
| 3468 | @ SELECT |
| 3469 | @ blob.rid AS rid, |
| 3470 | @ uuid, |
| 3471 | @ datetime(event.mtime,toLocal()) AS mDateTime, |
| 3472 | @ wiki_to_text(coalesce(ecomment,comment)) |
| 3473 | @ || ' (user: ' || coalesce(euser,user,'?') |
| 3474 | @ || (SELECT case when length(x)>0 then ' tags: ' || x else '' end |
| 3475 | @ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x |
| 3476 | @ FROM tag, tagxref |
| 3477 | @ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid |
| @@ -3422,10 +3495,15 @@ | |
| 3495 | @ AND tagxref.tagtype>0 |
| 3496 | @ AND tagxref.rid=blob.rid |
| 3497 | @ WHERE blob.rid=event.objid |
| 3498 | @ AND tag.tagname='branch' |
| 3499 | ; |
| 3500 | if( !once && g.db ){ |
| 3501 | once = 1; |
| 3502 | sqlite3_create_function(g.db, "wiki_to_text", 1, SQLITE_UTF8, 0, |
| 3503 | wiki_to_text_sqlfunc, 0, 0); |
| 3504 | } |
| 3505 | return zBaseSql; |
| 3506 | } |
| 3507 | |
| 3508 | /* |
| 3509 | ** Return true if the input string is a date in the ISO 8601 format: |
| 3510 |
+6
-3
| --- src/tkt.c | ||
| +++ src/tkt.c | ||
| @@ -765,10 +765,16 @@ | ||
| 765 | 765 | } |
| 766 | 766 | } |
| 767 | 767 | if( !showTimeline && g.perm.Hyperlink ){ |
| 768 | 768 | style_submenu_element("Timeline", "%R/info/%T", zUuid); |
| 769 | 769 | } |
| 770 | + zFullName = db_text(0, | |
| 771 | + "SELECT tkt_uuid FROM ticket" | |
| 772 | + " WHERE tkt_uuid GLOB '%q*'", zUuid); | |
| 773 | + if( g.perm.WrWiki && g.perm.WrTkt ){ | |
| 774 | + style_submenu_element("Edit Description", "%R/wikiedit?name=ticket/%T", zFullName); | |
| 775 | + } | |
| 770 | 776 | if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br>\n", -1); |
| 771 | 777 | ticket_init(); |
| 772 | 778 | initializeVariablesFromCGI(); |
| 773 | 779 | getAllTicketFields(); |
| 774 | 780 | initializeVariablesFromDb(); |
| @@ -777,13 +783,10 @@ | ||
| 777 | 783 | if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW_SCRIPT<br>\n", -1); |
| 778 | 784 | safe_html_context(DOCSRC_TICKET); |
| 779 | 785 | Th_Render(zScript); |
| 780 | 786 | if( g.thTrace ) Th_Trace("END_TKTVIEW<br>\n", -1); |
| 781 | 787 | |
| 782 | - zFullName = db_text(0, | |
| 783 | - "SELECT tkt_uuid FROM ticket" | |
| 784 | - " WHERE tkt_uuid GLOB '%q*'", zUuid); | |
| 785 | 788 | if( zFullName ){ |
| 786 | 789 | attachment_list(zFullName, "<h2>Attachments:</h2>", 1); |
| 787 | 790 | } |
| 788 | 791 | |
| 789 | 792 | style_finish_page(); |
| 790 | 793 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -765,10 +765,16 @@ | |
| 765 | } |
| 766 | } |
| 767 | if( !showTimeline && g.perm.Hyperlink ){ |
| 768 | style_submenu_element("Timeline", "%R/info/%T", zUuid); |
| 769 | } |
| 770 | if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br>\n", -1); |
| 771 | ticket_init(); |
| 772 | initializeVariablesFromCGI(); |
| 773 | getAllTicketFields(); |
| 774 | initializeVariablesFromDb(); |
| @@ -777,13 +783,10 @@ | |
| 777 | if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW_SCRIPT<br>\n", -1); |
| 778 | safe_html_context(DOCSRC_TICKET); |
| 779 | Th_Render(zScript); |
| 780 | if( g.thTrace ) Th_Trace("END_TKTVIEW<br>\n", -1); |
| 781 | |
| 782 | zFullName = db_text(0, |
| 783 | "SELECT tkt_uuid FROM ticket" |
| 784 | " WHERE tkt_uuid GLOB '%q*'", zUuid); |
| 785 | if( zFullName ){ |
| 786 | attachment_list(zFullName, "<h2>Attachments:</h2>", 1); |
| 787 | } |
| 788 | |
| 789 | style_finish_page(); |
| 790 |
| --- src/tkt.c | |
| +++ src/tkt.c | |
| @@ -765,10 +765,16 @@ | |
| 765 | } |
| 766 | } |
| 767 | if( !showTimeline && g.perm.Hyperlink ){ |
| 768 | style_submenu_element("Timeline", "%R/info/%T", zUuid); |
| 769 | } |
| 770 | zFullName = db_text(0, |
| 771 | "SELECT tkt_uuid FROM ticket" |
| 772 | " WHERE tkt_uuid GLOB '%q*'", zUuid); |
| 773 | if( g.perm.WrWiki && g.perm.WrTkt ){ |
| 774 | style_submenu_element("Edit Description", "%R/wikiedit?name=ticket/%T", zFullName); |
| 775 | } |
| 776 | if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br>\n", -1); |
| 777 | ticket_init(); |
| 778 | initializeVariablesFromCGI(); |
| 779 | getAllTicketFields(); |
| 780 | initializeVariablesFromDb(); |
| @@ -777,13 +783,10 @@ | |
| 783 | if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW_SCRIPT<br>\n", -1); |
| 784 | safe_html_context(DOCSRC_TICKET); |
| 785 | Th_Render(zScript); |
| 786 | if( g.thTrace ) Th_Trace("END_TKTVIEW<br>\n", -1); |
| 787 | |
| 788 | if( zFullName ){ |
| 789 | attachment_list(zFullName, "<h2>Attachments:</h2>", 1); |
| 790 | } |
| 791 | |
| 792 | style_finish_page(); |
| 793 |
+7
-1
| --- src/tktsetup.c | ||
| +++ src/tktsetup.c | ||
| @@ -505,11 +505,17 @@ | ||
| 505 | 505 | @ </tr> |
| 506 | 506 | @ <tr><td class="tktDspLabel">Version Found In:</td> |
| 507 | 507 | @ <td colspan="3" valign="top" class="tktDspValue"> |
| 508 | 508 | @ $<foundin> |
| 509 | 509 | @ </td></tr> |
| 510 | +@ </table> | |
| 511 | +@ | |
| 512 | +@ <th1> | |
| 513 | +@ wiki_assoc "ticket" $tkt_uuid | |
| 514 | +@ </th1> | |
| 510 | 515 | @ |
| 516 | +@ <table cellpadding="5" style="min-width:100%"> | |
| 511 | 517 | @ <th1> |
| 512 | 518 | @ if {[info exists comment]} { |
| 513 | 519 | @ if {[string length $comment]>10} { |
| 514 | 520 | @ html { |
| 515 | 521 | @ <tr><td class="tktDspLabel">Description:</td></tr> |
| @@ -531,11 +537,11 @@ | ||
| 531 | 537 | @ FROM ticketchng |
| 532 | 538 | @ WHERE tkt_id=$tkt_id AND length(icomment)>0} { |
| 533 | 539 | @ if {$seenRow} { |
| 534 | 540 | @ html "<hr>\n" |
| 535 | 541 | @ } else { |
| 536 | -@ html "<tr><td class='tktDspLabel'>User Comments:</td></tr>\n" | |
| 542 | +@ html "<tr><td class='tktDspLabel' style='text-align:left'>User Comments:</td></tr>\n" | |
| 537 | 543 | @ html "<tr><td colspan='5' class='tktDspValue'>\n" |
| 538 | 544 | @ set seenRow 1 |
| 539 | 545 | @ } |
| 540 | 546 | @ html "<span class='tktDspCommenter'>" |
| 541 | 547 | @ html "[htmlize $xlogin]" |
| 542 | 548 |
| --- src/tktsetup.c | |
| +++ src/tktsetup.c | |
| @@ -505,11 +505,17 @@ | |
| 505 | @ </tr> |
| 506 | @ <tr><td class="tktDspLabel">Version Found In:</td> |
| 507 | @ <td colspan="3" valign="top" class="tktDspValue"> |
| 508 | @ $<foundin> |
| 509 | @ </td></tr> |
| 510 | @ |
| 511 | @ <th1> |
| 512 | @ if {[info exists comment]} { |
| 513 | @ if {[string length $comment]>10} { |
| 514 | @ html { |
| 515 | @ <tr><td class="tktDspLabel">Description:</td></tr> |
| @@ -531,11 +537,11 @@ | |
| 531 | @ FROM ticketchng |
| 532 | @ WHERE tkt_id=$tkt_id AND length(icomment)>0} { |
| 533 | @ if {$seenRow} { |
| 534 | @ html "<hr>\n" |
| 535 | @ } else { |
| 536 | @ html "<tr><td class='tktDspLabel'>User Comments:</td></tr>\n" |
| 537 | @ html "<tr><td colspan='5' class='tktDspValue'>\n" |
| 538 | @ set seenRow 1 |
| 539 | @ } |
| 540 | @ html "<span class='tktDspCommenter'>" |
| 541 | @ html "[htmlize $xlogin]" |
| 542 |
| --- src/tktsetup.c | |
| +++ src/tktsetup.c | |
| @@ -505,11 +505,17 @@ | |
| 505 | @ </tr> |
| 506 | @ <tr><td class="tktDspLabel">Version Found In:</td> |
| 507 | @ <td colspan="3" valign="top" class="tktDspValue"> |
| 508 | @ $<foundin> |
| 509 | @ </td></tr> |
| 510 | @ </table> |
| 511 | @ |
| 512 | @ <th1> |
| 513 | @ wiki_assoc "ticket" $tkt_uuid |
| 514 | @ </th1> |
| 515 | @ |
| 516 | @ <table cellpadding="5" style="min-width:100%"> |
| 517 | @ <th1> |
| 518 | @ if {[info exists comment]} { |
| 519 | @ if {[string length $comment]>10} { |
| 520 | @ html { |
| 521 | @ <tr><td class="tktDspLabel">Description:</td></tr> |
| @@ -531,11 +537,11 @@ | |
| 537 | @ FROM ticketchng |
| 538 | @ WHERE tkt_id=$tkt_id AND length(icomment)>0} { |
| 539 | @ if {$seenRow} { |
| 540 | @ html "<hr>\n" |
| 541 | @ } else { |
| 542 | @ html "<tr><td class='tktDspLabel' style='text-align:left'>User Comments:</td></tr>\n" |
| 543 | @ html "<tr><td colspan='5' class='tktDspValue'>\n" |
| 544 | @ set seenRow 1 |
| 545 | @ } |
| 546 | @ html "<span class='tktDspCommenter'>" |
| 547 | @ html "[htmlize $xlogin]" |
| 548 |
+4
-2
| --- src/unversioned.c | ||
| +++ src/unversioned.c | ||
| @@ -218,12 +218,12 @@ | ||
| 218 | 218 | } |
| 219 | 219 | return 0; |
| 220 | 220 | } |
| 221 | 221 | |
| 222 | 222 | /* |
| 223 | -** COMMAND: uv# | |
| 224 | -** COMMAND: unversioned | |
| 223 | +** COMMAND: uv# abbrv-subcom | |
| 224 | +** COMMAND: unversioned abbrv-subcom | |
| 225 | 225 | ** |
| 226 | 226 | ** Usage: %fossil unversioned SUBCOMMAND ARGS... |
| 227 | 227 | ** or: %fossil uv SUBCOMMAND ARGS.. |
| 228 | 228 | ** |
| 229 | 229 | ** Unversioned files (UV-files) are artifacts that are synced and are available |
| @@ -266,10 +266,11 @@ | ||
| 266 | 266 | ** URL. |
| 267 | 267 | ** |
| 268 | 268 | ** Options: |
| 269 | 269 | ** -v|--verbose Extra diagnostic output |
| 270 | 270 | ** -n|--dry-run Show what would have happened |
| 271 | +** --proxy PROXY Use the specified HTTP proxy | |
| 271 | 272 | ** |
| 272 | 273 | ** remove|rm|delete FILE ... |
| 273 | 274 | ** Remove unversioned files from the local repository. |
| 274 | 275 | ** Changes are not pushed to other repositories until |
| 275 | 276 | ** the next sync. |
| @@ -285,10 +286,11 @@ | ||
| 285 | 286 | ** The remote account requires the 'y' capability. |
| 286 | 287 | ** |
| 287 | 288 | ** Options: |
| 288 | 289 | ** -v|--verbose Extra diagnostic output |
| 289 | 290 | ** -n|--dry-run Show what would have happened |
| 291 | +** --proxy PROXY Use the specified HTTP proxy | |
| 290 | 292 | ** |
| 291 | 293 | ** touch FILE ... Update the TIMESTAMP on all of the listed files |
| 292 | 294 | ** |
| 293 | 295 | ** Options: |
| 294 | 296 | ** --mtime TIMESTAMP Use TIMESTAMP instead of "now" for the "add", |
| 295 | 297 |
| --- src/unversioned.c | |
| +++ src/unversioned.c | |
| @@ -218,12 +218,12 @@ | |
| 218 | } |
| 219 | return 0; |
| 220 | } |
| 221 | |
| 222 | /* |
| 223 | ** COMMAND: uv# |
| 224 | ** COMMAND: unversioned |
| 225 | ** |
| 226 | ** Usage: %fossil unversioned SUBCOMMAND ARGS... |
| 227 | ** or: %fossil uv SUBCOMMAND ARGS.. |
| 228 | ** |
| 229 | ** Unversioned files (UV-files) are artifacts that are synced and are available |
| @@ -266,10 +266,11 @@ | |
| 266 | ** URL. |
| 267 | ** |
| 268 | ** Options: |
| 269 | ** -v|--verbose Extra diagnostic output |
| 270 | ** -n|--dry-run Show what would have happened |
| 271 | ** |
| 272 | ** remove|rm|delete FILE ... |
| 273 | ** Remove unversioned files from the local repository. |
| 274 | ** Changes are not pushed to other repositories until |
| 275 | ** the next sync. |
| @@ -285,10 +286,11 @@ | |
| 285 | ** The remote account requires the 'y' capability. |
| 286 | ** |
| 287 | ** Options: |
| 288 | ** -v|--verbose Extra diagnostic output |
| 289 | ** -n|--dry-run Show what would have happened |
| 290 | ** |
| 291 | ** touch FILE ... Update the TIMESTAMP on all of the listed files |
| 292 | ** |
| 293 | ** Options: |
| 294 | ** --mtime TIMESTAMP Use TIMESTAMP instead of "now" for the "add", |
| 295 |
| --- src/unversioned.c | |
| +++ src/unversioned.c | |
| @@ -218,12 +218,12 @@ | |
| 218 | } |
| 219 | return 0; |
| 220 | } |
| 221 | |
| 222 | /* |
| 223 | ** COMMAND: uv# abbrv-subcom |
| 224 | ** COMMAND: unversioned abbrv-subcom |
| 225 | ** |
| 226 | ** Usage: %fossil unversioned SUBCOMMAND ARGS... |
| 227 | ** or: %fossil uv SUBCOMMAND ARGS.. |
| 228 | ** |
| 229 | ** Unversioned files (UV-files) are artifacts that are synced and are available |
| @@ -266,10 +266,11 @@ | |
| 266 | ** URL. |
| 267 | ** |
| 268 | ** Options: |
| 269 | ** -v|--verbose Extra diagnostic output |
| 270 | ** -n|--dry-run Show what would have happened |
| 271 | ** --proxy PROXY Use the specified HTTP proxy |
| 272 | ** |
| 273 | ** remove|rm|delete FILE ... |
| 274 | ** Remove unversioned files from the local repository. |
| 275 | ** Changes are not pushed to other repositories until |
| 276 | ** the next sync. |
| @@ -285,10 +286,11 @@ | |
| 286 | ** The remote account requires the 'y' capability. |
| 287 | ** |
| 288 | ** Options: |
| 289 | ** -v|--verbose Extra diagnostic output |
| 290 | ** -n|--dry-run Show what would have happened |
| 291 | ** --proxy PROXY Use the specified HTTP proxy |
| 292 | ** |
| 293 | ** touch FILE ... Update the TIMESTAMP on all of the listed files |
| 294 | ** |
| 295 | ** Options: |
| 296 | ** --mtime TIMESTAMP Use TIMESTAMP instead of "now" for the "add", |
| 297 |
+2
-1
| --- src/update.c | ||
| +++ src/update.c | ||
| @@ -101,10 +101,11 @@ | ||
| 101 | 101 | ** used for merging, named *-baseline, *-original, |
| 102 | 102 | ** and *-merge. |
| 103 | 103 | ** --latest Acceptable in place of VERSION, update to |
| 104 | 104 | ** latest version |
| 105 | 105 | ** --nosync Do not auto-sync prior to update |
| 106 | +** --proxy PROXY Use PROXY as http proxy during sync operation | |
| 106 | 107 | ** --setmtime Set timestamps of all files to match their |
| 107 | 108 | ** SCM-side times (the timestamp of the last |
| 108 | 109 | ** check-in which modified them). |
| 109 | 110 | ** -v|--verbose Print status information about all files |
| 110 | 111 | ** -W|--width WIDTH Width of lines (default is to auto-detect). |
| @@ -198,11 +199,11 @@ | ||
| 198 | 199 | } |
| 199 | 200 | } |
| 200 | 201 | } |
| 201 | 202 | |
| 202 | 203 | /* If no VERSION is specified on the command-line, then look for a |
| 203 | - ** descendent of the current version. If there are multiple descendants, | |
| 204 | + ** descendant of the current version. If there are multiple descendants, | |
| 204 | 205 | ** look for one from the same branch as the current version. If there |
| 205 | 206 | ** are still multiple descendants, show them all and refuse to update |
| 206 | 207 | ** until the user selects one. |
| 207 | 208 | */ |
| 208 | 209 | if( tid==0 ){ |
| 209 | 210 |
| --- src/update.c | |
| +++ src/update.c | |
| @@ -101,10 +101,11 @@ | |
| 101 | ** used for merging, named *-baseline, *-original, |
| 102 | ** and *-merge. |
| 103 | ** --latest Acceptable in place of VERSION, update to |
| 104 | ** latest version |
| 105 | ** --nosync Do not auto-sync prior to update |
| 106 | ** --setmtime Set timestamps of all files to match their |
| 107 | ** SCM-side times (the timestamp of the last |
| 108 | ** check-in which modified them). |
| 109 | ** -v|--verbose Print status information about all files |
| 110 | ** -W|--width WIDTH Width of lines (default is to auto-detect). |
| @@ -198,11 +199,11 @@ | |
| 198 | } |
| 199 | } |
| 200 | } |
| 201 | |
| 202 | /* If no VERSION is specified on the command-line, then look for a |
| 203 | ** descendent of the current version. If there are multiple descendants, |
| 204 | ** look for one from the same branch as the current version. If there |
| 205 | ** are still multiple descendants, show them all and refuse to update |
| 206 | ** until the user selects one. |
| 207 | */ |
| 208 | if( tid==0 ){ |
| 209 |
| --- src/update.c | |
| +++ src/update.c | |
| @@ -101,10 +101,11 @@ | |
| 101 | ** used for merging, named *-baseline, *-original, |
| 102 | ** and *-merge. |
| 103 | ** --latest Acceptable in place of VERSION, update to |
| 104 | ** latest version |
| 105 | ** --nosync Do not auto-sync prior to update |
| 106 | ** --proxy PROXY Use PROXY as http proxy during sync operation |
| 107 | ** --setmtime Set timestamps of all files to match their |
| 108 | ** SCM-side times (the timestamp of the last |
| 109 | ** check-in which modified them). |
| 110 | ** -v|--verbose Print status information about all files |
| 111 | ** -W|--width WIDTH Width of lines (default is to auto-detect). |
| @@ -198,11 +199,11 @@ | |
| 199 | } |
| 200 | } |
| 201 | } |
| 202 | |
| 203 | /* If no VERSION is specified on the command-line, then look for a |
| 204 | ** descendant of the current version. If there are multiple descendants, |
| 205 | ** look for one from the same branch as the current version. If there |
| 206 | ** are still multiple descendants, show them all and refuse to update |
| 207 | ** until the user selects one. |
| 208 | */ |
| 209 | if( tid==0 ){ |
| 210 |
+2
-3
| --- src/user.c | ||
| +++ src/user.c | ||
| @@ -298,11 +298,11 @@ | ||
| 298 | 298 | ** Prompt the user to enter a single line of text. |
| 299 | 299 | */ |
| 300 | 300 | void prompt_user(const char *zPrompt, Blob *pIn){ |
| 301 | 301 | char *z; |
| 302 | 302 | char zLine[1000]; |
| 303 | - blob_zero(pIn); | |
| 303 | + blob_init(pIn, 0, 0); | |
| 304 | 304 | fossil_force_newline(); |
| 305 | 305 | fossil_print("%s", zPrompt); |
| 306 | 306 | fflush(stdout); |
| 307 | 307 | z = fgets(zLine, sizeof(zLine), stdin); |
| 308 | 308 | if( z ){ |
| @@ -331,12 +331,11 @@ | ||
| 331 | 331 | ** > fossil user default ?USERNAME? |
| 332 | 332 | ** |
| 333 | 333 | ** Query or set the default user. The default user is the |
| 334 | 334 | ** user for command-line interaction. |
| 335 | 335 | ** |
| 336 | -** > fossil user list | |
| 337 | -** > fossil user ls | |
| 336 | +** > fossil user list | ls | |
| 338 | 337 | ** |
| 339 | 338 | ** List all users known to the repository |
| 340 | 339 | ** |
| 341 | 340 | ** > fossil user new ?USERNAME? ?CONTACT-INFO? ?PASSWORD? |
| 342 | 341 | ** |
| 343 | 342 |
| --- src/user.c | |
| +++ src/user.c | |
| @@ -298,11 +298,11 @@ | |
| 298 | ** Prompt the user to enter a single line of text. |
| 299 | */ |
| 300 | void prompt_user(const char *zPrompt, Blob *pIn){ |
| 301 | char *z; |
| 302 | char zLine[1000]; |
| 303 | blob_zero(pIn); |
| 304 | fossil_force_newline(); |
| 305 | fossil_print("%s", zPrompt); |
| 306 | fflush(stdout); |
| 307 | z = fgets(zLine, sizeof(zLine), stdin); |
| 308 | if( z ){ |
| @@ -331,12 +331,11 @@ | |
| 331 | ** > fossil user default ?USERNAME? |
| 332 | ** |
| 333 | ** Query or set the default user. The default user is the |
| 334 | ** user for command-line interaction. |
| 335 | ** |
| 336 | ** > fossil user list |
| 337 | ** > fossil user ls |
| 338 | ** |
| 339 | ** List all users known to the repository |
| 340 | ** |
| 341 | ** > fossil user new ?USERNAME? ?CONTACT-INFO? ?PASSWORD? |
| 342 | ** |
| 343 |
| --- src/user.c | |
| +++ src/user.c | |
| @@ -298,11 +298,11 @@ | |
| 298 | ** Prompt the user to enter a single line of text. |
| 299 | */ |
| 300 | void prompt_user(const char *zPrompt, Blob *pIn){ |
| 301 | char *z; |
| 302 | char zLine[1000]; |
| 303 | blob_init(pIn, 0, 0); |
| 304 | fossil_force_newline(); |
| 305 | fossil_print("%s", zPrompt); |
| 306 | fflush(stdout); |
| 307 | z = fgets(zLine, sizeof(zLine), stdin); |
| 308 | if( z ){ |
| @@ -331,12 +331,11 @@ | |
| 331 | ** > fossil user default ?USERNAME? |
| 332 | ** |
| 333 | ** Query or set the default user. The default user is the |
| 334 | ** user for command-line interaction. |
| 335 | ** |
| 336 | ** > fossil user list | ls |
| 337 | ** |
| 338 | ** List all users known to the repository |
| 339 | ** |
| 340 | ** > fossil user new ?USERNAME? ?CONTACT-INFO? ?PASSWORD? |
| 341 | ** |
| 342 |
+1
-1
| --- src/utf8.c | ||
| +++ src/utf8.c | ||
| @@ -25,11 +25,11 @@ | ||
| 25 | 25 | #ifdef _WIN32 |
| 26 | 26 | # include <windows.h> |
| 27 | 27 | #endif |
| 28 | 28 | #include "cygsup.h" |
| 29 | 29 | |
| 30 | -#if defined(_WIN32) || defined(__CYGWIN__) | |
| 30 | +#if defined(_WIN32) | |
| 31 | 31 | /* |
| 32 | 32 | ** Translate MBCS to UTF-8. Return a pointer to the translated text. |
| 33 | 33 | ** Call fossil_mbcs_free() to deallocate any memory used to store the |
| 34 | 34 | ** returned pointer when done. |
| 35 | 35 | */ |
| 36 | 36 |
| --- src/utf8.c | |
| +++ src/utf8.c | |
| @@ -25,11 +25,11 @@ | |
| 25 | #ifdef _WIN32 |
| 26 | # include <windows.h> |
| 27 | #endif |
| 28 | #include "cygsup.h" |
| 29 | |
| 30 | #if defined(_WIN32) || defined(__CYGWIN__) |
| 31 | /* |
| 32 | ** Translate MBCS to UTF-8. Return a pointer to the translated text. |
| 33 | ** Call fossil_mbcs_free() to deallocate any memory used to store the |
| 34 | ** returned pointer when done. |
| 35 | */ |
| 36 |
| --- src/utf8.c | |
| +++ src/utf8.c | |
| @@ -25,11 +25,11 @@ | |
| 25 | #ifdef _WIN32 |
| 26 | # include <windows.h> |
| 27 | #endif |
| 28 | #include "cygsup.h" |
| 29 | |
| 30 | #if defined(_WIN32) |
| 31 | /* |
| 32 | ** Translate MBCS to UTF-8. Return a pointer to the translated text. |
| 33 | ** Call fossil_mbcs_free() to deallocate any memory used to store the |
| 34 | ** returned pointer when done. |
| 35 | */ |
| 36 |
+63
-17
| --- src/util.c | ||
| +++ src/util.c | ||
| @@ -670,28 +670,33 @@ | ||
| 670 | 670 | ** Search algorithm: |
| 671 | 671 | ** (1) The local "editor" setting |
| 672 | 672 | ** (2) The global "editor" setting |
| 673 | 673 | ** (3) The VISUAL environment variable |
| 674 | 674 | ** (4) The EDITOR environment variable |
| 675 | -** (5) (Windows only:) "notepad.exe" | |
| 675 | +** (5) Any of the following programs that are available: | |
| 676 | +** notepad, nano, pico, jove, edit, vi, vim, ed, | |
| 676 | 677 | */ |
| 677 | 678 | const char *fossil_text_editor(void){ |
| 678 | 679 | const char *zEditor = db_get("editor", 0); |
| 680 | + const char *azStdEd[] = { | |
| 681 | + "notepad", "nano", "pico", "jove", "edit", "vi", "vim", "ed" | |
| 682 | + }; | |
| 683 | + int i = 0; | |
| 679 | 684 | if( zEditor==0 ){ |
| 680 | 685 | zEditor = fossil_getenv("VISUAL"); |
| 681 | 686 | } |
| 682 | 687 | if( zEditor==0 ){ |
| 683 | 688 | zEditor = fossil_getenv("EDITOR"); |
| 684 | 689 | } |
| 685 | -#if defined(_WIN32) || defined(__CYGWIN__) | |
| 686 | - if( zEditor==0 ){ | |
| 687 | - zEditor = mprintf("%s\\notepad.exe", fossil_getenv("SYSTEMROOT")); | |
| 688 | -#if defined(__CYGWIN__) | |
| 689 | - zEditor = fossil_utf8_to_path(zEditor, 0); | |
| 690 | -#endif | |
| 691 | - } | |
| 692 | -#endif | |
| 690 | + while( zEditor==0 && i<count(azStdEd) ){ | |
| 691 | + if( fossil_app_on_path(azStdEd[i],0) ){ | |
| 692 | + zEditor = azStdEd[i]; | |
| 693 | + }else{ | |
| 694 | + i++; | |
| 695 | + } | |
| 696 | + } | |
| 697 | + if( zEditor && is_false(zEditor) ) zEditor = 0; | |
| 693 | 698 | return zEditor; |
| 694 | 699 | } |
| 695 | 700 | |
| 696 | 701 | /* |
| 697 | 702 | ** Construct a temporary filename. |
| @@ -895,35 +900,76 @@ | ||
| 895 | 900 | return n< 10 ? 1 : n< 100 ? 2 : n< 1000 ? 3 |
| 896 | 901 | : n< 10000 ? 4 : n< 100000 ? 5 : n< 1000000 ? 6 |
| 897 | 902 | : n<10000000 ? 7 : n<100000000 ? 8 : n<1000000000 ? 9 : 10; |
| 898 | 903 | } |
| 899 | 904 | |
| 900 | -#if !defined(_WIN32) | |
| 901 | -#if !defined(__DARWIN__) && !defined(__APPLE__) && !defined(__HAIKU__) | |
| 902 | 905 | /* |
| 903 | 906 | ** Search for an executable on the PATH environment variable. |
| 904 | 907 | ** Return true (1) if found and false (0) if not found. |
| 908 | +** | |
| 909 | +** Print the full pathname of the first location if ePrint==1. Print | |
| 910 | +** all pathnames for the executable if ePrint==2 or more. | |
| 905 | 911 | */ |
| 906 | -static int binaryOnPath(const char *zBinary){ | |
| 912 | +int fossil_app_on_path(const char *zBinary, int ePrint){ | |
| 907 | 913 | const char *zPath = fossil_getenv("PATH"); |
| 908 | 914 | char *zFull; |
| 909 | 915 | int i; |
| 910 | 916 | int bExists; |
| 917 | + int bFound = 0; | |
| 911 | 918 | while( zPath && zPath[0] ){ |
| 919 | +#ifdef _WIN32 | |
| 920 | + while( zPath[0]==';' ) zPath++; | |
| 921 | + for(i=0; zPath[i] && zPath[i]!=';'; i++){} | |
| 922 | + zFull = mprintf("%.*s\\%s.exe", i, zPath, zBinary); | |
| 923 | + bExists = file_access(zFull, R_OK); | |
| 924 | + if( bExists!=0 ){ | |
| 925 | + fossil_free(zFull); | |
| 926 | + zFull = mprintf("%.*s\\%s.bat", i, zPath, zBinary); | |
| 927 | + bExists = file_access(zFull, R_OK); | |
| 928 | + } | |
| 929 | +#else | |
| 912 | 930 | while( zPath[0]==':' ) zPath++; |
| 913 | 931 | for(i=0; zPath[i] && zPath[i]!=':'; i++){} |
| 914 | 932 | zFull = mprintf("%.*s/%s", i, zPath, zBinary); |
| 915 | 933 | bExists = file_access(zFull, X_OK); |
| 934 | +#endif | |
| 935 | + if( bExists==0 && ePrint ){ | |
| 936 | + fossil_print("%s\n", zFull); | |
| 937 | + } | |
| 916 | 938 | fossil_free(zFull); |
| 917 | - if( bExists==0 ) return 1; | |
| 939 | + if( bExists==0 ){ | |
| 940 | + if( ePrint<2 ) return 1; | |
| 941 | + bFound = 1; | |
| 942 | + } | |
| 918 | 943 | zPath += i; |
| 919 | 944 | } |
| 920 | - return 0; | |
| 945 | + return bFound; | |
| 921 | 946 | } |
| 922 | -#endif | |
| 923 | -#endif | |
| 924 | 947 | |
| 948 | +/* | |
| 949 | +** COMMAND: which* | |
| 950 | +** | |
| 951 | +** Usage: fossil which [-a] NAME ... | |
| 952 | +** | |
| 953 | +** For each NAME mentioned as an argument, print the first location on the | |
| 954 | +** on PATH of the executable with that name. Or, show all locations on PATH | |
| 955 | +** for each argument if the -a option is used. | |
| 956 | +** | |
| 957 | +** This command is a substitute for the unix "which" command, which is not | |
| 958 | +** always available, especially on Windows. | |
| 959 | +*/ | |
| 960 | +void test_app_on_path(void){ | |
| 961 | + int i; | |
| 962 | + int ePrint = 1; | |
| 963 | + if( find_option("all","a",0)!=0 ) ePrint = 2; | |
| 964 | + verify_all_options(); | |
| 965 | + for(i=2; i<g.argc; i++){ | |
| 966 | + if( fossil_app_on_path(g.argv[i], ePrint)==0 ){ | |
| 967 | + fossil_print("NOT FOUND: %s\n", g.argv[i]); | |
| 968 | + } | |
| 969 | + } | |
| 970 | +} | |
| 925 | 971 | |
| 926 | 972 | /* |
| 927 | 973 | ** Return the name of a command that will launch a web-browser. |
| 928 | 974 | */ |
| 929 | 975 | const char *fossil_web_browser(void){ |
| @@ -938,11 +984,11 @@ | ||
| 938 | 984 | static const char *const azBrowserProg[] = |
| 939 | 985 | { "xdg-open", "gnome-open", "firefox", "google-chrome" }; |
| 940 | 986 | int i; |
| 941 | 987 | zBrowser = "echo"; |
| 942 | 988 | for(i=0; i<count(azBrowserProg); i++){ |
| 943 | - if( binaryOnPath(azBrowserProg[i]) ){ | |
| 989 | + if( fossil_app_on_path(azBrowserProg[i],0) ){ | |
| 944 | 990 | zBrowser = azBrowserProg[i]; |
| 945 | 991 | break; |
| 946 | 992 | } |
| 947 | 993 | } |
| 948 | 994 | zBrowser = mprintf("%s 2>/dev/null", zBrowser); |
| 949 | 995 |
| --- src/util.c | |
| +++ src/util.c | |
| @@ -670,28 +670,33 @@ | |
| 670 | ** Search algorithm: |
| 671 | ** (1) The local "editor" setting |
| 672 | ** (2) The global "editor" setting |
| 673 | ** (3) The VISUAL environment variable |
| 674 | ** (4) The EDITOR environment variable |
| 675 | ** (5) (Windows only:) "notepad.exe" |
| 676 | */ |
| 677 | const char *fossil_text_editor(void){ |
| 678 | const char *zEditor = db_get("editor", 0); |
| 679 | if( zEditor==0 ){ |
| 680 | zEditor = fossil_getenv("VISUAL"); |
| 681 | } |
| 682 | if( zEditor==0 ){ |
| 683 | zEditor = fossil_getenv("EDITOR"); |
| 684 | } |
| 685 | #if defined(_WIN32) || defined(__CYGWIN__) |
| 686 | if( zEditor==0 ){ |
| 687 | zEditor = mprintf("%s\\notepad.exe", fossil_getenv("SYSTEMROOT")); |
| 688 | #if defined(__CYGWIN__) |
| 689 | zEditor = fossil_utf8_to_path(zEditor, 0); |
| 690 | #endif |
| 691 | } |
| 692 | #endif |
| 693 | return zEditor; |
| 694 | } |
| 695 | |
| 696 | /* |
| 697 | ** Construct a temporary filename. |
| @@ -895,35 +900,76 @@ | |
| 895 | return n< 10 ? 1 : n< 100 ? 2 : n< 1000 ? 3 |
| 896 | : n< 10000 ? 4 : n< 100000 ? 5 : n< 1000000 ? 6 |
| 897 | : n<10000000 ? 7 : n<100000000 ? 8 : n<1000000000 ? 9 : 10; |
| 898 | } |
| 899 | |
| 900 | #if !defined(_WIN32) |
| 901 | #if !defined(__DARWIN__) && !defined(__APPLE__) && !defined(__HAIKU__) |
| 902 | /* |
| 903 | ** Search for an executable on the PATH environment variable. |
| 904 | ** Return true (1) if found and false (0) if not found. |
| 905 | */ |
| 906 | static int binaryOnPath(const char *zBinary){ |
| 907 | const char *zPath = fossil_getenv("PATH"); |
| 908 | char *zFull; |
| 909 | int i; |
| 910 | int bExists; |
| 911 | while( zPath && zPath[0] ){ |
| 912 | while( zPath[0]==':' ) zPath++; |
| 913 | for(i=0; zPath[i] && zPath[i]!=':'; i++){} |
| 914 | zFull = mprintf("%.*s/%s", i, zPath, zBinary); |
| 915 | bExists = file_access(zFull, X_OK); |
| 916 | fossil_free(zFull); |
| 917 | if( bExists==0 ) return 1; |
| 918 | zPath += i; |
| 919 | } |
| 920 | return 0; |
| 921 | } |
| 922 | #endif |
| 923 | #endif |
| 924 | |
| 925 | |
| 926 | /* |
| 927 | ** Return the name of a command that will launch a web-browser. |
| 928 | */ |
| 929 | const char *fossil_web_browser(void){ |
| @@ -938,11 +984,11 @@ | |
| 938 | static const char *const azBrowserProg[] = |
| 939 | { "xdg-open", "gnome-open", "firefox", "google-chrome" }; |
| 940 | int i; |
| 941 | zBrowser = "echo"; |
| 942 | for(i=0; i<count(azBrowserProg); i++){ |
| 943 | if( binaryOnPath(azBrowserProg[i]) ){ |
| 944 | zBrowser = azBrowserProg[i]; |
| 945 | break; |
| 946 | } |
| 947 | } |
| 948 | zBrowser = mprintf("%s 2>/dev/null", zBrowser); |
| 949 |
| --- src/util.c | |
| +++ src/util.c | |
| @@ -670,28 +670,33 @@ | |
| 670 | ** Search algorithm: |
| 671 | ** (1) The local "editor" setting |
| 672 | ** (2) The global "editor" setting |
| 673 | ** (3) The VISUAL environment variable |
| 674 | ** (4) The EDITOR environment variable |
| 675 | ** (5) Any of the following programs that are available: |
| 676 | ** notepad, nano, pico, jove, edit, vi, vim, ed, |
| 677 | */ |
| 678 | const char *fossil_text_editor(void){ |
| 679 | const char *zEditor = db_get("editor", 0); |
| 680 | const char *azStdEd[] = { |
| 681 | "notepad", "nano", "pico", "jove", "edit", "vi", "vim", "ed" |
| 682 | }; |
| 683 | int i = 0; |
| 684 | if( zEditor==0 ){ |
| 685 | zEditor = fossil_getenv("VISUAL"); |
| 686 | } |
| 687 | if( zEditor==0 ){ |
| 688 | zEditor = fossil_getenv("EDITOR"); |
| 689 | } |
| 690 | while( zEditor==0 && i<count(azStdEd) ){ |
| 691 | if( fossil_app_on_path(azStdEd[i],0) ){ |
| 692 | zEditor = azStdEd[i]; |
| 693 | }else{ |
| 694 | i++; |
| 695 | } |
| 696 | } |
| 697 | if( zEditor && is_false(zEditor) ) zEditor = 0; |
| 698 | return zEditor; |
| 699 | } |
| 700 | |
| 701 | /* |
| 702 | ** Construct a temporary filename. |
| @@ -895,35 +900,76 @@ | |
| 900 | return n< 10 ? 1 : n< 100 ? 2 : n< 1000 ? 3 |
| 901 | : n< 10000 ? 4 : n< 100000 ? 5 : n< 1000000 ? 6 |
| 902 | : n<10000000 ? 7 : n<100000000 ? 8 : n<1000000000 ? 9 : 10; |
| 903 | } |
| 904 | |
| 905 | /* |
| 906 | ** Search for an executable on the PATH environment variable. |
| 907 | ** Return true (1) if found and false (0) if not found. |
| 908 | ** |
| 909 | ** Print the full pathname of the first location if ePrint==1. Print |
| 910 | ** all pathnames for the executable if ePrint==2 or more. |
| 911 | */ |
| 912 | int fossil_app_on_path(const char *zBinary, int ePrint){ |
| 913 | const char *zPath = fossil_getenv("PATH"); |
| 914 | char *zFull; |
| 915 | int i; |
| 916 | int bExists; |
| 917 | int bFound = 0; |
| 918 | while( zPath && zPath[0] ){ |
| 919 | #ifdef _WIN32 |
| 920 | while( zPath[0]==';' ) zPath++; |
| 921 | for(i=0; zPath[i] && zPath[i]!=';'; i++){} |
| 922 | zFull = mprintf("%.*s\\%s.exe", i, zPath, zBinary); |
| 923 | bExists = file_access(zFull, R_OK); |
| 924 | if( bExists!=0 ){ |
| 925 | fossil_free(zFull); |
| 926 | zFull = mprintf("%.*s\\%s.bat", i, zPath, zBinary); |
| 927 | bExists = file_access(zFull, R_OK); |
| 928 | } |
| 929 | #else |
| 930 | while( zPath[0]==':' ) zPath++; |
| 931 | for(i=0; zPath[i] && zPath[i]!=':'; i++){} |
| 932 | zFull = mprintf("%.*s/%s", i, zPath, zBinary); |
| 933 | bExists = file_access(zFull, X_OK); |
| 934 | #endif |
| 935 | if( bExists==0 && ePrint ){ |
| 936 | fossil_print("%s\n", zFull); |
| 937 | } |
| 938 | fossil_free(zFull); |
| 939 | if( bExists==0 ){ |
| 940 | if( ePrint<2 ) return 1; |
| 941 | bFound = 1; |
| 942 | } |
| 943 | zPath += i; |
| 944 | } |
| 945 | return bFound; |
| 946 | } |
| 947 | |
| 948 | /* |
| 949 | ** COMMAND: which* |
| 950 | ** |
| 951 | ** Usage: fossil which [-a] NAME ... |
| 952 | ** |
| 953 | ** For each NAME mentioned as an argument, print the first location on the |
| 954 | ** on PATH of the executable with that name. Or, show all locations on PATH |
| 955 | ** for each argument if the -a option is used. |
| 956 | ** |
| 957 | ** This command is a substitute for the unix "which" command, which is not |
| 958 | ** always available, especially on Windows. |
| 959 | */ |
| 960 | void test_app_on_path(void){ |
| 961 | int i; |
| 962 | int ePrint = 1; |
| 963 | if( find_option("all","a",0)!=0 ) ePrint = 2; |
| 964 | verify_all_options(); |
| 965 | for(i=2; i<g.argc; i++){ |
| 966 | if( fossil_app_on_path(g.argv[i], ePrint)==0 ){ |
| 967 | fossil_print("NOT FOUND: %s\n", g.argv[i]); |
| 968 | } |
| 969 | } |
| 970 | } |
| 971 | |
| 972 | /* |
| 973 | ** Return the name of a command that will launch a web-browser. |
| 974 | */ |
| 975 | const char *fossil_web_browser(void){ |
| @@ -938,11 +984,11 @@ | |
| 984 | static const char *const azBrowserProg[] = |
| 985 | { "xdg-open", "gnome-open", "firefox", "google-chrome" }; |
| 986 | int i; |
| 987 | zBrowser = "echo"; |
| 988 | for(i=0; i<count(azBrowserProg); i++){ |
| 989 | if( fossil_app_on_path(azBrowserProg[i],0) ){ |
| 990 | zBrowser = azBrowserProg[i]; |
| 991 | break; |
| 992 | } |
| 993 | } |
| 994 | zBrowser = mprintf("%s 2>/dev/null", zBrowser); |
| 995 |
+54
-13
| --- src/wiki.c | ||
| +++ src/wiki.c | ||
| @@ -21,10 +21,13 @@ | ||
| 21 | 21 | #include "config.h" |
| 22 | 22 | #include <assert.h> |
| 23 | 23 | #include <ctype.h> |
| 24 | 24 | #include "wiki.h" |
| 25 | 25 | |
| 26 | +#define has_prefix(literal_prfx, zStr) \ | |
| 27 | + (fossil_strncmp((zStr), "" literal_prfx, (sizeof literal_prfx)-1)==0) | |
| 28 | + | |
| 26 | 29 | /* |
| 27 | 30 | ** Return true if the input string is a well-formed wiki page name. |
| 28 | 31 | ** |
| 29 | 32 | ** Well-formed wiki page names do not begin or end with whitespace, |
| 30 | 33 | ** and do not contain tabs or other control characters and do not |
| @@ -410,29 +413,33 @@ | ||
| 410 | 413 | # define WIKITYPE_UNKNOWN (-1) |
| 411 | 414 | # define WIKITYPE_NORMAL 0 |
| 412 | 415 | # define WIKITYPE_BRANCH 1 |
| 413 | 416 | # define WIKITYPE_CHECKIN 2 |
| 414 | 417 | # define WIKITYPE_TAG 3 |
| 418 | +# define WIKITYPE_TICKET 4 | |
| 415 | 419 | #endif |
| 416 | 420 | |
| 417 | 421 | /* |
| 418 | 422 | ** Figure out what type of wiki page we are dealing with. |
| 419 | 423 | */ |
| 420 | 424 | int wiki_page_type(const char *zPageName){ |
| 421 | 425 | if( db_get_boolean("wiki-about",1)==0 ){ |
| 422 | 426 | return WIKITYPE_NORMAL; |
| 423 | 427 | }else |
| 424 | - if( sqlite3_strglob("checkin/*", zPageName)==0 | |
| 428 | + if( has_prefix("checkin/", zPageName) | |
| 425 | 429 | && db_exists("SELECT 1 FROM blob WHERE uuid=%Q",zPageName+8) |
| 426 | 430 | ){ |
| 427 | 431 | return WIKITYPE_CHECKIN; |
| 428 | 432 | }else |
| 429 | - if( sqlite3_strglob("branch/*", zPageName)==0 ){ | |
| 433 | + if( has_prefix("branch/", zPageName) ){ | |
| 430 | 434 | return WIKITYPE_BRANCH; |
| 431 | 435 | }else |
| 432 | - if( sqlite3_strglob("tag/*", zPageName)==0 ){ | |
| 436 | + if( has_prefix("tag/", zPageName) ){ | |
| 433 | 437 | return WIKITYPE_TAG; |
| 438 | + }else | |
| 439 | + if( has_prefix("ticket/", zPageName) ){ | |
| 440 | + return WIKITYPE_TICKET; | |
| 434 | 441 | } |
| 435 | 442 | return WIKITYPE_NORMAL; |
| 436 | 443 | } |
| 437 | 444 | |
| 438 | 445 | /* |
| @@ -442,10 +449,11 @@ | ||
| 442 | 449 | const char * wiki_page_type_name(const char *zPageName){ |
| 443 | 450 | switch(wiki_page_type(zPageName)){ |
| 444 | 451 | case WIKITYPE_CHECKIN: return "checkin"; |
| 445 | 452 | case WIKITYPE_BRANCH: return "branch"; |
| 446 | 453 | case WIKITYPE_TAG: return "tag"; |
| 454 | + case WIKITYPE_TICKET: return "ticket"; | |
| 447 | 455 | case WIKITYPE_NORMAL: |
| 448 | 456 | default: return "normal"; |
| 449 | 457 | } |
| 450 | 458 | } |
| 451 | 459 | |
| @@ -501,10 +509,20 @@ | ||
| 501 | 509 | style_header("Notes About Tag %h", zPageName); |
| 502 | 510 | style_submenu_element("Tag Timeline","%R/timeline?t=%t",zPageName); |
| 503 | 511 | } |
| 504 | 512 | break; |
| 505 | 513 | } |
| 514 | + case WIKITYPE_TICKET: { | |
| 515 | + zPageName += 7; | |
| 516 | + if( zExtra[0]==0 && !P("p") ){ | |
| 517 | + cgi_redirectf("%R/tktview/%s",zPageName); | |
| 518 | + }else{ | |
| 519 | + style_header("Notes About Ticket %h", zPageName); | |
| 520 | + style_submenu_element("Ticket","%R/tktview/%s",zPageName); | |
| 521 | + } | |
| 522 | + break; | |
| 523 | + } | |
| 506 | 524 | } |
| 507 | 525 | return eType; |
| 508 | 526 | } |
| 509 | 527 | |
| 510 | 528 | /* |
| @@ -516,15 +534,19 @@ | ||
| 516 | 534 | */ |
| 517 | 535 | static int wiki_special_permission(const char *zPageName){ |
| 518 | 536 | if( strncmp(zPageName,"branch/",7)!=0 |
| 519 | 537 | && strncmp(zPageName,"checkin/",8)!=0 |
| 520 | 538 | && strncmp(zPageName,"tag/",4)!=0 |
| 539 | + && strncmp(zPageName,"ticket/",7)!=0 | |
| 521 | 540 | ){ |
| 522 | 541 | return 1; |
| 523 | 542 | } |
| 524 | 543 | if( db_get_boolean("wiki-about",1)==0 ){ |
| 525 | 544 | return 1; |
| 545 | + } | |
| 546 | + if( strncmp(zPageName,"ticket/",7)==0 ){ | |
| 547 | + return g.perm.WrTkt; | |
| 526 | 548 | } |
| 527 | 549 | return g.perm.Write; |
| 528 | 550 | } |
| 529 | 551 | |
| 530 | 552 | /* |
| @@ -1968,11 +1990,12 @@ | ||
| 1968 | 1990 | style_submenu_element("All", "%R/wcontent?all=1"); |
| 1969 | 1991 | } |
| 1970 | 1992 | cgi_check_for_malice(); |
| 1971 | 1993 | showCkBr = db_exists( |
| 1972 | 1994 | "SELECT tag.tagname AS tn FROM tag JOIN tagxref USING(tagid) " |
| 1973 | - "WHERE ( tn GLOB 'wiki-checkin/*' OR tn GLOB 'wiki-branch/*' ) " | |
| 1995 | + "WHERE ( tn GLOB 'wiki-checkin/*' OR tn GLOB 'wiki-branch/*' OR " | |
| 1996 | + " tn GLOB 'wiki-tag/*' OR tn GLOB 'wiki-ticket/*' ) " | |
| 1974 | 1997 | " AND TYPEOF(tagxref.value+0)='integer'" ); |
| 1975 | 1998 | if( showCkBr ){ |
| 1976 | 1999 | showCkBr = P("showckbr")!=0; |
| 1977 | 2000 | style_submenu_checkbox("showckbr", "Show associated wikis", 0, 0); |
| 1978 | 2001 | } |
| @@ -1998,18 +2021,20 @@ | ||
| 1998 | 2021 | char *zAge; |
| 1999 | 2022 | int wcnt = db_column_int(&q, 4); |
| 2000 | 2023 | char *zWDisplayName; |
| 2001 | 2024 | |
| 2002 | 2025 | if( !showCkBr && |
| 2003 | - (sqlite3_strglob("checkin/*", zWName)==0 || | |
| 2004 | - sqlite3_strglob("branch/*", zWName)==0) ){ | |
| 2026 | + (has_prefix("checkin/", zWName) || | |
| 2027 | + has_prefix("branch/", zWName) || | |
| 2028 | + has_prefix("tag/", zWName) || | |
| 2029 | + has_prefix("ticket/", zWName) )){ | |
| 2005 | 2030 | continue; |
| 2006 | 2031 | } |
| 2007 | - if( sqlite3_strglob("checkin/*", zWName)==0 ){ | |
| 2032 | + if( has_prefix("checkin/",zWName) || has_prefix("ticket/",zWName) ){ | |
| 2008 | 2033 | zWDisplayName = mprintf("%.25s...", zWName); |
| 2009 | 2034 | }else{ |
| 2010 | - zWDisplayName = mprintf("%s", zWName); | |
| 2035 | + zWDisplayName = fossil_strdup(zWName); | |
| 2011 | 2036 | } |
| 2012 | 2037 | if( wrid==0 ){ |
| 2013 | 2038 | if( !showAll ) continue; |
| 2014 | 2039 | @ <tr><td data-sortkey="%h(zSort)">\ |
| 2015 | 2040 | @ %z(href("%R/whistory?name=%T",zWName))<s>%h(zWDisplayName)</s></a></td> |
| @@ -2501,12 +2526,14 @@ | ||
| 2501 | 2526 | const int wrid = db_column_int(&q, 2); |
| 2502 | 2527 | if(!showAll && !wrid){ |
| 2503 | 2528 | continue; |
| 2504 | 2529 | } |
| 2505 | 2530 | if( !showCkBr && |
| 2506 | - (sqlite3_strglob("checkin/*", zName)==0 || | |
| 2507 | - sqlite3_strglob("branch/*", zName)==0) ){ | |
| 2531 | + (has_prefix("checkin/", zName) || | |
| 2532 | + has_prefix("branch/", zName) || | |
| 2533 | + has_prefix("tag/", zName) || | |
| 2534 | + has_prefix("ticket/", zName) ) ){ | |
| 2508 | 2535 | continue; |
| 2509 | 2536 | } |
| 2510 | 2537 | if( showIds ){ |
| 2511 | 2538 | const char *zUuid = db_column_text(&q, 1); |
| 2512 | 2539 | fossil_print("%s ",zUuid); |
| @@ -2551,17 +2578,30 @@ | ||
| 2551 | 2578 | } |
| 2552 | 2579 | |
| 2553 | 2580 | /* |
| 2554 | 2581 | ** Add an "Wiki" button in a submenu that links to the read-wiki page. |
| 2555 | 2582 | */ |
| 2556 | -static void wiki_submenu_to_edit_wiki( | |
| 2583 | +static void wiki_submenu_to_read_wiki( | |
| 2557 | 2584 | const char *zPrefix, /* "branch", "tag", or "checkin" */ |
| 2558 | 2585 | const char *zName, /* Name of the object */ |
| 2559 | 2586 | unsigned int mFlags /* Zero or more WIKIASSOC_* flags */ |
| 2560 | 2587 | ){ |
| 2561 | 2588 | if( g.perm.RdWiki && (mFlags & WIKIASSOC_MENU_READ)!=0 ){ |
| 2562 | - style_submenu_element("Wiki", "%R/wikiedit?name=%s/%t", zPrefix, zName); | |
| 2589 | + style_submenu_element("Wiki", "%R/wiki?name=%s/%t", zPrefix, zName); | |
| 2590 | + } | |
| 2591 | +} | |
| 2592 | + | |
| 2593 | +/* | |
| 2594 | +** Add an "Edit Wiki" button in a submenu that links to the edit-wiki page. | |
| 2595 | +*/ | |
| 2596 | +static void wiki_submenu_to_edit_wiki( | |
| 2597 | + const char *zPrefix, /* "branch", "tag", or "checkin" */ | |
| 2598 | + const char *zName, /* Name of the object */ | |
| 2599 | + unsigned int mFlags /* Zero or more WIKIASSOC_* flags */ | |
| 2600 | +){ | |
| 2601 | + if( g.perm.WrWiki && (mFlags & WIKIASSOC_MENU_WRITE)!=0 ){ | |
| 2602 | + style_submenu_element("Edit Wiki", "%R/wikiedit?name=%s/%t", zPrefix, zName); | |
| 2563 | 2603 | } |
| 2564 | 2604 | } |
| 2565 | 2605 | |
| 2566 | 2606 | /* |
| 2567 | 2607 | ** Check to see if there exists a wiki page with a name zPrefix/zName. |
| @@ -2569,11 +2609,11 @@ | ||
| 2569 | 2609 | ** return true. |
| 2570 | 2610 | ** |
| 2571 | 2611 | ** If there is no such wiki page, return false. |
| 2572 | 2612 | */ |
| 2573 | 2613 | int wiki_render_associated( |
| 2574 | - const char *zPrefix, /* "branch", "tag", or "checkin" */ | |
| 2614 | + const char *zPrefix, /* "branch", "tag", "ticket", or "checkin" */ | |
| 2575 | 2615 | const char *zName, /* Name of the object */ |
| 2576 | 2616 | unsigned int mFlags /* Zero or more WIKIASSOC_* flags */ |
| 2577 | 2617 | ){ |
| 2578 | 2618 | int rid; |
| 2579 | 2619 | Manifest *pWiki; |
| @@ -2601,10 +2641,11 @@ | ||
| 2601 | 2641 | if( blob_size(&title) ){ |
| 2602 | 2642 | @ <div class="section accordion">%h(blob_str(&title))</div> |
| 2603 | 2643 | }else{ |
| 2604 | 2644 | wiki_section_label(zPrefix, zName, mFlags); |
| 2605 | 2645 | } |
| 2646 | + wiki_submenu_to_read_wiki(zPrefix, zName, mFlags); | |
| 2606 | 2647 | wiki_submenu_to_edit_wiki(zPrefix, zName, mFlags); |
| 2607 | 2648 | @ <div class="accordion_panel"> |
| 2608 | 2649 | safe_html_context(DOCSRC_WIKI); |
| 2609 | 2650 | safe_html(&tail); |
| 2610 | 2651 | convert_href_and_output(&tail); |
| 2611 | 2652 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -21,10 +21,13 @@ | |
| 21 | #include "config.h" |
| 22 | #include <assert.h> |
| 23 | #include <ctype.h> |
| 24 | #include "wiki.h" |
| 25 | |
| 26 | /* |
| 27 | ** Return true if the input string is a well-formed wiki page name. |
| 28 | ** |
| 29 | ** Well-formed wiki page names do not begin or end with whitespace, |
| 30 | ** and do not contain tabs or other control characters and do not |
| @@ -410,29 +413,33 @@ | |
| 410 | # define WIKITYPE_UNKNOWN (-1) |
| 411 | # define WIKITYPE_NORMAL 0 |
| 412 | # define WIKITYPE_BRANCH 1 |
| 413 | # define WIKITYPE_CHECKIN 2 |
| 414 | # define WIKITYPE_TAG 3 |
| 415 | #endif |
| 416 | |
| 417 | /* |
| 418 | ** Figure out what type of wiki page we are dealing with. |
| 419 | */ |
| 420 | int wiki_page_type(const char *zPageName){ |
| 421 | if( db_get_boolean("wiki-about",1)==0 ){ |
| 422 | return WIKITYPE_NORMAL; |
| 423 | }else |
| 424 | if( sqlite3_strglob("checkin/*", zPageName)==0 |
| 425 | && db_exists("SELECT 1 FROM blob WHERE uuid=%Q",zPageName+8) |
| 426 | ){ |
| 427 | return WIKITYPE_CHECKIN; |
| 428 | }else |
| 429 | if( sqlite3_strglob("branch/*", zPageName)==0 ){ |
| 430 | return WIKITYPE_BRANCH; |
| 431 | }else |
| 432 | if( sqlite3_strglob("tag/*", zPageName)==0 ){ |
| 433 | return WIKITYPE_TAG; |
| 434 | } |
| 435 | return WIKITYPE_NORMAL; |
| 436 | } |
| 437 | |
| 438 | /* |
| @@ -442,10 +449,11 @@ | |
| 442 | const char * wiki_page_type_name(const char *zPageName){ |
| 443 | switch(wiki_page_type(zPageName)){ |
| 444 | case WIKITYPE_CHECKIN: return "checkin"; |
| 445 | case WIKITYPE_BRANCH: return "branch"; |
| 446 | case WIKITYPE_TAG: return "tag"; |
| 447 | case WIKITYPE_NORMAL: |
| 448 | default: return "normal"; |
| 449 | } |
| 450 | } |
| 451 | |
| @@ -501,10 +509,20 @@ | |
| 501 | style_header("Notes About Tag %h", zPageName); |
| 502 | style_submenu_element("Tag Timeline","%R/timeline?t=%t",zPageName); |
| 503 | } |
| 504 | break; |
| 505 | } |
| 506 | } |
| 507 | return eType; |
| 508 | } |
| 509 | |
| 510 | /* |
| @@ -516,15 +534,19 @@ | |
| 516 | */ |
| 517 | static int wiki_special_permission(const char *zPageName){ |
| 518 | if( strncmp(zPageName,"branch/",7)!=0 |
| 519 | && strncmp(zPageName,"checkin/",8)!=0 |
| 520 | && strncmp(zPageName,"tag/",4)!=0 |
| 521 | ){ |
| 522 | return 1; |
| 523 | } |
| 524 | if( db_get_boolean("wiki-about",1)==0 ){ |
| 525 | return 1; |
| 526 | } |
| 527 | return g.perm.Write; |
| 528 | } |
| 529 | |
| 530 | /* |
| @@ -1968,11 +1990,12 @@ | |
| 1968 | style_submenu_element("All", "%R/wcontent?all=1"); |
| 1969 | } |
| 1970 | cgi_check_for_malice(); |
| 1971 | showCkBr = db_exists( |
| 1972 | "SELECT tag.tagname AS tn FROM tag JOIN tagxref USING(tagid) " |
| 1973 | "WHERE ( tn GLOB 'wiki-checkin/*' OR tn GLOB 'wiki-branch/*' ) " |
| 1974 | " AND TYPEOF(tagxref.value+0)='integer'" ); |
| 1975 | if( showCkBr ){ |
| 1976 | showCkBr = P("showckbr")!=0; |
| 1977 | style_submenu_checkbox("showckbr", "Show associated wikis", 0, 0); |
| 1978 | } |
| @@ -1998,18 +2021,20 @@ | |
| 1998 | char *zAge; |
| 1999 | int wcnt = db_column_int(&q, 4); |
| 2000 | char *zWDisplayName; |
| 2001 | |
| 2002 | if( !showCkBr && |
| 2003 | (sqlite3_strglob("checkin/*", zWName)==0 || |
| 2004 | sqlite3_strglob("branch/*", zWName)==0) ){ |
| 2005 | continue; |
| 2006 | } |
| 2007 | if( sqlite3_strglob("checkin/*", zWName)==0 ){ |
| 2008 | zWDisplayName = mprintf("%.25s...", zWName); |
| 2009 | }else{ |
| 2010 | zWDisplayName = mprintf("%s", zWName); |
| 2011 | } |
| 2012 | if( wrid==0 ){ |
| 2013 | if( !showAll ) continue; |
| 2014 | @ <tr><td data-sortkey="%h(zSort)">\ |
| 2015 | @ %z(href("%R/whistory?name=%T",zWName))<s>%h(zWDisplayName)</s></a></td> |
| @@ -2501,12 +2526,14 @@ | |
| 2501 | const int wrid = db_column_int(&q, 2); |
| 2502 | if(!showAll && !wrid){ |
| 2503 | continue; |
| 2504 | } |
| 2505 | if( !showCkBr && |
| 2506 | (sqlite3_strglob("checkin/*", zName)==0 || |
| 2507 | sqlite3_strglob("branch/*", zName)==0) ){ |
| 2508 | continue; |
| 2509 | } |
| 2510 | if( showIds ){ |
| 2511 | const char *zUuid = db_column_text(&q, 1); |
| 2512 | fossil_print("%s ",zUuid); |
| @@ -2551,17 +2578,30 @@ | |
| 2551 | } |
| 2552 | |
| 2553 | /* |
| 2554 | ** Add an "Wiki" button in a submenu that links to the read-wiki page. |
| 2555 | */ |
| 2556 | static void wiki_submenu_to_edit_wiki( |
| 2557 | const char *zPrefix, /* "branch", "tag", or "checkin" */ |
| 2558 | const char *zName, /* Name of the object */ |
| 2559 | unsigned int mFlags /* Zero or more WIKIASSOC_* flags */ |
| 2560 | ){ |
| 2561 | if( g.perm.RdWiki && (mFlags & WIKIASSOC_MENU_READ)!=0 ){ |
| 2562 | style_submenu_element("Wiki", "%R/wikiedit?name=%s/%t", zPrefix, zName); |
| 2563 | } |
| 2564 | } |
| 2565 | |
| 2566 | /* |
| 2567 | ** Check to see if there exists a wiki page with a name zPrefix/zName. |
| @@ -2569,11 +2609,11 @@ | |
| 2569 | ** return true. |
| 2570 | ** |
| 2571 | ** If there is no such wiki page, return false. |
| 2572 | */ |
| 2573 | int wiki_render_associated( |
| 2574 | const char *zPrefix, /* "branch", "tag", or "checkin" */ |
| 2575 | const char *zName, /* Name of the object */ |
| 2576 | unsigned int mFlags /* Zero or more WIKIASSOC_* flags */ |
| 2577 | ){ |
| 2578 | int rid; |
| 2579 | Manifest *pWiki; |
| @@ -2601,10 +2641,11 @@ | |
| 2601 | if( blob_size(&title) ){ |
| 2602 | @ <div class="section accordion">%h(blob_str(&title))</div> |
| 2603 | }else{ |
| 2604 | wiki_section_label(zPrefix, zName, mFlags); |
| 2605 | } |
| 2606 | wiki_submenu_to_edit_wiki(zPrefix, zName, mFlags); |
| 2607 | @ <div class="accordion_panel"> |
| 2608 | safe_html_context(DOCSRC_WIKI); |
| 2609 | safe_html(&tail); |
| 2610 | convert_href_and_output(&tail); |
| 2611 |
| --- src/wiki.c | |
| +++ src/wiki.c | |
| @@ -21,10 +21,13 @@ | |
| 21 | #include "config.h" |
| 22 | #include <assert.h> |
| 23 | #include <ctype.h> |
| 24 | #include "wiki.h" |
| 25 | |
| 26 | #define has_prefix(literal_prfx, zStr) \ |
| 27 | (fossil_strncmp((zStr), "" literal_prfx, (sizeof literal_prfx)-1)==0) |
| 28 | |
| 29 | /* |
| 30 | ** Return true if the input string is a well-formed wiki page name. |
| 31 | ** |
| 32 | ** Well-formed wiki page names do not begin or end with whitespace, |
| 33 | ** and do not contain tabs or other control characters and do not |
| @@ -410,29 +413,33 @@ | |
| 413 | # define WIKITYPE_UNKNOWN (-1) |
| 414 | # define WIKITYPE_NORMAL 0 |
| 415 | # define WIKITYPE_BRANCH 1 |
| 416 | # define WIKITYPE_CHECKIN 2 |
| 417 | # define WIKITYPE_TAG 3 |
| 418 | # define WIKITYPE_TICKET 4 |
| 419 | #endif |
| 420 | |
| 421 | /* |
| 422 | ** Figure out what type of wiki page we are dealing with. |
| 423 | */ |
| 424 | int wiki_page_type(const char *zPageName){ |
| 425 | if( db_get_boolean("wiki-about",1)==0 ){ |
| 426 | return WIKITYPE_NORMAL; |
| 427 | }else |
| 428 | if( has_prefix("checkin/", zPageName) |
| 429 | && db_exists("SELECT 1 FROM blob WHERE uuid=%Q",zPageName+8) |
| 430 | ){ |
| 431 | return WIKITYPE_CHECKIN; |
| 432 | }else |
| 433 | if( has_prefix("branch/", zPageName) ){ |
| 434 | return WIKITYPE_BRANCH; |
| 435 | }else |
| 436 | if( has_prefix("tag/", zPageName) ){ |
| 437 | return WIKITYPE_TAG; |
| 438 | }else |
| 439 | if( has_prefix("ticket/", zPageName) ){ |
| 440 | return WIKITYPE_TICKET; |
| 441 | } |
| 442 | return WIKITYPE_NORMAL; |
| 443 | } |
| 444 | |
| 445 | /* |
| @@ -442,10 +449,11 @@ | |
| 449 | const char * wiki_page_type_name(const char *zPageName){ |
| 450 | switch(wiki_page_type(zPageName)){ |
| 451 | case WIKITYPE_CHECKIN: return "checkin"; |
| 452 | case WIKITYPE_BRANCH: return "branch"; |
| 453 | case WIKITYPE_TAG: return "tag"; |
| 454 | case WIKITYPE_TICKET: return "ticket"; |
| 455 | case WIKITYPE_NORMAL: |
| 456 | default: return "normal"; |
| 457 | } |
| 458 | } |
| 459 | |
| @@ -501,10 +509,20 @@ | |
| 509 | style_header("Notes About Tag %h", zPageName); |
| 510 | style_submenu_element("Tag Timeline","%R/timeline?t=%t",zPageName); |
| 511 | } |
| 512 | break; |
| 513 | } |
| 514 | case WIKITYPE_TICKET: { |
| 515 | zPageName += 7; |
| 516 | if( zExtra[0]==0 && !P("p") ){ |
| 517 | cgi_redirectf("%R/tktview/%s",zPageName); |
| 518 | }else{ |
| 519 | style_header("Notes About Ticket %h", zPageName); |
| 520 | style_submenu_element("Ticket","%R/tktview/%s",zPageName); |
| 521 | } |
| 522 | break; |
| 523 | } |
| 524 | } |
| 525 | return eType; |
| 526 | } |
| 527 | |
| 528 | /* |
| @@ -516,15 +534,19 @@ | |
| 534 | */ |
| 535 | static int wiki_special_permission(const char *zPageName){ |
| 536 | if( strncmp(zPageName,"branch/",7)!=0 |
| 537 | && strncmp(zPageName,"checkin/",8)!=0 |
| 538 | && strncmp(zPageName,"tag/",4)!=0 |
| 539 | && strncmp(zPageName,"ticket/",7)!=0 |
| 540 | ){ |
| 541 | return 1; |
| 542 | } |
| 543 | if( db_get_boolean("wiki-about",1)==0 ){ |
| 544 | return 1; |
| 545 | } |
| 546 | if( strncmp(zPageName,"ticket/",7)==0 ){ |
| 547 | return g.perm.WrTkt; |
| 548 | } |
| 549 | return g.perm.Write; |
| 550 | } |
| 551 | |
| 552 | /* |
| @@ -1968,11 +1990,12 @@ | |
| 1990 | style_submenu_element("All", "%R/wcontent?all=1"); |
| 1991 | } |
| 1992 | cgi_check_for_malice(); |
| 1993 | showCkBr = db_exists( |
| 1994 | "SELECT tag.tagname AS tn FROM tag JOIN tagxref USING(tagid) " |
| 1995 | "WHERE ( tn GLOB 'wiki-checkin/*' OR tn GLOB 'wiki-branch/*' OR " |
| 1996 | " tn GLOB 'wiki-tag/*' OR tn GLOB 'wiki-ticket/*' ) " |
| 1997 | " AND TYPEOF(tagxref.value+0)='integer'" ); |
| 1998 | if( showCkBr ){ |
| 1999 | showCkBr = P("showckbr")!=0; |
| 2000 | style_submenu_checkbox("showckbr", "Show associated wikis", 0, 0); |
| 2001 | } |
| @@ -1998,18 +2021,20 @@ | |
| 2021 | char *zAge; |
| 2022 | int wcnt = db_column_int(&q, 4); |
| 2023 | char *zWDisplayName; |
| 2024 | |
| 2025 | if( !showCkBr && |
| 2026 | (has_prefix("checkin/", zWName) || |
| 2027 | has_prefix("branch/", zWName) || |
| 2028 | has_prefix("tag/", zWName) || |
| 2029 | has_prefix("ticket/", zWName) )){ |
| 2030 | continue; |
| 2031 | } |
| 2032 | if( has_prefix("checkin/",zWName) || has_prefix("ticket/",zWName) ){ |
| 2033 | zWDisplayName = mprintf("%.25s...", zWName); |
| 2034 | }else{ |
| 2035 | zWDisplayName = fossil_strdup(zWName); |
| 2036 | } |
| 2037 | if( wrid==0 ){ |
| 2038 | if( !showAll ) continue; |
| 2039 | @ <tr><td data-sortkey="%h(zSort)">\ |
| 2040 | @ %z(href("%R/whistory?name=%T",zWName))<s>%h(zWDisplayName)</s></a></td> |
| @@ -2501,12 +2526,14 @@ | |
| 2526 | const int wrid = db_column_int(&q, 2); |
| 2527 | if(!showAll && !wrid){ |
| 2528 | continue; |
| 2529 | } |
| 2530 | if( !showCkBr && |
| 2531 | (has_prefix("checkin/", zName) || |
| 2532 | has_prefix("branch/", zName) || |
| 2533 | has_prefix("tag/", zName) || |
| 2534 | has_prefix("ticket/", zName) ) ){ |
| 2535 | continue; |
| 2536 | } |
| 2537 | if( showIds ){ |
| 2538 | const char *zUuid = db_column_text(&q, 1); |
| 2539 | fossil_print("%s ",zUuid); |
| @@ -2551,17 +2578,30 @@ | |
| 2578 | } |
| 2579 | |
| 2580 | /* |
| 2581 | ** Add an "Wiki" button in a submenu that links to the read-wiki page. |
| 2582 | */ |
| 2583 | static void wiki_submenu_to_read_wiki( |
| 2584 | const char *zPrefix, /* "branch", "tag", or "checkin" */ |
| 2585 | const char *zName, /* Name of the object */ |
| 2586 | unsigned int mFlags /* Zero or more WIKIASSOC_* flags */ |
| 2587 | ){ |
| 2588 | if( g.perm.RdWiki && (mFlags & WIKIASSOC_MENU_READ)!=0 ){ |
| 2589 | style_submenu_element("Wiki", "%R/wiki?name=%s/%t", zPrefix, zName); |
| 2590 | } |
| 2591 | } |
| 2592 | |
| 2593 | /* |
| 2594 | ** Add an "Edit Wiki" button in a submenu that links to the edit-wiki page. |
| 2595 | */ |
| 2596 | static void wiki_submenu_to_edit_wiki( |
| 2597 | const char *zPrefix, /* "branch", "tag", or "checkin" */ |
| 2598 | const char *zName, /* Name of the object */ |
| 2599 | unsigned int mFlags /* Zero or more WIKIASSOC_* flags */ |
| 2600 | ){ |
| 2601 | if( g.perm.WrWiki && (mFlags & WIKIASSOC_MENU_WRITE)!=0 ){ |
| 2602 | style_submenu_element("Edit Wiki", "%R/wikiedit?name=%s/%t", zPrefix, zName); |
| 2603 | } |
| 2604 | } |
| 2605 | |
| 2606 | /* |
| 2607 | ** Check to see if there exists a wiki page with a name zPrefix/zName. |
| @@ -2569,11 +2609,11 @@ | |
| 2609 | ** return true. |
| 2610 | ** |
| 2611 | ** If there is no such wiki page, return false. |
| 2612 | */ |
| 2613 | int wiki_render_associated( |
| 2614 | const char *zPrefix, /* "branch", "tag", "ticket", or "checkin" */ |
| 2615 | const char *zName, /* Name of the object */ |
| 2616 | unsigned int mFlags /* Zero or more WIKIASSOC_* flags */ |
| 2617 | ){ |
| 2618 | int rid; |
| 2619 | Manifest *pWiki; |
| @@ -2601,10 +2641,11 @@ | |
| 2641 | if( blob_size(&title) ){ |
| 2642 | @ <div class="section accordion">%h(blob_str(&title))</div> |
| 2643 | }else{ |
| 2644 | wiki_section_label(zPrefix, zName, mFlags); |
| 2645 | } |
| 2646 | wiki_submenu_to_read_wiki(zPrefix, zName, mFlags); |
| 2647 | wiki_submenu_to_edit_wiki(zPrefix, zName, mFlags); |
| 2648 | @ <div class="accordion_panel"> |
| 2649 | safe_html_context(DOCSRC_WIKI); |
| 2650 | safe_html(&tail); |
| 2651 | convert_href_and_output(&tail); |
| 2652 |
+367
-181
| --- src/wikiformat.c | ||
| +++ src/wikiformat.c | ||
| @@ -23,22 +23,43 @@ | ||
| 23 | 23 | |
| 24 | 24 | #if INTERFACE |
| 25 | 25 | /* |
| 26 | 26 | ** Allowed wiki transformation operations |
| 27 | 27 | */ |
| 28 | -#define WIKI_HTMLONLY 0x001 /* HTML markup only. No wiki */ | |
| 29 | -#define WIKI_INLINE 0x002 /* Do not surround with <p>..</p> */ | |
| 30 | -#define WIKI_NOBLOCK 0x004 /* No block markup of any kind */ | |
| 31 | -#define WIKI_BUTTONS 0x008 /* Allow sub-menu buttons */ | |
| 32 | -#define WIKI_NOBADLINKS 0x010 /* Ignore broken hyperlinks */ | |
| 33 | -#define WIKI_LINKSONLY 0x020 /* No markup. Only decorate links */ | |
| 34 | -#define WIKI_NEWLINE 0x040 /* Honor \n - break lines at each \n */ | |
| 35 | -#define WIKI_MARKDOWNLINKS 0x080 /* Resolve hyperlinks as in markdown */ | |
| 36 | -#define WIKI_SAFE 0x100 /* Make the result safe for embedding */ | |
| 37 | -#define WIKI_TARGET_BLANK 0x200 /* Hyperlinks go to a new window */ | |
| 38 | -#define WIKI_NOBRACKET 0x400 /* Omit extra [..] around hyperlinks */ | |
| 39 | -#endif | |
| 28 | +#define WIKI_HTMLONLY 0x0001 /* HTML markup only. No wiki */ | |
| 29 | +#define WIKI_INLINE 0x0002 /* Do not surround with <p>..</p> */ | |
| 30 | +/* avalable for reuse: 0x0004 --- formerly WIKI_NOBLOCK */ | |
| 31 | +#define WIKI_BUTTONS 0x0008 /* Allow sub-menu buttons */ | |
| 32 | +#define WIKI_NOBADLINKS 0x0010 /* Ignore broken hyperlinks */ | |
| 33 | +#define WIKI_LINKSONLY 0x0020 /* No markup. Only decorate links */ | |
| 34 | +#define WIKI_NEWLINE 0x0040 /* Honor \n - break lines at each \n */ | |
| 35 | +#define WIKI_MARKDOWNLINKS 0x0080 /* Resolve hyperlinks as in markdown */ | |
| 36 | +#define WIKI_SAFE 0x0100 /* Make the result safe for embedding */ | |
| 37 | +#define WIKI_TARGET_BLANK 0x0200 /* Hyperlinks go to a new window */ | |
| 38 | +#define WIKI_NOBRACKET 0x0400 /* Omit extra [..] around hyperlinks */ | |
| 39 | +#define WIKI_ADMIN 0x0800 /* Ignore g.perm.Hyperlink */ | |
| 40 | +#define WIKI_MARK 0x1000 /* Add <mark>..</mark> around problems */ | |
| 41 | + | |
| 42 | +/* | |
| 43 | +** Return values from wiki_convert | |
| 44 | +*/ | |
| 45 | +#define RENDER_LINK 0x0001 /* One or more hyperlinks rendered */ | |
| 46 | +#define RENDER_ENTITY 0x0002 /* One or more HTML entities (ex: <) */ | |
| 47 | +#define RENDER_TAG 0x0004 /* One or more HTML tags */ | |
| 48 | +#define RENDER_BLOCKTAG 0x0008 /* One or more HTML block tags (ex: <p>) */ | |
| 49 | +#define RENDER_BLOCK 0x0010 /* Block wiki (paragraphs, etc.) */ | |
| 50 | +#define RENDER_MARK 0x0020 /* Output contains <mark>..</mark> */ | |
| 51 | +#define RENDER_BADLINK 0x0100 /* Bad hyperlink syntax seen */ | |
| 52 | +#define RENDER_BADTARGET 0x0200 /* Bad hyperlink target */ | |
| 53 | +#define RENDER_BADTAG 0x0400 /* Bad HTML tag or tag syntax */ | |
| 54 | +#define RENDER_BADENTITY 0x0800 /* Bad HTML entity syntax */ | |
| 55 | +#define RENDER_BADHTML 0x1000 /* Bad HTML seen */ | |
| 56 | +#define RENDER_ERROR 0x8000 /* Some other kind of error */ | |
| 57 | +/* Composite values: */ | |
| 58 | +#define RENDER_ANYERROR 0x9f00 /* Mask for any kind of error */ | |
| 59 | + | |
| 60 | +#endif /* INTERFACE */ | |
| 40 | 61 | |
| 41 | 62 | |
| 42 | 63 | /* |
| 43 | 64 | ** These are the only markup attributes allowed. |
| 44 | 65 | */ |
| @@ -444,20 +465,20 @@ | ||
| 444 | 465 | #define AT_NEWLINE 0x0010000 /* At start of a line */ |
| 445 | 466 | #define AT_PARAGRAPH 0x0020000 /* At start of a paragraph */ |
| 446 | 467 | #define ALLOW_WIKI 0x0040000 /* Allow wiki markup */ |
| 447 | 468 | #define ALLOW_LINKS 0x0080000 /* Allow [...] hyperlinks */ |
| 448 | 469 | #define FONT_MARKUP_ONLY 0x0100000 /* Only allow MUTYPE_FONT markup */ |
| 449 | -#define INLINE_MARKUP_ONLY 0x0200000 /* Allow only "inline" markup */ | |
| 450 | -#define IN_LIST 0x0400000 /* Within wiki <ul> or <ol> */ | |
| 470 | +#define IN_LIST 0x0200000 /* Within wiki <ul> or <ol> */ | |
| 451 | 471 | |
| 452 | 472 | /* |
| 453 | 473 | ** Current state of the rendering engine |
| 454 | 474 | */ |
| 455 | 475 | typedef struct Renderer Renderer; |
| 456 | 476 | struct Renderer { |
| 457 | 477 | Blob *pOut; /* Output appended to this blob */ |
| 458 | 478 | int state; /* Flag that govern rendering */ |
| 479 | + int mRender; /* Mask of RENDER_* values to return */ | |
| 459 | 480 | unsigned renderFlags; /* Flags from the client */ |
| 460 | 481 | int wikiList; /* Current wiki list type */ |
| 461 | 482 | int inVerbatim; /* True in <verbatim> mode */ |
| 462 | 483 | int preVerbState; /* Value of state prior to verbatim */ |
| 463 | 484 | int wantAutoParagraph; /* True if a <p> is desired */ |
| @@ -679,21 +700,26 @@ | ||
| 679 | 700 | static int nextWikiToken(const char *z, Renderer *p, int *pTokenType){ |
| 680 | 701 | int n; |
| 681 | 702 | if( z[0]=='<' ){ |
| 682 | 703 | n = html_tag_length(z); |
| 683 | 704 | if( n>0 ){ |
| 705 | + p->mRender |= RENDER_TAG; | |
| 684 | 706 | *pTokenType = TOKEN_MARKUP; |
| 685 | 707 | return n; |
| 686 | 708 | }else{ |
| 709 | + p->mRender |= RENDER_BADTAG; | |
| 710 | + *pTokenType = TOKEN_CHARACTER; | |
| 711 | + return 1; | |
| 712 | + } | |
| 713 | + } | |
| 714 | + if( z[0]=='&' ){ | |
| 715 | + p->mRender |= RENDER_ENTITY; | |
| 716 | + if( (p->inVerbatim || !isElement(z)) ){ | |
| 687 | 717 | *pTokenType = TOKEN_CHARACTER; |
| 688 | 718 | return 1; |
| 689 | 719 | } |
| 690 | 720 | } |
| 691 | - if( z[0]=='&' && (p->inVerbatim || !isElement(z)) ){ | |
| 692 | - *pTokenType = TOKEN_CHARACTER; | |
| 693 | - return 1; | |
| 694 | - } | |
| 695 | 721 | if( (p->state & ALLOW_WIKI)!=0 ){ |
| 696 | 722 | if( z[0]=='\n' ){ |
| 697 | 723 | n = paragraphBreakLength(z); |
| 698 | 724 | if( n>0 ){ |
| 699 | 725 | *pTokenType = TOKEN_PARAGRAPH; |
| @@ -725,17 +751,31 @@ | ||
| 725 | 751 | if( n>0 ){ |
| 726 | 752 | *pTokenType = TOKEN_INDENT; |
| 727 | 753 | return n; |
| 728 | 754 | } |
| 729 | 755 | } |
| 730 | - if( z[0]=='[' && (n = linkLength(z))>0 ){ | |
| 756 | + if( z[0]=='[' ){ | |
| 757 | + if( (n = linkLength(z))>0 ){ | |
| 758 | + *pTokenType = TOKEN_LINK; | |
| 759 | + return n; | |
| 760 | + }else if( p->state & WIKI_MARK ){ | |
| 761 | + blob_append_string(p->pOut, "<mark>"); | |
| 762 | + p->mRender |= RENDER_BADLINK|RENDER_MARK; | |
| 763 | + }else{ | |
| 764 | + p->mRender |= RENDER_BADLINK; | |
| 765 | + } | |
| 766 | + } | |
| 767 | + }else if( (p->state & ALLOW_LINKS)!=0 && z[0]=='[' ){ | |
| 768 | + if( (n = linkLength(z))>0 ){ | |
| 731 | 769 | *pTokenType = TOKEN_LINK; |
| 732 | 770 | return n; |
| 771 | + }else if( p->state & WIKI_MARK ){ | |
| 772 | + blob_append_string(p->pOut, "<mark>"); | |
| 773 | + p->mRender |= RENDER_BADLINK|RENDER_MARK; | |
| 774 | + }else{ | |
| 775 | + p->mRender |= RENDER_BADLINK; | |
| 733 | 776 | } |
| 734 | - }else if( (p->state & ALLOW_LINKS)!=0 && z[0]=='[' && (n = linkLength(z))>0 ){ | |
| 735 | - *pTokenType = TOKEN_LINK; | |
| 736 | - return n; | |
| 737 | 777 | } |
| 738 | 778 | *pTokenType = TOKEN_TEXT; |
| 739 | 779 | return 1 + textLength(z+1, p->state); |
| 740 | 780 | } |
| 741 | 781 | |
| @@ -745,13 +785,20 @@ | ||
| 745 | 785 | ** z points to the start of a token. Return the number of |
| 746 | 786 | ** characters in that token. Write the token type into *pTokenType. |
| 747 | 787 | */ |
| 748 | 788 | static int nextRawToken(const char *z, Renderer *p, int *pTokenType){ |
| 749 | 789 | int n; |
| 750 | - if( z[0]=='[' && (n = linkLength(z))>0 ){ | |
| 751 | - *pTokenType = TOKEN_LINK; | |
| 752 | - return n; | |
| 790 | + if( z[0]=='[' ){ | |
| 791 | + if( (n = linkLength(z))>0 ){ | |
| 792 | + *pTokenType = TOKEN_LINK; | |
| 793 | + return n; | |
| 794 | + }else if( p->state & WIKI_MARK ){ | |
| 795 | + blob_append_string(p->pOut, "<mark>"); | |
| 796 | + p->mRender |= RENDER_BADLINK|RENDER_MARK; | |
| 797 | + }else{ | |
| 798 | + p->mRender |= RENDER_BADLINK; | |
| 799 | + } | |
| 753 | 800 | } |
| 754 | 801 | *pTokenType = TOKEN_RAW; |
| 755 | 802 | return 1 + textLength(z+1, p->state); |
| 756 | 803 | } |
| 757 | 804 | |
| @@ -1248,12 +1295,16 @@ | ||
| 1248 | 1295 | ** [wiki:WikiPageName] |
| 1249 | 1296 | ** |
| 1250 | 1297 | ** [2010-02-27 07:13] |
| 1251 | 1298 | ** |
| 1252 | 1299 | ** [InterMap:Link] -> Interwiki link |
| 1300 | +** | |
| 1301 | +** The return value is a mask of RENDER_* values indicating what happened. | |
| 1302 | +** Probably the return value is 0 on success and RENDER_BADTARGET or | |
| 1303 | +** RENDER_BADLINK if there are problems. | |
| 1253 | 1304 | */ |
| 1254 | -void wiki_resolve_hyperlink( | |
| 1305 | +int wiki_resolve_hyperlink( | |
| 1255 | 1306 | Blob *pOut, /* Write the HTML output here */ |
| 1256 | 1307 | int mFlags, /* Rendering option flags */ |
| 1257 | 1308 | const char *zTarget, /* Hyperlink target; text within [...] */ |
| 1258 | 1309 | char *zClose, /* Write hyperlink closing text here */ |
| 1259 | 1310 | int nClose, /* Bytes available in zClose[] */ |
| @@ -1263,10 +1314,11 @@ | ||
| 1263 | 1314 | const char *zTerm = "</a>"; |
| 1264 | 1315 | const char *z; |
| 1265 | 1316 | char *zExtra = 0; |
| 1266 | 1317 | const char *zExtraNS = 0; |
| 1267 | 1318 | char *zRemote = 0; |
| 1319 | + int rc = 0; | |
| 1268 | 1320 | |
| 1269 | 1321 | if( zTitle ){ |
| 1270 | 1322 | zExtra = mprintf(" title='%h'", zTitle); |
| 1271 | 1323 | zExtraNS = zExtra+1; |
| 1272 | 1324 | }else if( mFlags & WIKI_TARGET_BLANK ){ |
| @@ -1316,15 +1368,20 @@ | ||
| 1316 | 1368 | } |
| 1317 | 1369 | } |
| 1318 | 1370 | }else if( !in_this_repo(zTarget) ){ |
| 1319 | 1371 | if( (mFlags & (WIKI_LINKSONLY|WIKI_NOBADLINKS))!=0 ){ |
| 1320 | 1372 | zTerm = ""; |
| 1373 | + }else if( (mFlags & WIKI_MARK)!=0 ){ | |
| 1374 | + blob_appendf(pOut, "<mark>%s", zLB); | |
| 1375 | + zTerm = "]</mark>"; | |
| 1376 | + rc |= RENDER_MARK; | |
| 1321 | 1377 | }else{ |
| 1322 | 1378 | blob_appendf(pOut, "<span class=\"brokenlink\">%s", zLB); |
| 1323 | 1379 | zTerm = "]</span>"; |
| 1324 | 1380 | } |
| 1325 | - }else if( g.perm.Hyperlink ){ | |
| 1381 | + rc |= RENDER_BADTARGET; | |
| 1382 | + }else if( g.perm.Hyperlink || (mFlags & WIKI_ADMIN)!=0 ){ | |
| 1326 | 1383 | blob_appendf(pOut, "%z%s",xhref(zExtraNS, "%R/info/%s", zTarget), zLB); |
| 1327 | 1384 | zTerm = "]</a>"; |
| 1328 | 1385 | }else{ |
| 1329 | 1386 | zTerm = ""; |
| 1330 | 1387 | } |
| @@ -1340,33 +1397,54 @@ | ||
| 1340 | 1397 | }else{ |
| 1341 | 1398 | blob_appendf(pOut, "<a href=\"%R/wiki?name=%T\"%s>", z, zExtra); |
| 1342 | 1399 | } |
| 1343 | 1400 | }else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-' |
| 1344 | 1401 | && db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){ |
| 1345 | - /* Dates or date-and-times in ISO8610 resolve to a link to the | |
| 1402 | + /* Dates or date-and-times in ISO8601 resolve to a link to the | |
| 1346 | 1403 | ** timeline for that date */ |
| 1347 | 1404 | blob_appendf(pOut, "<a href=\"%R/timeline?c=%T\"%s>", zTarget, zExtra); |
| 1348 | 1405 | }else if( mFlags & WIKI_MARKDOWNLINKS ){ |
| 1349 | 1406 | /* If none of the above, and if rendering links for markdown, then |
| 1350 | 1407 | ** create a link to the literal text of the target */ |
| 1351 | 1408 | blob_appendf(pOut, "<a href=\"%h\"%s>", zTarget, zExtra); |
| 1409 | + }else if( mFlags & WIKI_MARK ){ | |
| 1410 | + blob_appendf(pOut, "<mark>["); | |
| 1411 | + zTerm = "]</mark>"; | |
| 1412 | + rc |= RENDER_BADTARGET|RENDER_MARK; | |
| 1352 | 1413 | }else if( zOrig && zTarget>=&zOrig[2] |
| 1353 | 1414 | && zTarget[-1]=='[' && !fossil_isspace(zTarget[-2]) ){ |
| 1354 | 1415 | /* If the hyperlink markup is not preceded by whitespace, then it |
| 1355 | 1416 | ** is probably a C-language subscript or similar, not really a |
| 1356 | 1417 | ** hyperlink. Just ignore it. */ |
| 1357 | 1418 | zTerm = ""; |
| 1358 | 1419 | }else if( (mFlags & (WIKI_NOBADLINKS|WIKI_LINKSONLY))!=0 ){ |
| 1359 | 1420 | /* Also ignore the link if various flags are set */ |
| 1360 | 1421 | zTerm = ""; |
| 1422 | + rc |= RENDER_BADTARGET; | |
| 1361 | 1423 | }else{ |
| 1362 | 1424 | blob_appendf(pOut, "<span class=\"brokenlink\">[%h]", zTarget); |
| 1363 | 1425 | zTerm = "</span>"; |
| 1426 | + rc |= RENDER_BADTARGET; | |
| 1364 | 1427 | } |
| 1365 | 1428 | if( zExtra ) fossil_free(zExtra); |
| 1366 | 1429 | assert( (int)strlen(zTerm)<nClose ); |
| 1367 | 1430 | sqlite3_snprintf(nClose, zClose, "%s", zTerm); |
| 1431 | + return rc; | |
| 1432 | +} | |
| 1433 | + | |
| 1434 | +/* | |
| 1435 | +** Check zTarget to see if it looks like a valid hyperlink target. | |
| 1436 | +** Return true if it does seem valid and false if not. | |
| 1437 | +*/ | |
| 1438 | +int wiki_valid_link_target(char *zTarget){ | |
| 1439 | + char zClose[30]; | |
| 1440 | + Blob notUsed; | |
| 1441 | + blob_init(¬Used, 0, 0); | |
| 1442 | + wiki_resolve_hyperlink(¬Used, WIKI_NOBADLINKS|WIKI_ADMIN, | |
| 1443 | + zTarget, zClose, sizeof(zClose)-1, 0, 0); | |
| 1444 | + blob_reset(¬Used); | |
| 1445 | + return zClose[0]!=0; | |
| 1368 | 1446 | } |
| 1369 | 1447 | |
| 1370 | 1448 | /* |
| 1371 | 1449 | ** Check to see if the given parsed markup is the correct |
| 1372 | 1450 | ** </verbatim> tag. |
| @@ -1450,11 +1528,10 @@ | ||
| 1450 | 1528 | */ |
| 1451 | 1529 | static void wiki_render(Renderer *p, char *z){ |
| 1452 | 1530 | int tokenType; |
| 1453 | 1531 | ParsedMarkup markup; |
| 1454 | 1532 | int n; |
| 1455 | - int inlineOnly = (p->state & INLINE_MARKUP_ONLY)!=0; | |
| 1456 | 1533 | int wikiHtmlOnly = (p->state & (WIKI_HTMLONLY | WIKI_LINKSONLY))!=0; |
| 1457 | 1534 | int linksOnly = (p->state & WIKI_LINKSONLY)!=0; |
| 1458 | 1535 | char *zOrig = z; |
| 1459 | 1536 | |
| 1460 | 1537 | /* Make sure the attribute constants and names still align |
| @@ -1468,22 +1545,17 @@ | ||
| 1468 | 1545 | n = nextWikiToken(z, p, &tokenType); |
| 1469 | 1546 | } |
| 1470 | 1547 | p->state &= ~(AT_NEWLINE|AT_PARAGRAPH); |
| 1471 | 1548 | switch( tokenType ){ |
| 1472 | 1549 | case TOKEN_PARAGRAPH: { |
| 1473 | - if( inlineOnly ){ | |
| 1474 | - /* blob_append_string(p->pOut, " ¶ "); */ | |
| 1475 | - blob_append_string(p->pOut, " "); | |
| 1476 | - }else{ | |
| 1477 | - if( p->wikiList ){ | |
| 1478 | - popStackToTag(p, p->wikiList); | |
| 1479 | - p->wikiList = 0; | |
| 1480 | - } | |
| 1481 | - endAutoParagraph(p); | |
| 1482 | - blob_append_string(p->pOut, "\n\n"); | |
| 1483 | - p->wantAutoParagraph = 1; | |
| 1484 | - } | |
| 1550 | + if( p->wikiList ){ | |
| 1551 | + popStackToTag(p, p->wikiList); | |
| 1552 | + p->wikiList = 0; | |
| 1553 | + } | |
| 1554 | + endAutoParagraph(p); | |
| 1555 | + blob_append_string(p->pOut, "\n\n"); | |
| 1556 | + p->wantAutoParagraph = 1; | |
| 1485 | 1557 | p->state |= AT_PARAGRAPH|AT_NEWLINE; |
| 1486 | 1558 | break; |
| 1487 | 1559 | } |
| 1488 | 1560 | case TOKEN_NEWLINE: { |
| 1489 | 1561 | if( p->renderFlags & WIKI_NEWLINE ){ |
| @@ -1493,86 +1565,91 @@ | ||
| 1493 | 1565 | } |
| 1494 | 1566 | p->state |= AT_NEWLINE; |
| 1495 | 1567 | break; |
| 1496 | 1568 | } |
| 1497 | 1569 | case TOKEN_BUL_LI: { |
| 1498 | - if( inlineOnly ){ | |
| 1499 | - blob_append_string(p->pOut, " • "); | |
| 1500 | - }else{ | |
| 1501 | - if( p->wikiList!=MARKUP_UL ){ | |
| 1502 | - if( p->wikiList ){ | |
| 1503 | - popStackToTag(p, p->wikiList); | |
| 1504 | - } | |
| 1505 | - endAutoParagraph(p); | |
| 1506 | - pushStack(p, MARKUP_UL); | |
| 1507 | - blob_append_string(p->pOut, "<ul>"); | |
| 1508 | - p->wikiList = MARKUP_UL; | |
| 1509 | - } | |
| 1510 | - popStackToTag(p, MARKUP_LI); | |
| 1511 | - startAutoParagraph(p); | |
| 1512 | - pushStack(p, MARKUP_LI); | |
| 1513 | - blob_append_string(p->pOut, "<li>"); | |
| 1514 | - } | |
| 1570 | + p->mRender |= RENDER_BLOCK; | |
| 1571 | + if( p->wikiList!=MARKUP_UL ){ | |
| 1572 | + if( p->wikiList ){ | |
| 1573 | + popStackToTag(p, p->wikiList); | |
| 1574 | + } | |
| 1575 | + endAutoParagraph(p); | |
| 1576 | + pushStack(p, MARKUP_UL); | |
| 1577 | + blob_append_string(p->pOut, "<ul>"); | |
| 1578 | + p->wikiList = MARKUP_UL; | |
| 1579 | + } | |
| 1580 | + popStackToTag(p, MARKUP_LI); | |
| 1581 | + startAutoParagraph(p); | |
| 1582 | + pushStack(p, MARKUP_LI); | |
| 1583 | + blob_append_string(p->pOut, "<li>"); | |
| 1515 | 1584 | break; |
| 1516 | 1585 | } |
| 1517 | 1586 | case TOKEN_NUM_LI: { |
| 1518 | - if( inlineOnly ){ | |
| 1519 | - blob_append_string(p->pOut, " # "); | |
| 1520 | - }else{ | |
| 1521 | - if( p->wikiList!=MARKUP_OL ){ | |
| 1522 | - if( p->wikiList ){ | |
| 1523 | - popStackToTag(p, p->wikiList); | |
| 1524 | - } | |
| 1525 | - endAutoParagraph(p); | |
| 1526 | - pushStack(p, MARKUP_OL); | |
| 1527 | - blob_append_string(p->pOut, "<ol>"); | |
| 1528 | - p->wikiList = MARKUP_OL; | |
| 1529 | - } | |
| 1530 | - popStackToTag(p, MARKUP_LI); | |
| 1531 | - startAutoParagraph(p); | |
| 1532 | - pushStack(p, MARKUP_LI); | |
| 1533 | - blob_append_string(p->pOut, "<li>"); | |
| 1534 | - } | |
| 1587 | + p->mRender |= RENDER_BLOCK; | |
| 1588 | + if( p->wikiList!=MARKUP_OL ){ | |
| 1589 | + if( p->wikiList ){ | |
| 1590 | + popStackToTag(p, p->wikiList); | |
| 1591 | + } | |
| 1592 | + endAutoParagraph(p); | |
| 1593 | + pushStack(p, MARKUP_OL); | |
| 1594 | + blob_append_string(p->pOut, "<ol>"); | |
| 1595 | + p->wikiList = MARKUP_OL; | |
| 1596 | + } | |
| 1597 | + popStackToTag(p, MARKUP_LI); | |
| 1598 | + startAutoParagraph(p); | |
| 1599 | + pushStack(p, MARKUP_LI); | |
| 1600 | + blob_append_string(p->pOut, "<li>"); | |
| 1535 | 1601 | break; |
| 1536 | 1602 | } |
| 1537 | 1603 | case TOKEN_ENUM: { |
| 1538 | - if( inlineOnly ){ | |
| 1539 | - blob_appendf(p->pOut, " (%d) ", atoi(z)); | |
| 1540 | - }else{ | |
| 1541 | - if( p->wikiList!=MARKUP_OL ){ | |
| 1542 | - if( p->wikiList ){ | |
| 1543 | - popStackToTag(p, p->wikiList); | |
| 1544 | - } | |
| 1545 | - endAutoParagraph(p); | |
| 1546 | - pushStack(p, MARKUP_OL); | |
| 1547 | - blob_append_string(p->pOut, "<ol>"); | |
| 1548 | - p->wikiList = MARKUP_OL; | |
| 1549 | - } | |
| 1550 | - popStackToTag(p, MARKUP_LI); | |
| 1551 | - startAutoParagraph(p); | |
| 1552 | - pushStack(p, MARKUP_LI); | |
| 1553 | - blob_appendf(p->pOut, "<li value=\"%d\">", atoi(z)); | |
| 1554 | - } | |
| 1604 | + p->mRender |= RENDER_BLOCK; | |
| 1605 | + if( p->wikiList!=MARKUP_OL ){ | |
| 1606 | + if( p->wikiList ){ | |
| 1607 | + popStackToTag(p, p->wikiList); | |
| 1608 | + } | |
| 1609 | + endAutoParagraph(p); | |
| 1610 | + pushStack(p, MARKUP_OL); | |
| 1611 | + blob_append_string(p->pOut, "<ol>"); | |
| 1612 | + p->wikiList = MARKUP_OL; | |
| 1613 | + } | |
| 1614 | + popStackToTag(p, MARKUP_LI); | |
| 1615 | + startAutoParagraph(p); | |
| 1616 | + pushStack(p, MARKUP_LI); | |
| 1617 | + blob_appendf(p->pOut, "<li value=\"%d\">", atoi(z)); | |
| 1555 | 1618 | break; |
| 1556 | 1619 | } |
| 1557 | 1620 | case TOKEN_INDENT: { |
| 1558 | - if( !inlineOnly ){ | |
| 1559 | - assert( p->wikiList==0 ); | |
| 1560 | - pushStack(p, MARKUP_BLOCKQUOTE); | |
| 1561 | - blob_append_string(p->pOut, "<blockquote>"); | |
| 1562 | - p->wantAutoParagraph = 0; | |
| 1563 | - p->wikiList = MARKUP_BLOCKQUOTE; | |
| 1564 | - } | |
| 1621 | + p->mRender |= RENDER_BLOCK; | |
| 1622 | + assert( p->wikiList==0 ); | |
| 1623 | + pushStack(p, MARKUP_BLOCKQUOTE); | |
| 1624 | + blob_append_string(p->pOut, "<blockquote>"); | |
| 1625 | + p->wantAutoParagraph = 0; | |
| 1626 | + p->wikiList = MARKUP_BLOCKQUOTE; | |
| 1565 | 1627 | break; |
| 1566 | 1628 | } |
| 1567 | 1629 | case TOKEN_CHARACTER: { |
| 1568 | 1630 | startAutoParagraph(p); |
| 1631 | + if( p->state & WIKI_MARK ){ | |
| 1632 | + blob_append_string(p->pOut, "<mark>"); | |
| 1633 | + p->mRender |= RENDER_MARK; | |
| 1634 | + } | |
| 1569 | 1635 | if( z[0]=='<' ){ |
| 1636 | + p->mRender |= RENDER_BADTAG; | |
| 1570 | 1637 | blob_append_string(p->pOut, "<"); |
| 1571 | 1638 | }else if( z[0]=='&' ){ |
| 1639 | + p->mRender |= RENDER_BADENTITY; | |
| 1572 | 1640 | blob_append_string(p->pOut, "&"); |
| 1573 | 1641 | } |
| 1642 | + if( p->state & WIKI_MARK ){ | |
| 1643 | + if( fossil_isalnum(z[1]) || (z[1]=='/' && fossil_isalnum(z[2])) ){ | |
| 1644 | + int kk; | |
| 1645 | + for(kk=2; fossil_isalnum(z[kk]); kk++){} | |
| 1646 | + blob_append(p->pOut, &z[1], kk-1); | |
| 1647 | + n = kk; | |
| 1648 | + } | |
| 1649 | + blob_append_string(p->pOut, "</mark>"); | |
| 1650 | + } | |
| 1574 | 1651 | break; |
| 1575 | 1652 | } |
| 1576 | 1653 | case TOKEN_LINK: { |
| 1577 | 1654 | char *zTarget; |
| 1578 | 1655 | char *zDisplay = 0; |
| @@ -1581,10 +1658,11 @@ | ||
| 1581 | 1658 | char zClose[20]; |
| 1582 | 1659 | char cS1 = 0; |
| 1583 | 1660 | int iS1 = 0; |
| 1584 | 1661 | |
| 1585 | 1662 | startAutoParagraph(p); |
| 1663 | + p->mRender |= RENDER_LINK; | |
| 1586 | 1664 | zTarget = &z[1]; |
| 1587 | 1665 | for(i=1; z[i] && z[i]!=']'; i++){ |
| 1588 | 1666 | if( z[i]=='|' && zDisplay==0 ){ |
| 1589 | 1667 | zDisplay = &z[i+1]; |
| 1590 | 1668 | for(j=i; j>0 && fossil_isspace(z[j-1]); j--){} |
| @@ -1597,11 +1675,11 @@ | ||
| 1597 | 1675 | if( zDisplay==0 ){ |
| 1598 | 1676 | zDisplay = zTarget + interwiki_removable_prefix(zTarget); |
| 1599 | 1677 | }else{ |
| 1600 | 1678 | while( fossil_isspace(*zDisplay) ) zDisplay++; |
| 1601 | 1679 | } |
| 1602 | - wiki_resolve_hyperlink(p->pOut, p->state, | |
| 1680 | + p->mRender |= wiki_resolve_hyperlink(p->pOut, p->state, | |
| 1603 | 1681 | zTarget, zClose, sizeof(zClose), zOrig, 0); |
| 1604 | 1682 | if( linksOnly || zClose[0]==0 || p->inVerbatim ){ |
| 1605 | 1683 | if( cS1 ) z[iS1] = cS1; |
| 1606 | 1684 | if( zClose[0]!=']' ){ |
| 1607 | 1685 | blob_appendf(p->pOut, "[%h]%s", zTarget, zClose); |
| @@ -1687,14 +1765,22 @@ | ||
| 1687 | 1765 | |
| 1688 | 1766 | /* Render invalid markup literally. The markup appears in the |
| 1689 | 1767 | ** final output as plain text. |
| 1690 | 1768 | */ |
| 1691 | 1769 | if( markup.iCode==MARKUP_INVALID ){ |
| 1770 | + p->mRender |= RENDER_BADTAG; | |
| 1692 | 1771 | unparseMarkup(&markup); |
| 1693 | 1772 | startAutoParagraph(p); |
| 1694 | - blob_append_string(p->pOut, "<"); | |
| 1695 | - n = 1; | |
| 1773 | + if( p->state & WIKI_MARK ){ | |
| 1774 | + p->mRender |= RENDER_MARK; | |
| 1775 | + blob_append_string(p->pOut, "<mark>"); | |
| 1776 | + htmlize_to_blob(p->pOut, z, n); | |
| 1777 | + blob_append_string(p->pOut, "</mark>"); | |
| 1778 | + }else{ | |
| 1779 | + blob_append_string(p->pOut, "<"); | |
| 1780 | + htmlize_to_blob(p->pOut, z+1, n-1); | |
| 1781 | + } | |
| 1696 | 1782 | }else |
| 1697 | 1783 | |
| 1698 | 1784 | /* If the markup is not font-change markup ignore it if the |
| 1699 | 1785 | ** font-change-only flag is set. |
| 1700 | 1786 | */ |
| @@ -1708,16 +1794,10 @@ | ||
| 1708 | 1794 | }else{ |
| 1709 | 1795 | p->state &= ~ALLOW_WIKI; |
| 1710 | 1796 | } |
| 1711 | 1797 | }else |
| 1712 | 1798 | |
| 1713 | - /* Ignore block markup for in-line rendering. | |
| 1714 | - */ | |
| 1715 | - if( inlineOnly && (markup.iType&MUTYPE_INLINE)==0 ){ | |
| 1716 | - /* Do nothing */ | |
| 1717 | - }else | |
| 1718 | - | |
| 1719 | 1799 | /* Generate end-tags */ |
| 1720 | 1800 | if( markup.endTag ){ |
| 1721 | 1801 | popStackToTag(p, markup.iCode); |
| 1722 | 1802 | }else |
| 1723 | 1803 | |
| @@ -1799,10 +1879,11 @@ | ||
| 1799 | 1879 | }else |
| 1800 | 1880 | { |
| 1801 | 1881 | if( markup.iType==MUTYPE_FONT ){ |
| 1802 | 1882 | startAutoParagraph(p); |
| 1803 | 1883 | }else if( markup.iType==MUTYPE_BLOCK || markup.iType==MUTYPE_LIST ){ |
| 1884 | + p->mRender |= RENDER_BLOCKTAG; | |
| 1804 | 1885 | p->wantAutoParagraph = 0; |
| 1805 | 1886 | } |
| 1806 | 1887 | if( markup.iCode==MARKUP_HR |
| 1807 | 1888 | || markup.iCode==MARKUP_H1 |
| 1808 | 1889 | || markup.iCode==MARKUP_H2 |
| @@ -1829,12 +1910,14 @@ | ||
| 1829 | 1910 | ** Transform the text in the pIn blob. Write the results |
| 1830 | 1911 | ** into the pOut blob. The pOut blob should already be |
| 1831 | 1912 | ** initialized. The output is merely appended to pOut. |
| 1832 | 1913 | ** If pOut is NULL, then the output is appended to the CGI |
| 1833 | 1914 | ** reply. |
| 1915 | +** | |
| 1916 | +** Return a mask of RENDER_ flags indicating what happened. | |
| 1834 | 1917 | */ |
| 1835 | -void wiki_convert(Blob *pIn, Blob *pOut, int flags){ | |
| 1918 | +int wiki_convert(Blob *pIn, Blob *pOut, int flags){ | |
| 1836 | 1919 | Renderer renderer; |
| 1837 | 1920 | |
| 1838 | 1921 | memset(&renderer, 0, sizeof(renderer)); |
| 1839 | 1922 | renderer.renderFlags = flags; |
| 1840 | 1923 | renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH|flags; |
| @@ -1858,10 +1941,11 @@ | ||
| 1858 | 1941 | while( renderer.nStack ){ |
| 1859 | 1942 | popStack(&renderer); |
| 1860 | 1943 | } |
| 1861 | 1944 | blob_append_char(renderer.pOut, '\n'); |
| 1862 | 1945 | free(renderer.aStack); |
| 1946 | + return renderer.mRender; | |
| 1863 | 1947 | } |
| 1864 | 1948 | |
| 1865 | 1949 | /* |
| 1866 | 1950 | ** COMMAND: test-wiki-render |
| 1867 | 1951 | ** |
| @@ -1870,36 +1954,85 @@ | ||
| 1870 | 1954 | ** Translate the input FILE from Fossil-wiki into HTML and write |
| 1871 | 1955 | ** the resulting HTML on standard output. |
| 1872 | 1956 | ** |
| 1873 | 1957 | ** Options: |
| 1874 | 1958 | ** --buttons Set the WIKI_BUTTONS flag |
| 1959 | +** --dark-pikchr Render pikchrs in dark mode | |
| 1960 | +** --flow Render as text using comment_format | |
| 1875 | 1961 | ** --htmlonly Set the WIKI_HTMLONLY flag |
| 1876 | -** --linksonly Set the WIKI_LINKSONLY flag | |
| 1877 | -** --nobadlinks Set the WIKI_NOBADLINKS flag | |
| 1878 | 1962 | ** --inline Set the WIKI_INLINE flag |
| 1879 | -** --noblock Set the WIKI_NOBLOCK flag | |
| 1880 | -** --dark-pikchr Render pikchrs in dark mode | |
| 1963 | +** --linksonly Set the WIKI_LINKSONLY flag | |
| 1964 | +** -m TEXT Use TEXT in place of the content of FILE | |
| 1965 | +** --mark Add <mark>...</mark> around problems | |
| 1966 | +** --nobadlinks Set the WIKI_NOBADLINKS flag | |
| 1967 | +** --text Run the output through html_to_plaintext() | |
| 1968 | +** --type Break down the return code from wiki_convert() | |
| 1881 | 1969 | */ |
| 1882 | 1970 | void test_wiki_render(void){ |
| 1883 | 1971 | Blob in, out; |
| 1884 | 1972 | int flags = 0; |
| 1973 | + int bText; | |
| 1974 | + int bFlow = 0; | |
| 1975 | + int showType = 0; | |
| 1976 | + int mType; | |
| 1977 | + const char *zIn; | |
| 1885 | 1978 | if( find_option("buttons",0,0)!=0 ) flags |= WIKI_BUTTONS; |
| 1886 | 1979 | if( find_option("htmlonly",0,0)!=0 ) flags |= WIKI_HTMLONLY; |
| 1887 | 1980 | if( find_option("linksonly",0,0)!=0 ) flags |= WIKI_LINKSONLY; |
| 1888 | 1981 | if( find_option("nobadlinks",0,0)!=0 ) flags |= WIKI_NOBADLINKS; |
| 1889 | 1982 | if( find_option("inline",0,0)!=0 ) flags |= WIKI_INLINE; |
| 1890 | - if( find_option("noblock",0,0)!=0 ) flags |= WIKI_NOBLOCK; | |
| 1983 | + if( find_option("mark",0,0)!=0 ) flags |= WIKI_MARK; | |
| 1891 | 1984 | if( find_option("dark-pikchr",0,0)!=0 ){ |
| 1892 | 1985 | pikchr_to_html_add_flags( PIKCHR_PROCESS_DARK_MODE ); |
| 1893 | 1986 | } |
| 1987 | + bText = find_option("text",0,0)!=0; | |
| 1988 | + bFlow = find_option("flow",0,0)!=0; | |
| 1989 | + showType = find_option("type",0,0)!=0; | |
| 1990 | + zIn = find_option("msg","m",1); | |
| 1894 | 1991 | db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0); |
| 1895 | 1992 | verify_all_options(); |
| 1896 | - if( g.argc!=3 ) usage("FILE"); | |
| 1993 | + if( (zIn==0 && g.argc!=3) || (zIn!=0 && g.argc!=2) ) usage("FILE"); | |
| 1897 | 1994 | blob_zero(&out); |
| 1898 | - blob_read_from_file(&in, g.argv[2], ExtFILE); | |
| 1899 | - wiki_convert(&in, &out, flags); | |
| 1900 | - blob_write_to_file(&out, "-"); | |
| 1995 | + if( zIn ){ | |
| 1996 | + blob_init(&in, zIn, -1); | |
| 1997 | + }else{ | |
| 1998 | + blob_read_from_file(&in, g.argv[2], ExtFILE); | |
| 1999 | + } | |
| 2000 | + mType = wiki_convert(&in, &out, flags); | |
| 2001 | + if( bText ){ | |
| 2002 | + Blob txt; | |
| 2003 | + int htot = HTOT_TRIM; | |
| 2004 | + if( terminal_is_vt100() ) htot |= HTOT_VT100; | |
| 2005 | + if( bFlow ) htot |= HTOT_FLOW; | |
| 2006 | + blob_init(&txt, 0, 0); | |
| 2007 | + html_to_plaintext(blob_str(&out),&txt, htot); | |
| 2008 | + blob_reset(&out); | |
| 2009 | + out = txt; | |
| 2010 | + } | |
| 2011 | + if( bFlow ){ | |
| 2012 | + fossil_print(" "); | |
| 2013 | + comment_print(blob_str(&out), 0, 3, terminal_get_width(80)-3, | |
| 2014 | + get_comment_format()); | |
| 2015 | + }else{ | |
| 2016 | + blob_write_to_file(&out, "-"); | |
| 2017 | + } | |
| 2018 | + if( showType ){ | |
| 2019 | + fossil_print("%.*c\nResult Codes:", terminal_get_width(80)-1, '*'); | |
| 2020 | + if( mType & RENDER_LINK ) fossil_print(" LINK"); | |
| 2021 | + if( mType & RENDER_ENTITY ) fossil_print(" ENTITY"); | |
| 2022 | + if( mType & RENDER_TAG ) fossil_print(" TAG"); | |
| 2023 | + if( mType & RENDER_BLOCKTAG ) fossil_print(" BLOCKTAG"); | |
| 2024 | + if( mType & RENDER_BLOCK ) fossil_print(" BLOCK"); | |
| 2025 | + if( mType & RENDER_MARK ) fossil_print(" MARK"); | |
| 2026 | + if( mType & RENDER_BADLINK ) fossil_print(" BADLINK"); | |
| 2027 | + if( mType & RENDER_BADTARGET ) fossil_print(" BADTARGET"); | |
| 2028 | + if( mType & RENDER_BADTAG ) fossil_print(" BADTAG"); | |
| 2029 | + if( mType & RENDER_BADENTITY ) fossil_print(" BADENTITY"); | |
| 2030 | + if( mType & RENDER_BADHTML ) fossil_print(" BADHTML"); | |
| 2031 | + if( mType & RENDER_ERROR ) fossil_print(" ERROR"); | |
| 2032 | + fossil_print("\n"); | |
| 2033 | + } | |
| 1901 | 2034 | } |
| 1902 | 2035 | |
| 1903 | 2036 | /* |
| 1904 | 2037 | ** COMMAND: test-markdown-render |
| 1905 | 2038 | ** |
| @@ -1906,24 +2039,26 @@ | ||
| 1906 | 2039 | ** Usage: %fossil test-markdown-render FILE ... |
| 1907 | 2040 | ** |
| 1908 | 2041 | ** Render markdown in FILE as HTML on stdout. |
| 1909 | 2042 | ** Options: |
| 1910 | 2043 | ** |
| 1911 | -** --safe Restrict the output to use only "safe" HTML | |
| 2044 | +** --dark-pikchr Render pikchrs in dark mode | |
| 1912 | 2045 | ** --lint-footnotes Print stats for footnotes-related issues |
| 1913 | -** --dark-pikchr Render pikchrs in dark mode | |
| 2046 | +** --safe Restrict the output to use only "safe" HTML | |
| 2047 | +** --text Run the output through html_to_plaintext(). | |
| 1914 | 2048 | */ |
| 1915 | 2049 | void test_markdown_render(void){ |
| 1916 | 2050 | Blob in, out; |
| 1917 | 2051 | int i; |
| 1918 | - int bSafe = 0, bFnLint = 0; | |
| 2052 | + int bSafe = 0, bFnLint = 0, bText = 0; | |
| 1919 | 2053 | db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0); |
| 1920 | 2054 | bSafe = find_option("safe",0,0)!=0; |
| 1921 | 2055 | bFnLint = find_option("lint-footnotes",0,0)!=0; |
| 1922 | 2056 | if( find_option("dark-pikchr",0,0)!=0 ){ |
| 1923 | 2057 | pikchr_to_html_add_flags( PIKCHR_PROCESS_DARK_MODE ); |
| 1924 | 2058 | } |
| 2059 | + bText = find_option("text",0,0)!=0; | |
| 1925 | 2060 | verify_all_options(); |
| 1926 | 2061 | for(i=2; i<g.argc; i++){ |
| 1927 | 2062 | blob_zero(&out); |
| 1928 | 2063 | blob_read_from_file(&in, g.argv[i], ExtFILE); |
| 1929 | 2064 | if( g.argc>3 ){ |
| @@ -1930,10 +2065,17 @@ | ||
| 1930 | 2065 | fossil_print("<!------ %h ------->\n", g.argv[i]); |
| 1931 | 2066 | } |
| 1932 | 2067 | markdown_to_html(&in, 0, &out); |
| 1933 | 2068 | safe_html_context( bSafe ? DOCSRC_UNTRUSTED : DOCSRC_TRUSTED ); |
| 1934 | 2069 | safe_html(&out); |
| 2070 | + if( bText ){ | |
| 2071 | + Blob txt; | |
| 2072 | + blob_init(&txt, 0, 0); | |
| 2073 | + html_to_plaintext(blob_str(&out), &txt, HTOT_VT100); | |
| 2074 | + blob_reset(&out); | |
| 2075 | + out = txt; | |
| 2076 | + } | |
| 1935 | 2077 | blob_write_to_file(&out, "-"); |
| 1936 | 2078 | blob_reset(&in); |
| 1937 | 2079 | blob_reset(&out); |
| 1938 | 2080 | } |
| 1939 | 2081 | if( bFnLint && (g.ftntsIssues[0] || g.ftntsIssues[1] |
| @@ -1990,33 +2132,30 @@ | ||
| 1990 | 2132 | ** [target|...] |
| 1991 | 2133 | ** |
| 1992 | 2134 | ** Where "target" can be either an artifact ID prefix or a wiki page |
| 1993 | 2135 | ** name. For each such hyperlink found, add an entry to the |
| 1994 | 2136 | ** backlink table. |
| 2137 | +** | |
| 2138 | +** The return value is a mask of RENDER_ flags. | |
| 1995 | 2139 | */ |
| 1996 | -void wiki_extract_links( | |
| 2140 | +int wiki_extract_links( | |
| 1997 | 2141 | char *z, /* The wiki text from which to extract links */ |
| 1998 | 2142 | Backlink *pBklnk, /* Backlink extraction context */ |
| 1999 | 2143 | int flags /* wiki parsing flags */ |
| 2000 | 2144 | ){ |
| 2001 | 2145 | Renderer renderer; |
| 2002 | 2146 | int tokenType; |
| 2003 | 2147 | ParsedMarkup markup; |
| 2004 | 2148 | int n; |
| 2005 | - int inlineOnly; | |
| 2006 | 2149 | int wikiHtmlOnly = 0; |
| 2007 | 2150 | |
| 2008 | 2151 | memset(&renderer, 0, sizeof(renderer)); |
| 2009 | 2152 | renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH; |
| 2010 | - if( flags & WIKI_NOBLOCK ){ | |
| 2011 | - renderer.state |= INLINE_MARKUP_ONLY; | |
| 2012 | - } | |
| 2013 | 2153 | if( wikiUsesHtml() ){ |
| 2014 | 2154 | renderer.state |= WIKI_HTMLONLY; |
| 2015 | 2155 | wikiHtmlOnly = 1; |
| 2016 | 2156 | } |
| 2017 | - inlineOnly = (renderer.state & INLINE_MARKUP_ONLY)!=0; | |
| 2018 | 2157 | |
| 2019 | 2158 | while( z[0] ){ |
| 2020 | 2159 | if( wikiHtmlOnly ){ |
| 2021 | 2160 | n = nextRawToken(z, &renderer, &tokenType); |
| 2022 | 2161 | }else{ |
| @@ -2093,16 +2232,10 @@ | ||
| 2093 | 2232 | }else{ |
| 2094 | 2233 | renderer.state &= ~ALLOW_WIKI; |
| 2095 | 2234 | } |
| 2096 | 2235 | }else |
| 2097 | 2236 | |
| 2098 | - /* Ignore block markup for in-line rendering. | |
| 2099 | - */ | |
| 2100 | - if( inlineOnly && (markup.iType&MUTYPE_INLINE)==0 ){ | |
| 2101 | - /* Do nothing */ | |
| 2102 | - }else | |
| 2103 | - | |
| 2104 | 2237 | /* Generate end-tags */ |
| 2105 | 2238 | if( markup.endTag ){ |
| 2106 | 2239 | popStackToTag(&renderer, markup.iCode); |
| 2107 | 2240 | }else |
| 2108 | 2241 | |
| @@ -2141,10 +2274,11 @@ | ||
| 2141 | 2274 | } |
| 2142 | 2275 | } |
| 2143 | 2276 | z += n; |
| 2144 | 2277 | } |
| 2145 | 2278 | free(renderer.aStack); |
| 2279 | + return renderer.mRender; | |
| 2146 | 2280 | } |
| 2147 | 2281 | |
| 2148 | 2282 | /* |
| 2149 | 2283 | ** Return the length, in bytes, of the HTML token that z is pointing to. |
| 2150 | 2284 | */ |
| @@ -2393,33 +2527,61 @@ | ||
| 2393 | 2527 | blob_reset(&in); |
| 2394 | 2528 | fossil_puts(blob_buffer(&out), 0, blob_size(&out)); |
| 2395 | 2529 | blob_reset(&out); |
| 2396 | 2530 | } |
| 2397 | 2531 | } |
| 2532 | + | |
| 2533 | +#if INTERFACE | |
| 2534 | +/* | |
| 2535 | +** Allowed flag options for html_to_plaintext(). | |
| 2536 | +*/ | |
| 2537 | +#define HTOT_VT100 0x01 /* <mark> becomes ^[[91m */ | |
| 2538 | +#define HTOT_FLOW 0x02 /* Collapse internal whitespace to a single space */ | |
| 2539 | +#define HTOT_TRIM 0x04 /* Trim off leading and trailing whitespace */ | |
| 2540 | + | |
| 2541 | +#endif /* INTERFACE */ | |
| 2542 | + | |
| 2543 | +/* | |
| 2544 | +** Add <MARK> or </MARK> to the output, or similar VT-100 escape | |
| 2545 | +** codes. | |
| 2546 | +*/ | |
| 2547 | +static void addMark(Blob *pOut, int mFlags, int isClose){ | |
| 2548 | + static const char *az[4] = { "<MARK>", "</MARK>", "\033[91m", "\033[0m" }; | |
| 2549 | + int i = 0; | |
| 2550 | + if( isClose ) i++; | |
| 2551 | + if( mFlags & HTOT_VT100 ) i += 2; | |
| 2552 | + blob_append(pOut, az[i], -1); | |
| 2553 | +} | |
| 2398 | 2554 | |
| 2399 | 2555 | /* |
| 2400 | 2556 | ** Remove all HTML markup from the input text. The output written into |
| 2401 | 2557 | ** pOut is pure text. |
| 2402 | 2558 | ** |
| 2403 | 2559 | ** Put the title on the first line, if there is any <title> markup. |
| 2404 | 2560 | ** If there is no <title>, then create a blank first line. |
| 2405 | 2561 | */ |
| 2406 | -void html_to_plaintext(const char *zIn, Blob *pOut){ | |
| 2562 | +void html_to_plaintext(const char *zIn, Blob *pOut, int mFlags){ | |
| 2407 | 2563 | int n; |
| 2408 | 2564 | int i, j; |
| 2409 | - int inTitle = 0; /* True between <title>...</title> */ | |
| 2410 | - int seenText = 0; /* True after first non-whitespace seen */ | |
| 2411 | - int nNL = 0; /* Number of \n characters at the end of pOut */ | |
| 2412 | - int nWS = 0; /* True if pOut ends with whitespace */ | |
| 2413 | - while( fossil_isspace(zIn[0]) ) zIn++; | |
| 2565 | + int bFlow = 0; /* Transform internal WS into a single space */ | |
| 2566 | + int prevWS = 1; /* Previous output was whitespace or start of msg */ | |
| 2567 | + int nMark = 0; /* True if inside of <mark>..</mark> */ | |
| 2568 | + | |
| 2569 | + for(i=0; fossil_isspace(zIn[i]); i++){} | |
| 2570 | + if( i>0 && (mFlags & HTOT_TRIM)==0 ){ | |
| 2571 | + blob_append(pOut, zIn, i); | |
| 2572 | + } | |
| 2573 | + zIn += i; | |
| 2574 | + if( mFlags & HTOT_FLOW ) bFlow = 1; | |
| 2414 | 2575 | while( zIn[0] ){ |
| 2415 | 2576 | n = html_token_length(zIn); |
| 2416 | 2577 | if( zIn[0]=='<' && n>1 ){ |
| 2417 | 2578 | int isCloseTag; |
| 2418 | 2579 | int eTag; |
| 2419 | 2580 | int eType; |
| 2420 | 2581 | char zTag[32]; |
| 2582 | + prevWS = 0; | |
| 2421 | 2583 | isCloseTag = zIn[1]=='/'; |
| 2422 | 2584 | for(i=0, j=1+isCloseTag; i<30 && fossil_isalnum(zIn[j]); i++, j++){ |
| 2423 | 2585 | zTag[i] = fossil_tolower(zIn[j]); |
| 2424 | 2586 | } |
| 2425 | 2587 | zTag[i] = 0; |
| @@ -2433,92 +2595,116 @@ | ||
| 2433 | 2595 | zIn += n; |
| 2434 | 2596 | } |
| 2435 | 2597 | if( zIn[0]=='<' ) zIn += n; |
| 2436 | 2598 | continue; |
| 2437 | 2599 | } |
| 2438 | - if( eTag==MARKUP_TITLE ){ | |
| 2439 | - inTitle = !isCloseTag; | |
| 2440 | - } | |
| 2441 | - if( !isCloseTag && seenText && (eType & (MUTYPE_BLOCK|MUTYPE_TABLE))!=0 ){ | |
| 2442 | - if( nNL==0 ){ | |
| 2443 | - blob_append_char(pOut, '\n'); | |
| 2444 | - nNL++; | |
| 2445 | - } | |
| 2446 | - nWS = 1; | |
| 2447 | - } | |
| 2448 | - }else if( fossil_isspace(zIn[0]) ){ | |
| 2449 | - if( seenText ){ | |
| 2450 | - nNL = 0; | |
| 2451 | - if( !inTitle ){ /* '\n' -> ' ' within <title> */ | |
| 2452 | - for(i=0; i<n; i++) if( zIn[i]=='\n' ) nNL++; | |
| 2453 | - } | |
| 2454 | - if( !nWS ){ | |
| 2455 | - blob_append_char(pOut, nNL ? '\n' : ' '); | |
| 2456 | - nWS = 1; | |
| 2457 | - } | |
| 2458 | - } | |
| 2459 | - }else if( zIn[0]=='&' ){ | |
| 2460 | - char c = '?'; | |
| 2461 | - if( zIn[1]=='#' ){ | |
| 2462 | - int x = atoi(&zIn[1]); | |
| 2463 | - if( x>0 && x<=127 ) c = x; | |
| 2464 | - }else{ | |
| 2465 | - static const struct { int n; char c; char *z; } aEntity[] = { | |
| 2600 | + if( eTag==MARKUP_INVALID && strcmp(zTag,"mark")==0 ){ | |
| 2601 | + if( isCloseTag && nMark ){ | |
| 2602 | + addMark(pOut, mFlags, 1); | |
| 2603 | + nMark = 0; | |
| 2604 | + }else if( !isCloseTag && !nMark ){ | |
| 2605 | + addMark(pOut, mFlags, 0); | |
| 2606 | + nMark = 1; | |
| 2607 | + } | |
| 2608 | + zIn += n; | |
| 2609 | + continue; | |
| 2610 | + } | |
| 2611 | + if( eTag==MARKUP_TITLE ){ | |
| 2612 | + if( isCloseTag && (mFlags & HTOT_FLOW)==0 ){ | |
| 2613 | + bFlow = 0; | |
| 2614 | + }else{ | |
| 2615 | + bFlow = 1; | |
| 2616 | + } | |
| 2617 | + } | |
| 2618 | + if( !isCloseTag && (eType & (MUTYPE_BLOCK|MUTYPE_TABLE))!=0 ){ | |
| 2619 | + blob_append_char(pOut, '\n'); | |
| 2620 | + } | |
| 2621 | + }else if( fossil_isspace(zIn[0]) ){ | |
| 2622 | + if( bFlow==0 ){ | |
| 2623 | + if( zIn[n]==0 && (mFlags & HTOT_TRIM) ) break; | |
| 2624 | + blob_append(pOut, zIn, n); | |
| 2625 | + }else if( !prevWS ){ | |
| 2626 | + prevWS = 1; | |
| 2627 | + blob_append_char(pOut, ' '); | |
| 2628 | + zIn += n; | |
| 2629 | + n = 0; | |
| 2630 | + } | |
| 2631 | + }else if( zIn[0]=='&' ){ | |
| 2632 | + u32 c = '?'; | |
| 2633 | + prevWS = 0; | |
| 2634 | + if( zIn[1]=='#' ){ | |
| 2635 | + c = atoi(&zIn[2]); | |
| 2636 | + if( c==0 ) c = '?'; | |
| 2637 | + }else{ | |
| 2638 | + static const struct { int n; u32 c; char *z; } aEntity[] = { | |
| 2466 | 2639 | { 5, '&', "&" }, |
| 2467 | 2640 | { 4, '<', "<" }, |
| 2468 | 2641 | { 4, '>', ">" }, |
| 2469 | 2642 | { 6, ' ', " " }, |
| 2643 | + { 6, '"', """ }, | |
| 2470 | 2644 | }; |
| 2471 | 2645 | int jj; |
| 2472 | 2646 | for(jj=0; jj<count(aEntity); jj++){ |
| 2473 | 2647 | if( aEntity[jj].n==n && strncmp(aEntity[jj].z,zIn,n)==0 ){ |
| 2474 | 2648 | c = aEntity[jj].c; |
| 2475 | 2649 | break; |
| 2476 | 2650 | } |
| 2477 | 2651 | } |
| 2478 | 2652 | } |
| 2479 | - if( fossil_isspace(c) ){ | |
| 2480 | - if( nWS==0 && seenText ) blob_append_char(pOut, c); | |
| 2481 | - nWS = 1; | |
| 2482 | - nNL = c=='\n'; | |
| 2653 | + if( c<0x00080 ){ | |
| 2654 | + blob_append_char(pOut, c & 0xff); | |
| 2655 | + }else if( c<0x00800 ){ | |
| 2656 | + blob_append_char(pOut, 0xc0 + (u8)((c>>6)&0x1f)); | |
| 2657 | + blob_append_char(pOut, 0x80 + (u8)(c&0x3f)); | |
| 2658 | + }else if( c<0x10000 ){ | |
| 2659 | + blob_append_char(pOut, 0xe0 + (u8)((c>>12)&0x0f)); | |
| 2660 | + blob_append_char(pOut, 0x80 + (u8)((c>>6)&0x3f)); | |
| 2661 | + blob_append_char(pOut, 0x80 + (u8)(c&0x3f)); | |
| 2483 | 2662 | }else{ |
| 2484 | - if( !seenText && !inTitle ) blob_append_char(pOut, '\n'); | |
| 2485 | - seenText = 1; | |
| 2486 | - nNL = nWS = 0; | |
| 2487 | - blob_append_char(pOut, c); | |
| 2663 | + blob_append_char(pOut, 0xf0 + (u8)((c>>18)&0x07)); | |
| 2664 | + blob_append_char(pOut, 0x80 + (u8)((c>>12)&0x3f)); | |
| 2665 | + blob_append_char(pOut, 0x80 + (u8)((c>>6)&0x3f)); | |
| 2666 | + blob_append_char(pOut, 0x80 + (u8)(c&0x3f)); | |
| 2488 | 2667 | } |
| 2489 | 2668 | }else{ |
| 2490 | - if( !seenText && !inTitle ) blob_append_char(pOut, '\n'); | |
| 2491 | - seenText = 1; | |
| 2492 | - nNL = nWS = 0; | |
| 2669 | + prevWS = 0; | |
| 2493 | 2670 | blob_append(pOut, zIn, n); |
| 2494 | 2671 | } |
| 2495 | 2672 | zIn += n; |
| 2496 | 2673 | } |
| 2497 | - if( nNL==0 ) blob_append_char(pOut, '\n'); | |
| 2674 | + if( nMark ){ | |
| 2675 | + addMark(pOut, mFlags, 1); | |
| 2676 | + } | |
| 2498 | 2677 | } |
| 2499 | 2678 | |
| 2500 | 2679 | /* |
| 2501 | 2680 | ** COMMAND: test-html-to-text |
| 2502 | 2681 | ** |
| 2503 | -** Usage: %fossil test-html-to-text FILE ... | |
| 2682 | +** Usage: %fossil test-html-to-text [OPTIONS] FILE ... | |
| 2504 | 2683 | ** |
| 2505 | 2684 | ** Read all files named on the command-line. Convert the file |
| 2506 | 2685 | ** content from HTML to text and write the results on standard |
| 2507 | 2686 | ** output. |
| 2508 | 2687 | ** |
| 2509 | 2688 | ** This command is intended as a test and debug interface for |
| 2510 | 2689 | ** the html_to_plaintext() routine. |
| 2690 | +** | |
| 2691 | +** Options: | |
| 2692 | +** | |
| 2693 | +** --vt100 Translate <mark> and </mark> into ANSI/VT100 | |
| 2694 | +** escapes to highlight the contained text. | |
| 2511 | 2695 | */ |
| 2512 | 2696 | void test_html_to_text(void){ |
| 2513 | 2697 | Blob in, out; |
| 2514 | 2698 | int i; |
| 2699 | + int mFlags = 0; | |
| 2700 | + if( find_option("vt100",0,0)!=0 ) mFlags |= HTOT_VT100; | |
| 2515 | 2701 | |
| 2516 | 2702 | for(i=2; i<g.argc; i++){ |
| 2517 | 2703 | blob_read_from_file(&in, g.argv[i], ExtFILE); |
| 2518 | 2704 | blob_zero(&out); |
| 2519 | - html_to_plaintext(blob_str(&in), &out); | |
| 2705 | + html_to_plaintext(blob_str(&in), &out, mFlags); | |
| 2520 | 2706 | blob_reset(&in); |
| 2521 | 2707 | fossil_puts(blob_buffer(&out), 0, blob_size(&out)); |
| 2522 | 2708 | blob_reset(&out); |
| 2523 | 2709 | } |
| 2524 | 2710 | } |
| 2525 | 2711 |
| --- src/wikiformat.c | |
| +++ src/wikiformat.c | |
| @@ -23,22 +23,43 @@ | |
| 23 | |
| 24 | #if INTERFACE |
| 25 | /* |
| 26 | ** Allowed wiki transformation operations |
| 27 | */ |
| 28 | #define WIKI_HTMLONLY 0x001 /* HTML markup only. No wiki */ |
| 29 | #define WIKI_INLINE 0x002 /* Do not surround with <p>..</p> */ |
| 30 | #define WIKI_NOBLOCK 0x004 /* No block markup of any kind */ |
| 31 | #define WIKI_BUTTONS 0x008 /* Allow sub-menu buttons */ |
| 32 | #define WIKI_NOBADLINKS 0x010 /* Ignore broken hyperlinks */ |
| 33 | #define WIKI_LINKSONLY 0x020 /* No markup. Only decorate links */ |
| 34 | #define WIKI_NEWLINE 0x040 /* Honor \n - break lines at each \n */ |
| 35 | #define WIKI_MARKDOWNLINKS 0x080 /* Resolve hyperlinks as in markdown */ |
| 36 | #define WIKI_SAFE 0x100 /* Make the result safe for embedding */ |
| 37 | #define WIKI_TARGET_BLANK 0x200 /* Hyperlinks go to a new window */ |
| 38 | #define WIKI_NOBRACKET 0x400 /* Omit extra [..] around hyperlinks */ |
| 39 | #endif |
| 40 | |
| 41 | |
| 42 | /* |
| 43 | ** These are the only markup attributes allowed. |
| 44 | */ |
| @@ -444,20 +465,20 @@ | |
| 444 | #define AT_NEWLINE 0x0010000 /* At start of a line */ |
| 445 | #define AT_PARAGRAPH 0x0020000 /* At start of a paragraph */ |
| 446 | #define ALLOW_WIKI 0x0040000 /* Allow wiki markup */ |
| 447 | #define ALLOW_LINKS 0x0080000 /* Allow [...] hyperlinks */ |
| 448 | #define FONT_MARKUP_ONLY 0x0100000 /* Only allow MUTYPE_FONT markup */ |
| 449 | #define INLINE_MARKUP_ONLY 0x0200000 /* Allow only "inline" markup */ |
| 450 | #define IN_LIST 0x0400000 /* Within wiki <ul> or <ol> */ |
| 451 | |
| 452 | /* |
| 453 | ** Current state of the rendering engine |
| 454 | */ |
| 455 | typedef struct Renderer Renderer; |
| 456 | struct Renderer { |
| 457 | Blob *pOut; /* Output appended to this blob */ |
| 458 | int state; /* Flag that govern rendering */ |
| 459 | unsigned renderFlags; /* Flags from the client */ |
| 460 | int wikiList; /* Current wiki list type */ |
| 461 | int inVerbatim; /* True in <verbatim> mode */ |
| 462 | int preVerbState; /* Value of state prior to verbatim */ |
| 463 | int wantAutoParagraph; /* True if a <p> is desired */ |
| @@ -679,21 +700,26 @@ | |
| 679 | static int nextWikiToken(const char *z, Renderer *p, int *pTokenType){ |
| 680 | int n; |
| 681 | if( z[0]=='<' ){ |
| 682 | n = html_tag_length(z); |
| 683 | if( n>0 ){ |
| 684 | *pTokenType = TOKEN_MARKUP; |
| 685 | return n; |
| 686 | }else{ |
| 687 | *pTokenType = TOKEN_CHARACTER; |
| 688 | return 1; |
| 689 | } |
| 690 | } |
| 691 | if( z[0]=='&' && (p->inVerbatim || !isElement(z)) ){ |
| 692 | *pTokenType = TOKEN_CHARACTER; |
| 693 | return 1; |
| 694 | } |
| 695 | if( (p->state & ALLOW_WIKI)!=0 ){ |
| 696 | if( z[0]=='\n' ){ |
| 697 | n = paragraphBreakLength(z); |
| 698 | if( n>0 ){ |
| 699 | *pTokenType = TOKEN_PARAGRAPH; |
| @@ -725,17 +751,31 @@ | |
| 725 | if( n>0 ){ |
| 726 | *pTokenType = TOKEN_INDENT; |
| 727 | return n; |
| 728 | } |
| 729 | } |
| 730 | if( z[0]=='[' && (n = linkLength(z))>0 ){ |
| 731 | *pTokenType = TOKEN_LINK; |
| 732 | return n; |
| 733 | } |
| 734 | }else if( (p->state & ALLOW_LINKS)!=0 && z[0]=='[' && (n = linkLength(z))>0 ){ |
| 735 | *pTokenType = TOKEN_LINK; |
| 736 | return n; |
| 737 | } |
| 738 | *pTokenType = TOKEN_TEXT; |
| 739 | return 1 + textLength(z+1, p->state); |
| 740 | } |
| 741 | |
| @@ -745,13 +785,20 @@ | |
| 745 | ** z points to the start of a token. Return the number of |
| 746 | ** characters in that token. Write the token type into *pTokenType. |
| 747 | */ |
| 748 | static int nextRawToken(const char *z, Renderer *p, int *pTokenType){ |
| 749 | int n; |
| 750 | if( z[0]=='[' && (n = linkLength(z))>0 ){ |
| 751 | *pTokenType = TOKEN_LINK; |
| 752 | return n; |
| 753 | } |
| 754 | *pTokenType = TOKEN_RAW; |
| 755 | return 1 + textLength(z+1, p->state); |
| 756 | } |
| 757 | |
| @@ -1248,12 +1295,16 @@ | |
| 1248 | ** [wiki:WikiPageName] |
| 1249 | ** |
| 1250 | ** [2010-02-27 07:13] |
| 1251 | ** |
| 1252 | ** [InterMap:Link] -> Interwiki link |
| 1253 | */ |
| 1254 | void wiki_resolve_hyperlink( |
| 1255 | Blob *pOut, /* Write the HTML output here */ |
| 1256 | int mFlags, /* Rendering option flags */ |
| 1257 | const char *zTarget, /* Hyperlink target; text within [...] */ |
| 1258 | char *zClose, /* Write hyperlink closing text here */ |
| 1259 | int nClose, /* Bytes available in zClose[] */ |
| @@ -1263,10 +1314,11 @@ | |
| 1263 | const char *zTerm = "</a>"; |
| 1264 | const char *z; |
| 1265 | char *zExtra = 0; |
| 1266 | const char *zExtraNS = 0; |
| 1267 | char *zRemote = 0; |
| 1268 | |
| 1269 | if( zTitle ){ |
| 1270 | zExtra = mprintf(" title='%h'", zTitle); |
| 1271 | zExtraNS = zExtra+1; |
| 1272 | }else if( mFlags & WIKI_TARGET_BLANK ){ |
| @@ -1316,15 +1368,20 @@ | |
| 1316 | } |
| 1317 | } |
| 1318 | }else if( !in_this_repo(zTarget) ){ |
| 1319 | if( (mFlags & (WIKI_LINKSONLY|WIKI_NOBADLINKS))!=0 ){ |
| 1320 | zTerm = ""; |
| 1321 | }else{ |
| 1322 | blob_appendf(pOut, "<span class=\"brokenlink\">%s", zLB); |
| 1323 | zTerm = "]</span>"; |
| 1324 | } |
| 1325 | }else if( g.perm.Hyperlink ){ |
| 1326 | blob_appendf(pOut, "%z%s",xhref(zExtraNS, "%R/info/%s", zTarget), zLB); |
| 1327 | zTerm = "]</a>"; |
| 1328 | }else{ |
| 1329 | zTerm = ""; |
| 1330 | } |
| @@ -1340,33 +1397,54 @@ | |
| 1340 | }else{ |
| 1341 | blob_appendf(pOut, "<a href=\"%R/wiki?name=%T\"%s>", z, zExtra); |
| 1342 | } |
| 1343 | }else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-' |
| 1344 | && db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){ |
| 1345 | /* Dates or date-and-times in ISO8610 resolve to a link to the |
| 1346 | ** timeline for that date */ |
| 1347 | blob_appendf(pOut, "<a href=\"%R/timeline?c=%T\"%s>", zTarget, zExtra); |
| 1348 | }else if( mFlags & WIKI_MARKDOWNLINKS ){ |
| 1349 | /* If none of the above, and if rendering links for markdown, then |
| 1350 | ** create a link to the literal text of the target */ |
| 1351 | blob_appendf(pOut, "<a href=\"%h\"%s>", zTarget, zExtra); |
| 1352 | }else if( zOrig && zTarget>=&zOrig[2] |
| 1353 | && zTarget[-1]=='[' && !fossil_isspace(zTarget[-2]) ){ |
| 1354 | /* If the hyperlink markup is not preceded by whitespace, then it |
| 1355 | ** is probably a C-language subscript or similar, not really a |
| 1356 | ** hyperlink. Just ignore it. */ |
| 1357 | zTerm = ""; |
| 1358 | }else if( (mFlags & (WIKI_NOBADLINKS|WIKI_LINKSONLY))!=0 ){ |
| 1359 | /* Also ignore the link if various flags are set */ |
| 1360 | zTerm = ""; |
| 1361 | }else{ |
| 1362 | blob_appendf(pOut, "<span class=\"brokenlink\">[%h]", zTarget); |
| 1363 | zTerm = "</span>"; |
| 1364 | } |
| 1365 | if( zExtra ) fossil_free(zExtra); |
| 1366 | assert( (int)strlen(zTerm)<nClose ); |
| 1367 | sqlite3_snprintf(nClose, zClose, "%s", zTerm); |
| 1368 | } |
| 1369 | |
| 1370 | /* |
| 1371 | ** Check to see if the given parsed markup is the correct |
| 1372 | ** </verbatim> tag. |
| @@ -1450,11 +1528,10 @@ | |
| 1450 | */ |
| 1451 | static void wiki_render(Renderer *p, char *z){ |
| 1452 | int tokenType; |
| 1453 | ParsedMarkup markup; |
| 1454 | int n; |
| 1455 | int inlineOnly = (p->state & INLINE_MARKUP_ONLY)!=0; |
| 1456 | int wikiHtmlOnly = (p->state & (WIKI_HTMLONLY | WIKI_LINKSONLY))!=0; |
| 1457 | int linksOnly = (p->state & WIKI_LINKSONLY)!=0; |
| 1458 | char *zOrig = z; |
| 1459 | |
| 1460 | /* Make sure the attribute constants and names still align |
| @@ -1468,22 +1545,17 @@ | |
| 1468 | n = nextWikiToken(z, p, &tokenType); |
| 1469 | } |
| 1470 | p->state &= ~(AT_NEWLINE|AT_PARAGRAPH); |
| 1471 | switch( tokenType ){ |
| 1472 | case TOKEN_PARAGRAPH: { |
| 1473 | if( inlineOnly ){ |
| 1474 | /* blob_append_string(p->pOut, " ¶ "); */ |
| 1475 | blob_append_string(p->pOut, " "); |
| 1476 | }else{ |
| 1477 | if( p->wikiList ){ |
| 1478 | popStackToTag(p, p->wikiList); |
| 1479 | p->wikiList = 0; |
| 1480 | } |
| 1481 | endAutoParagraph(p); |
| 1482 | blob_append_string(p->pOut, "\n\n"); |
| 1483 | p->wantAutoParagraph = 1; |
| 1484 | } |
| 1485 | p->state |= AT_PARAGRAPH|AT_NEWLINE; |
| 1486 | break; |
| 1487 | } |
| 1488 | case TOKEN_NEWLINE: { |
| 1489 | if( p->renderFlags & WIKI_NEWLINE ){ |
| @@ -1493,86 +1565,91 @@ | |
| 1493 | } |
| 1494 | p->state |= AT_NEWLINE; |
| 1495 | break; |
| 1496 | } |
| 1497 | case TOKEN_BUL_LI: { |
| 1498 | if( inlineOnly ){ |
| 1499 | blob_append_string(p->pOut, " • "); |
| 1500 | }else{ |
| 1501 | if( p->wikiList!=MARKUP_UL ){ |
| 1502 | if( p->wikiList ){ |
| 1503 | popStackToTag(p, p->wikiList); |
| 1504 | } |
| 1505 | endAutoParagraph(p); |
| 1506 | pushStack(p, MARKUP_UL); |
| 1507 | blob_append_string(p->pOut, "<ul>"); |
| 1508 | p->wikiList = MARKUP_UL; |
| 1509 | } |
| 1510 | popStackToTag(p, MARKUP_LI); |
| 1511 | startAutoParagraph(p); |
| 1512 | pushStack(p, MARKUP_LI); |
| 1513 | blob_append_string(p->pOut, "<li>"); |
| 1514 | } |
| 1515 | break; |
| 1516 | } |
| 1517 | case TOKEN_NUM_LI: { |
| 1518 | if( inlineOnly ){ |
| 1519 | blob_append_string(p->pOut, " # "); |
| 1520 | }else{ |
| 1521 | if( p->wikiList!=MARKUP_OL ){ |
| 1522 | if( p->wikiList ){ |
| 1523 | popStackToTag(p, p->wikiList); |
| 1524 | } |
| 1525 | endAutoParagraph(p); |
| 1526 | pushStack(p, MARKUP_OL); |
| 1527 | blob_append_string(p->pOut, "<ol>"); |
| 1528 | p->wikiList = MARKUP_OL; |
| 1529 | } |
| 1530 | popStackToTag(p, MARKUP_LI); |
| 1531 | startAutoParagraph(p); |
| 1532 | pushStack(p, MARKUP_LI); |
| 1533 | blob_append_string(p->pOut, "<li>"); |
| 1534 | } |
| 1535 | break; |
| 1536 | } |
| 1537 | case TOKEN_ENUM: { |
| 1538 | if( inlineOnly ){ |
| 1539 | blob_appendf(p->pOut, " (%d) ", atoi(z)); |
| 1540 | }else{ |
| 1541 | if( p->wikiList!=MARKUP_OL ){ |
| 1542 | if( p->wikiList ){ |
| 1543 | popStackToTag(p, p->wikiList); |
| 1544 | } |
| 1545 | endAutoParagraph(p); |
| 1546 | pushStack(p, MARKUP_OL); |
| 1547 | blob_append_string(p->pOut, "<ol>"); |
| 1548 | p->wikiList = MARKUP_OL; |
| 1549 | } |
| 1550 | popStackToTag(p, MARKUP_LI); |
| 1551 | startAutoParagraph(p); |
| 1552 | pushStack(p, MARKUP_LI); |
| 1553 | blob_appendf(p->pOut, "<li value=\"%d\">", atoi(z)); |
| 1554 | } |
| 1555 | break; |
| 1556 | } |
| 1557 | case TOKEN_INDENT: { |
| 1558 | if( !inlineOnly ){ |
| 1559 | assert( p->wikiList==0 ); |
| 1560 | pushStack(p, MARKUP_BLOCKQUOTE); |
| 1561 | blob_append_string(p->pOut, "<blockquote>"); |
| 1562 | p->wantAutoParagraph = 0; |
| 1563 | p->wikiList = MARKUP_BLOCKQUOTE; |
| 1564 | } |
| 1565 | break; |
| 1566 | } |
| 1567 | case TOKEN_CHARACTER: { |
| 1568 | startAutoParagraph(p); |
| 1569 | if( z[0]=='<' ){ |
| 1570 | blob_append_string(p->pOut, "<"); |
| 1571 | }else if( z[0]=='&' ){ |
| 1572 | blob_append_string(p->pOut, "&"); |
| 1573 | } |
| 1574 | break; |
| 1575 | } |
| 1576 | case TOKEN_LINK: { |
| 1577 | char *zTarget; |
| 1578 | char *zDisplay = 0; |
| @@ -1581,10 +1658,11 @@ | |
| 1581 | char zClose[20]; |
| 1582 | char cS1 = 0; |
| 1583 | int iS1 = 0; |
| 1584 | |
| 1585 | startAutoParagraph(p); |
| 1586 | zTarget = &z[1]; |
| 1587 | for(i=1; z[i] && z[i]!=']'; i++){ |
| 1588 | if( z[i]=='|' && zDisplay==0 ){ |
| 1589 | zDisplay = &z[i+1]; |
| 1590 | for(j=i; j>0 && fossil_isspace(z[j-1]); j--){} |
| @@ -1597,11 +1675,11 @@ | |
| 1597 | if( zDisplay==0 ){ |
| 1598 | zDisplay = zTarget + interwiki_removable_prefix(zTarget); |
| 1599 | }else{ |
| 1600 | while( fossil_isspace(*zDisplay) ) zDisplay++; |
| 1601 | } |
| 1602 | wiki_resolve_hyperlink(p->pOut, p->state, |
| 1603 | zTarget, zClose, sizeof(zClose), zOrig, 0); |
| 1604 | if( linksOnly || zClose[0]==0 || p->inVerbatim ){ |
| 1605 | if( cS1 ) z[iS1] = cS1; |
| 1606 | if( zClose[0]!=']' ){ |
| 1607 | blob_appendf(p->pOut, "[%h]%s", zTarget, zClose); |
| @@ -1687,14 +1765,22 @@ | |
| 1687 | |
| 1688 | /* Render invalid markup literally. The markup appears in the |
| 1689 | ** final output as plain text. |
| 1690 | */ |
| 1691 | if( markup.iCode==MARKUP_INVALID ){ |
| 1692 | unparseMarkup(&markup); |
| 1693 | startAutoParagraph(p); |
| 1694 | blob_append_string(p->pOut, "<"); |
| 1695 | n = 1; |
| 1696 | }else |
| 1697 | |
| 1698 | /* If the markup is not font-change markup ignore it if the |
| 1699 | ** font-change-only flag is set. |
| 1700 | */ |
| @@ -1708,16 +1794,10 @@ | |
| 1708 | }else{ |
| 1709 | p->state &= ~ALLOW_WIKI; |
| 1710 | } |
| 1711 | }else |
| 1712 | |
| 1713 | /* Ignore block markup for in-line rendering. |
| 1714 | */ |
| 1715 | if( inlineOnly && (markup.iType&MUTYPE_INLINE)==0 ){ |
| 1716 | /* Do nothing */ |
| 1717 | }else |
| 1718 | |
| 1719 | /* Generate end-tags */ |
| 1720 | if( markup.endTag ){ |
| 1721 | popStackToTag(p, markup.iCode); |
| 1722 | }else |
| 1723 | |
| @@ -1799,10 +1879,11 @@ | |
| 1799 | }else |
| 1800 | { |
| 1801 | if( markup.iType==MUTYPE_FONT ){ |
| 1802 | startAutoParagraph(p); |
| 1803 | }else if( markup.iType==MUTYPE_BLOCK || markup.iType==MUTYPE_LIST ){ |
| 1804 | p->wantAutoParagraph = 0; |
| 1805 | } |
| 1806 | if( markup.iCode==MARKUP_HR |
| 1807 | || markup.iCode==MARKUP_H1 |
| 1808 | || markup.iCode==MARKUP_H2 |
| @@ -1829,12 +1910,14 @@ | |
| 1829 | ** Transform the text in the pIn blob. Write the results |
| 1830 | ** into the pOut blob. The pOut blob should already be |
| 1831 | ** initialized. The output is merely appended to pOut. |
| 1832 | ** If pOut is NULL, then the output is appended to the CGI |
| 1833 | ** reply. |
| 1834 | */ |
| 1835 | void wiki_convert(Blob *pIn, Blob *pOut, int flags){ |
| 1836 | Renderer renderer; |
| 1837 | |
| 1838 | memset(&renderer, 0, sizeof(renderer)); |
| 1839 | renderer.renderFlags = flags; |
| 1840 | renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH|flags; |
| @@ -1858,10 +1941,11 @@ | |
| 1858 | while( renderer.nStack ){ |
| 1859 | popStack(&renderer); |
| 1860 | } |
| 1861 | blob_append_char(renderer.pOut, '\n'); |
| 1862 | free(renderer.aStack); |
| 1863 | } |
| 1864 | |
| 1865 | /* |
| 1866 | ** COMMAND: test-wiki-render |
| 1867 | ** |
| @@ -1870,36 +1954,85 @@ | |
| 1870 | ** Translate the input FILE from Fossil-wiki into HTML and write |
| 1871 | ** the resulting HTML on standard output. |
| 1872 | ** |
| 1873 | ** Options: |
| 1874 | ** --buttons Set the WIKI_BUTTONS flag |
| 1875 | ** --htmlonly Set the WIKI_HTMLONLY flag |
| 1876 | ** --linksonly Set the WIKI_LINKSONLY flag |
| 1877 | ** --nobadlinks Set the WIKI_NOBADLINKS flag |
| 1878 | ** --inline Set the WIKI_INLINE flag |
| 1879 | ** --noblock Set the WIKI_NOBLOCK flag |
| 1880 | ** --dark-pikchr Render pikchrs in dark mode |
| 1881 | */ |
| 1882 | void test_wiki_render(void){ |
| 1883 | Blob in, out; |
| 1884 | int flags = 0; |
| 1885 | if( find_option("buttons",0,0)!=0 ) flags |= WIKI_BUTTONS; |
| 1886 | if( find_option("htmlonly",0,0)!=0 ) flags |= WIKI_HTMLONLY; |
| 1887 | if( find_option("linksonly",0,0)!=0 ) flags |= WIKI_LINKSONLY; |
| 1888 | if( find_option("nobadlinks",0,0)!=0 ) flags |= WIKI_NOBADLINKS; |
| 1889 | if( find_option("inline",0,0)!=0 ) flags |= WIKI_INLINE; |
| 1890 | if( find_option("noblock",0,0)!=0 ) flags |= WIKI_NOBLOCK; |
| 1891 | if( find_option("dark-pikchr",0,0)!=0 ){ |
| 1892 | pikchr_to_html_add_flags( PIKCHR_PROCESS_DARK_MODE ); |
| 1893 | } |
| 1894 | db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0); |
| 1895 | verify_all_options(); |
| 1896 | if( g.argc!=3 ) usage("FILE"); |
| 1897 | blob_zero(&out); |
| 1898 | blob_read_from_file(&in, g.argv[2], ExtFILE); |
| 1899 | wiki_convert(&in, &out, flags); |
| 1900 | blob_write_to_file(&out, "-"); |
| 1901 | } |
| 1902 | |
| 1903 | /* |
| 1904 | ** COMMAND: test-markdown-render |
| 1905 | ** |
| @@ -1906,24 +2039,26 @@ | |
| 1906 | ** Usage: %fossil test-markdown-render FILE ... |
| 1907 | ** |
| 1908 | ** Render markdown in FILE as HTML on stdout. |
| 1909 | ** Options: |
| 1910 | ** |
| 1911 | ** --safe Restrict the output to use only "safe" HTML |
| 1912 | ** --lint-footnotes Print stats for footnotes-related issues |
| 1913 | ** --dark-pikchr Render pikchrs in dark mode |
| 1914 | */ |
| 1915 | void test_markdown_render(void){ |
| 1916 | Blob in, out; |
| 1917 | int i; |
| 1918 | int bSafe = 0, bFnLint = 0; |
| 1919 | db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0); |
| 1920 | bSafe = find_option("safe",0,0)!=0; |
| 1921 | bFnLint = find_option("lint-footnotes",0,0)!=0; |
| 1922 | if( find_option("dark-pikchr",0,0)!=0 ){ |
| 1923 | pikchr_to_html_add_flags( PIKCHR_PROCESS_DARK_MODE ); |
| 1924 | } |
| 1925 | verify_all_options(); |
| 1926 | for(i=2; i<g.argc; i++){ |
| 1927 | blob_zero(&out); |
| 1928 | blob_read_from_file(&in, g.argv[i], ExtFILE); |
| 1929 | if( g.argc>3 ){ |
| @@ -1930,10 +2065,17 @@ | |
| 1930 | fossil_print("<!------ %h ------->\n", g.argv[i]); |
| 1931 | } |
| 1932 | markdown_to_html(&in, 0, &out); |
| 1933 | safe_html_context( bSafe ? DOCSRC_UNTRUSTED : DOCSRC_TRUSTED ); |
| 1934 | safe_html(&out); |
| 1935 | blob_write_to_file(&out, "-"); |
| 1936 | blob_reset(&in); |
| 1937 | blob_reset(&out); |
| 1938 | } |
| 1939 | if( bFnLint && (g.ftntsIssues[0] || g.ftntsIssues[1] |
| @@ -1990,33 +2132,30 @@ | |
| 1990 | ** [target|...] |
| 1991 | ** |
| 1992 | ** Where "target" can be either an artifact ID prefix or a wiki page |
| 1993 | ** name. For each such hyperlink found, add an entry to the |
| 1994 | ** backlink table. |
| 1995 | */ |
| 1996 | void wiki_extract_links( |
| 1997 | char *z, /* The wiki text from which to extract links */ |
| 1998 | Backlink *pBklnk, /* Backlink extraction context */ |
| 1999 | int flags /* wiki parsing flags */ |
| 2000 | ){ |
| 2001 | Renderer renderer; |
| 2002 | int tokenType; |
| 2003 | ParsedMarkup markup; |
| 2004 | int n; |
| 2005 | int inlineOnly; |
| 2006 | int wikiHtmlOnly = 0; |
| 2007 | |
| 2008 | memset(&renderer, 0, sizeof(renderer)); |
| 2009 | renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH; |
| 2010 | if( flags & WIKI_NOBLOCK ){ |
| 2011 | renderer.state |= INLINE_MARKUP_ONLY; |
| 2012 | } |
| 2013 | if( wikiUsesHtml() ){ |
| 2014 | renderer.state |= WIKI_HTMLONLY; |
| 2015 | wikiHtmlOnly = 1; |
| 2016 | } |
| 2017 | inlineOnly = (renderer.state & INLINE_MARKUP_ONLY)!=0; |
| 2018 | |
| 2019 | while( z[0] ){ |
| 2020 | if( wikiHtmlOnly ){ |
| 2021 | n = nextRawToken(z, &renderer, &tokenType); |
| 2022 | }else{ |
| @@ -2093,16 +2232,10 @@ | |
| 2093 | }else{ |
| 2094 | renderer.state &= ~ALLOW_WIKI; |
| 2095 | } |
| 2096 | }else |
| 2097 | |
| 2098 | /* Ignore block markup for in-line rendering. |
| 2099 | */ |
| 2100 | if( inlineOnly && (markup.iType&MUTYPE_INLINE)==0 ){ |
| 2101 | /* Do nothing */ |
| 2102 | }else |
| 2103 | |
| 2104 | /* Generate end-tags */ |
| 2105 | if( markup.endTag ){ |
| 2106 | popStackToTag(&renderer, markup.iCode); |
| 2107 | }else |
| 2108 | |
| @@ -2141,10 +2274,11 @@ | |
| 2141 | } |
| 2142 | } |
| 2143 | z += n; |
| 2144 | } |
| 2145 | free(renderer.aStack); |
| 2146 | } |
| 2147 | |
| 2148 | /* |
| 2149 | ** Return the length, in bytes, of the HTML token that z is pointing to. |
| 2150 | */ |
| @@ -2393,33 +2527,61 @@ | |
| 2393 | blob_reset(&in); |
| 2394 | fossil_puts(blob_buffer(&out), 0, blob_size(&out)); |
| 2395 | blob_reset(&out); |
| 2396 | } |
| 2397 | } |
| 2398 | |
| 2399 | /* |
| 2400 | ** Remove all HTML markup from the input text. The output written into |
| 2401 | ** pOut is pure text. |
| 2402 | ** |
| 2403 | ** Put the title on the first line, if there is any <title> markup. |
| 2404 | ** If there is no <title>, then create a blank first line. |
| 2405 | */ |
| 2406 | void html_to_plaintext(const char *zIn, Blob *pOut){ |
| 2407 | int n; |
| 2408 | int i, j; |
| 2409 | int inTitle = 0; /* True between <title>...</title> */ |
| 2410 | int seenText = 0; /* True after first non-whitespace seen */ |
| 2411 | int nNL = 0; /* Number of \n characters at the end of pOut */ |
| 2412 | int nWS = 0; /* True if pOut ends with whitespace */ |
| 2413 | while( fossil_isspace(zIn[0]) ) zIn++; |
| 2414 | while( zIn[0] ){ |
| 2415 | n = html_token_length(zIn); |
| 2416 | if( zIn[0]=='<' && n>1 ){ |
| 2417 | int isCloseTag; |
| 2418 | int eTag; |
| 2419 | int eType; |
| 2420 | char zTag[32]; |
| 2421 | isCloseTag = zIn[1]=='/'; |
| 2422 | for(i=0, j=1+isCloseTag; i<30 && fossil_isalnum(zIn[j]); i++, j++){ |
| 2423 | zTag[i] = fossil_tolower(zIn[j]); |
| 2424 | } |
| 2425 | zTag[i] = 0; |
| @@ -2433,92 +2595,116 @@ | |
| 2433 | zIn += n; |
| 2434 | } |
| 2435 | if( zIn[0]=='<' ) zIn += n; |
| 2436 | continue; |
| 2437 | } |
| 2438 | if( eTag==MARKUP_TITLE ){ |
| 2439 | inTitle = !isCloseTag; |
| 2440 | } |
| 2441 | if( !isCloseTag && seenText && (eType & (MUTYPE_BLOCK|MUTYPE_TABLE))!=0 ){ |
| 2442 | if( nNL==0 ){ |
| 2443 | blob_append_char(pOut, '\n'); |
| 2444 | nNL++; |
| 2445 | } |
| 2446 | nWS = 1; |
| 2447 | } |
| 2448 | }else if( fossil_isspace(zIn[0]) ){ |
| 2449 | if( seenText ){ |
| 2450 | nNL = 0; |
| 2451 | if( !inTitle ){ /* '\n' -> ' ' within <title> */ |
| 2452 | for(i=0; i<n; i++) if( zIn[i]=='\n' ) nNL++; |
| 2453 | } |
| 2454 | if( !nWS ){ |
| 2455 | blob_append_char(pOut, nNL ? '\n' : ' '); |
| 2456 | nWS = 1; |
| 2457 | } |
| 2458 | } |
| 2459 | }else if( zIn[0]=='&' ){ |
| 2460 | char c = '?'; |
| 2461 | if( zIn[1]=='#' ){ |
| 2462 | int x = atoi(&zIn[1]); |
| 2463 | if( x>0 && x<=127 ) c = x; |
| 2464 | }else{ |
| 2465 | static const struct { int n; char c; char *z; } aEntity[] = { |
| 2466 | { 5, '&', "&" }, |
| 2467 | { 4, '<', "<" }, |
| 2468 | { 4, '>', ">" }, |
| 2469 | { 6, ' ', " " }, |
| 2470 | }; |
| 2471 | int jj; |
| 2472 | for(jj=0; jj<count(aEntity); jj++){ |
| 2473 | if( aEntity[jj].n==n && strncmp(aEntity[jj].z,zIn,n)==0 ){ |
| 2474 | c = aEntity[jj].c; |
| 2475 | break; |
| 2476 | } |
| 2477 | } |
| 2478 | } |
| 2479 | if( fossil_isspace(c) ){ |
| 2480 | if( nWS==0 && seenText ) blob_append_char(pOut, c); |
| 2481 | nWS = 1; |
| 2482 | nNL = c=='\n'; |
| 2483 | }else{ |
| 2484 | if( !seenText && !inTitle ) blob_append_char(pOut, '\n'); |
| 2485 | seenText = 1; |
| 2486 | nNL = nWS = 0; |
| 2487 | blob_append_char(pOut, c); |
| 2488 | } |
| 2489 | }else{ |
| 2490 | if( !seenText && !inTitle ) blob_append_char(pOut, '\n'); |
| 2491 | seenText = 1; |
| 2492 | nNL = nWS = 0; |
| 2493 | blob_append(pOut, zIn, n); |
| 2494 | } |
| 2495 | zIn += n; |
| 2496 | } |
| 2497 | if( nNL==0 ) blob_append_char(pOut, '\n'); |
| 2498 | } |
| 2499 | |
| 2500 | /* |
| 2501 | ** COMMAND: test-html-to-text |
| 2502 | ** |
| 2503 | ** Usage: %fossil test-html-to-text FILE ... |
| 2504 | ** |
| 2505 | ** Read all files named on the command-line. Convert the file |
| 2506 | ** content from HTML to text and write the results on standard |
| 2507 | ** output. |
| 2508 | ** |
| 2509 | ** This command is intended as a test and debug interface for |
| 2510 | ** the html_to_plaintext() routine. |
| 2511 | */ |
| 2512 | void test_html_to_text(void){ |
| 2513 | Blob in, out; |
| 2514 | int i; |
| 2515 | |
| 2516 | for(i=2; i<g.argc; i++){ |
| 2517 | blob_read_from_file(&in, g.argv[i], ExtFILE); |
| 2518 | blob_zero(&out); |
| 2519 | html_to_plaintext(blob_str(&in), &out); |
| 2520 | blob_reset(&in); |
| 2521 | fossil_puts(blob_buffer(&out), 0, blob_size(&out)); |
| 2522 | blob_reset(&out); |
| 2523 | } |
| 2524 | } |
| 2525 |
| --- src/wikiformat.c | |
| +++ src/wikiformat.c | |
| @@ -23,22 +23,43 @@ | |
| 23 | |
| 24 | #if INTERFACE |
| 25 | /* |
| 26 | ** Allowed wiki transformation operations |
| 27 | */ |
| 28 | #define WIKI_HTMLONLY 0x0001 /* HTML markup only. No wiki */ |
| 29 | #define WIKI_INLINE 0x0002 /* Do not surround with <p>..</p> */ |
| 30 | /* avalable for reuse: 0x0004 --- formerly WIKI_NOBLOCK */ |
| 31 | #define WIKI_BUTTONS 0x0008 /* Allow sub-menu buttons */ |
| 32 | #define WIKI_NOBADLINKS 0x0010 /* Ignore broken hyperlinks */ |
| 33 | #define WIKI_LINKSONLY 0x0020 /* No markup. Only decorate links */ |
| 34 | #define WIKI_NEWLINE 0x0040 /* Honor \n - break lines at each \n */ |
| 35 | #define WIKI_MARKDOWNLINKS 0x0080 /* Resolve hyperlinks as in markdown */ |
| 36 | #define WIKI_SAFE 0x0100 /* Make the result safe for embedding */ |
| 37 | #define WIKI_TARGET_BLANK 0x0200 /* Hyperlinks go to a new window */ |
| 38 | #define WIKI_NOBRACKET 0x0400 /* Omit extra [..] around hyperlinks */ |
| 39 | #define WIKI_ADMIN 0x0800 /* Ignore g.perm.Hyperlink */ |
| 40 | #define WIKI_MARK 0x1000 /* Add <mark>..</mark> around problems */ |
| 41 | |
| 42 | /* |
| 43 | ** Return values from wiki_convert |
| 44 | */ |
| 45 | #define RENDER_LINK 0x0001 /* One or more hyperlinks rendered */ |
| 46 | #define RENDER_ENTITY 0x0002 /* One or more HTML entities (ex: <) */ |
| 47 | #define RENDER_TAG 0x0004 /* One or more HTML tags */ |
| 48 | #define RENDER_BLOCKTAG 0x0008 /* One or more HTML block tags (ex: <p>) */ |
| 49 | #define RENDER_BLOCK 0x0010 /* Block wiki (paragraphs, etc.) */ |
| 50 | #define RENDER_MARK 0x0020 /* Output contains <mark>..</mark> */ |
| 51 | #define RENDER_BADLINK 0x0100 /* Bad hyperlink syntax seen */ |
| 52 | #define RENDER_BADTARGET 0x0200 /* Bad hyperlink target */ |
| 53 | #define RENDER_BADTAG 0x0400 /* Bad HTML tag or tag syntax */ |
| 54 | #define RENDER_BADENTITY 0x0800 /* Bad HTML entity syntax */ |
| 55 | #define RENDER_BADHTML 0x1000 /* Bad HTML seen */ |
| 56 | #define RENDER_ERROR 0x8000 /* Some other kind of error */ |
| 57 | /* Composite values: */ |
| 58 | #define RENDER_ANYERROR 0x9f00 /* Mask for any kind of error */ |
| 59 | |
| 60 | #endif /* INTERFACE */ |
| 61 | |
| 62 | |
| 63 | /* |
| 64 | ** These are the only markup attributes allowed. |
| 65 | */ |
| @@ -444,20 +465,20 @@ | |
| 465 | #define AT_NEWLINE 0x0010000 /* At start of a line */ |
| 466 | #define AT_PARAGRAPH 0x0020000 /* At start of a paragraph */ |
| 467 | #define ALLOW_WIKI 0x0040000 /* Allow wiki markup */ |
| 468 | #define ALLOW_LINKS 0x0080000 /* Allow [...] hyperlinks */ |
| 469 | #define FONT_MARKUP_ONLY 0x0100000 /* Only allow MUTYPE_FONT markup */ |
| 470 | #define IN_LIST 0x0200000 /* Within wiki <ul> or <ol> */ |
| 471 | |
| 472 | /* |
| 473 | ** Current state of the rendering engine |
| 474 | */ |
| 475 | typedef struct Renderer Renderer; |
| 476 | struct Renderer { |
| 477 | Blob *pOut; /* Output appended to this blob */ |
| 478 | int state; /* Flag that govern rendering */ |
| 479 | int mRender; /* Mask of RENDER_* values to return */ |
| 480 | unsigned renderFlags; /* Flags from the client */ |
| 481 | int wikiList; /* Current wiki list type */ |
| 482 | int inVerbatim; /* True in <verbatim> mode */ |
| 483 | int preVerbState; /* Value of state prior to verbatim */ |
| 484 | int wantAutoParagraph; /* True if a <p> is desired */ |
| @@ -679,21 +700,26 @@ | |
| 700 | static int nextWikiToken(const char *z, Renderer *p, int *pTokenType){ |
| 701 | int n; |
| 702 | if( z[0]=='<' ){ |
| 703 | n = html_tag_length(z); |
| 704 | if( n>0 ){ |
| 705 | p->mRender |= RENDER_TAG; |
| 706 | *pTokenType = TOKEN_MARKUP; |
| 707 | return n; |
| 708 | }else{ |
| 709 | p->mRender |= RENDER_BADTAG; |
| 710 | *pTokenType = TOKEN_CHARACTER; |
| 711 | return 1; |
| 712 | } |
| 713 | } |
| 714 | if( z[0]=='&' ){ |
| 715 | p->mRender |= RENDER_ENTITY; |
| 716 | if( (p->inVerbatim || !isElement(z)) ){ |
| 717 | *pTokenType = TOKEN_CHARACTER; |
| 718 | return 1; |
| 719 | } |
| 720 | } |
| 721 | if( (p->state & ALLOW_WIKI)!=0 ){ |
| 722 | if( z[0]=='\n' ){ |
| 723 | n = paragraphBreakLength(z); |
| 724 | if( n>0 ){ |
| 725 | *pTokenType = TOKEN_PARAGRAPH; |
| @@ -725,17 +751,31 @@ | |
| 751 | if( n>0 ){ |
| 752 | *pTokenType = TOKEN_INDENT; |
| 753 | return n; |
| 754 | } |
| 755 | } |
| 756 | if( z[0]=='[' ){ |
| 757 | if( (n = linkLength(z))>0 ){ |
| 758 | *pTokenType = TOKEN_LINK; |
| 759 | return n; |
| 760 | }else if( p->state & WIKI_MARK ){ |
| 761 | blob_append_string(p->pOut, "<mark>"); |
| 762 | p->mRender |= RENDER_BADLINK|RENDER_MARK; |
| 763 | }else{ |
| 764 | p->mRender |= RENDER_BADLINK; |
| 765 | } |
| 766 | } |
| 767 | }else if( (p->state & ALLOW_LINKS)!=0 && z[0]=='[' ){ |
| 768 | if( (n = linkLength(z))>0 ){ |
| 769 | *pTokenType = TOKEN_LINK; |
| 770 | return n; |
| 771 | }else if( p->state & WIKI_MARK ){ |
| 772 | blob_append_string(p->pOut, "<mark>"); |
| 773 | p->mRender |= RENDER_BADLINK|RENDER_MARK; |
| 774 | }else{ |
| 775 | p->mRender |= RENDER_BADLINK; |
| 776 | } |
| 777 | } |
| 778 | *pTokenType = TOKEN_TEXT; |
| 779 | return 1 + textLength(z+1, p->state); |
| 780 | } |
| 781 | |
| @@ -745,13 +785,20 @@ | |
| 785 | ** z points to the start of a token. Return the number of |
| 786 | ** characters in that token. Write the token type into *pTokenType. |
| 787 | */ |
| 788 | static int nextRawToken(const char *z, Renderer *p, int *pTokenType){ |
| 789 | int n; |
| 790 | if( z[0]=='[' ){ |
| 791 | if( (n = linkLength(z))>0 ){ |
| 792 | *pTokenType = TOKEN_LINK; |
| 793 | return n; |
| 794 | }else if( p->state & WIKI_MARK ){ |
| 795 | blob_append_string(p->pOut, "<mark>"); |
| 796 | p->mRender |= RENDER_BADLINK|RENDER_MARK; |
| 797 | }else{ |
| 798 | p->mRender |= RENDER_BADLINK; |
| 799 | } |
| 800 | } |
| 801 | *pTokenType = TOKEN_RAW; |
| 802 | return 1 + textLength(z+1, p->state); |
| 803 | } |
| 804 | |
| @@ -1248,12 +1295,16 @@ | |
| 1295 | ** [wiki:WikiPageName] |
| 1296 | ** |
| 1297 | ** [2010-02-27 07:13] |
| 1298 | ** |
| 1299 | ** [InterMap:Link] -> Interwiki link |
| 1300 | ** |
| 1301 | ** The return value is a mask of RENDER_* values indicating what happened. |
| 1302 | ** Probably the return value is 0 on success and RENDER_BADTARGET or |
| 1303 | ** RENDER_BADLINK if there are problems. |
| 1304 | */ |
| 1305 | int wiki_resolve_hyperlink( |
| 1306 | Blob *pOut, /* Write the HTML output here */ |
| 1307 | int mFlags, /* Rendering option flags */ |
| 1308 | const char *zTarget, /* Hyperlink target; text within [...] */ |
| 1309 | char *zClose, /* Write hyperlink closing text here */ |
| 1310 | int nClose, /* Bytes available in zClose[] */ |
| @@ -1263,10 +1314,11 @@ | |
| 1314 | const char *zTerm = "</a>"; |
| 1315 | const char *z; |
| 1316 | char *zExtra = 0; |
| 1317 | const char *zExtraNS = 0; |
| 1318 | char *zRemote = 0; |
| 1319 | int rc = 0; |
| 1320 | |
| 1321 | if( zTitle ){ |
| 1322 | zExtra = mprintf(" title='%h'", zTitle); |
| 1323 | zExtraNS = zExtra+1; |
| 1324 | }else if( mFlags & WIKI_TARGET_BLANK ){ |
| @@ -1316,15 +1368,20 @@ | |
| 1368 | } |
| 1369 | } |
| 1370 | }else if( !in_this_repo(zTarget) ){ |
| 1371 | if( (mFlags & (WIKI_LINKSONLY|WIKI_NOBADLINKS))!=0 ){ |
| 1372 | zTerm = ""; |
| 1373 | }else if( (mFlags & WIKI_MARK)!=0 ){ |
| 1374 | blob_appendf(pOut, "<mark>%s", zLB); |
| 1375 | zTerm = "]</mark>"; |
| 1376 | rc |= RENDER_MARK; |
| 1377 | }else{ |
| 1378 | blob_appendf(pOut, "<span class=\"brokenlink\">%s", zLB); |
| 1379 | zTerm = "]</span>"; |
| 1380 | } |
| 1381 | rc |= RENDER_BADTARGET; |
| 1382 | }else if( g.perm.Hyperlink || (mFlags & WIKI_ADMIN)!=0 ){ |
| 1383 | blob_appendf(pOut, "%z%s",xhref(zExtraNS, "%R/info/%s", zTarget), zLB); |
| 1384 | zTerm = "]</a>"; |
| 1385 | }else{ |
| 1386 | zTerm = ""; |
| 1387 | } |
| @@ -1340,33 +1397,54 @@ | |
| 1397 | }else{ |
| 1398 | blob_appendf(pOut, "<a href=\"%R/wiki?name=%T\"%s>", z, zExtra); |
| 1399 | } |
| 1400 | }else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-' |
| 1401 | && db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){ |
| 1402 | /* Dates or date-and-times in ISO8601 resolve to a link to the |
| 1403 | ** timeline for that date */ |
| 1404 | blob_appendf(pOut, "<a href=\"%R/timeline?c=%T\"%s>", zTarget, zExtra); |
| 1405 | }else if( mFlags & WIKI_MARKDOWNLINKS ){ |
| 1406 | /* If none of the above, and if rendering links for markdown, then |
| 1407 | ** create a link to the literal text of the target */ |
| 1408 | blob_appendf(pOut, "<a href=\"%h\"%s>", zTarget, zExtra); |
| 1409 | }else if( mFlags & WIKI_MARK ){ |
| 1410 | blob_appendf(pOut, "<mark>["); |
| 1411 | zTerm = "]</mark>"; |
| 1412 | rc |= RENDER_BADTARGET|RENDER_MARK; |
| 1413 | }else if( zOrig && zTarget>=&zOrig[2] |
| 1414 | && zTarget[-1]=='[' && !fossil_isspace(zTarget[-2]) ){ |
| 1415 | /* If the hyperlink markup is not preceded by whitespace, then it |
| 1416 | ** is probably a C-language subscript or similar, not really a |
| 1417 | ** hyperlink. Just ignore it. */ |
| 1418 | zTerm = ""; |
| 1419 | }else if( (mFlags & (WIKI_NOBADLINKS|WIKI_LINKSONLY))!=0 ){ |
| 1420 | /* Also ignore the link if various flags are set */ |
| 1421 | zTerm = ""; |
| 1422 | rc |= RENDER_BADTARGET; |
| 1423 | }else{ |
| 1424 | blob_appendf(pOut, "<span class=\"brokenlink\">[%h]", zTarget); |
| 1425 | zTerm = "</span>"; |
| 1426 | rc |= RENDER_BADTARGET; |
| 1427 | } |
| 1428 | if( zExtra ) fossil_free(zExtra); |
| 1429 | assert( (int)strlen(zTerm)<nClose ); |
| 1430 | sqlite3_snprintf(nClose, zClose, "%s", zTerm); |
| 1431 | return rc; |
| 1432 | } |
| 1433 | |
| 1434 | /* |
| 1435 | ** Check zTarget to see if it looks like a valid hyperlink target. |
| 1436 | ** Return true if it does seem valid and false if not. |
| 1437 | */ |
| 1438 | int wiki_valid_link_target(char *zTarget){ |
| 1439 | char zClose[30]; |
| 1440 | Blob notUsed; |
| 1441 | blob_init(¬Used, 0, 0); |
| 1442 | wiki_resolve_hyperlink(¬Used, WIKI_NOBADLINKS|WIKI_ADMIN, |
| 1443 | zTarget, zClose, sizeof(zClose)-1, 0, 0); |
| 1444 | blob_reset(¬Used); |
| 1445 | return zClose[0]!=0; |
| 1446 | } |
| 1447 | |
| 1448 | /* |
| 1449 | ** Check to see if the given parsed markup is the correct |
| 1450 | ** </verbatim> tag. |
| @@ -1450,11 +1528,10 @@ | |
| 1528 | */ |
| 1529 | static void wiki_render(Renderer *p, char *z){ |
| 1530 | int tokenType; |
| 1531 | ParsedMarkup markup; |
| 1532 | int n; |
| 1533 | int wikiHtmlOnly = (p->state & (WIKI_HTMLONLY | WIKI_LINKSONLY))!=0; |
| 1534 | int linksOnly = (p->state & WIKI_LINKSONLY)!=0; |
| 1535 | char *zOrig = z; |
| 1536 | |
| 1537 | /* Make sure the attribute constants and names still align |
| @@ -1468,22 +1545,17 @@ | |
| 1545 | n = nextWikiToken(z, p, &tokenType); |
| 1546 | } |
| 1547 | p->state &= ~(AT_NEWLINE|AT_PARAGRAPH); |
| 1548 | switch( tokenType ){ |
| 1549 | case TOKEN_PARAGRAPH: { |
| 1550 | if( p->wikiList ){ |
| 1551 | popStackToTag(p, p->wikiList); |
| 1552 | p->wikiList = 0; |
| 1553 | } |
| 1554 | endAutoParagraph(p); |
| 1555 | blob_append_string(p->pOut, "\n\n"); |
| 1556 | p->wantAutoParagraph = 1; |
| 1557 | p->state |= AT_PARAGRAPH|AT_NEWLINE; |
| 1558 | break; |
| 1559 | } |
| 1560 | case TOKEN_NEWLINE: { |
| 1561 | if( p->renderFlags & WIKI_NEWLINE ){ |
| @@ -1493,86 +1565,91 @@ | |
| 1565 | } |
| 1566 | p->state |= AT_NEWLINE; |
| 1567 | break; |
| 1568 | } |
| 1569 | case TOKEN_BUL_LI: { |
| 1570 | p->mRender |= RENDER_BLOCK; |
| 1571 | if( p->wikiList!=MARKUP_UL ){ |
| 1572 | if( p->wikiList ){ |
| 1573 | popStackToTag(p, p->wikiList); |
| 1574 | } |
| 1575 | endAutoParagraph(p); |
| 1576 | pushStack(p, MARKUP_UL); |
| 1577 | blob_append_string(p->pOut, "<ul>"); |
| 1578 | p->wikiList = MARKUP_UL; |
| 1579 | } |
| 1580 | popStackToTag(p, MARKUP_LI); |
| 1581 | startAutoParagraph(p); |
| 1582 | pushStack(p, MARKUP_LI); |
| 1583 | blob_append_string(p->pOut, "<li>"); |
| 1584 | break; |
| 1585 | } |
| 1586 | case TOKEN_NUM_LI: { |
| 1587 | p->mRender |= RENDER_BLOCK; |
| 1588 | if( p->wikiList!=MARKUP_OL ){ |
| 1589 | if( p->wikiList ){ |
| 1590 | popStackToTag(p, p->wikiList); |
| 1591 | } |
| 1592 | endAutoParagraph(p); |
| 1593 | pushStack(p, MARKUP_OL); |
| 1594 | blob_append_string(p->pOut, "<ol>"); |
| 1595 | p->wikiList = MARKUP_OL; |
| 1596 | } |
| 1597 | popStackToTag(p, MARKUP_LI); |
| 1598 | startAutoParagraph(p); |
| 1599 | pushStack(p, MARKUP_LI); |
| 1600 | blob_append_string(p->pOut, "<li>"); |
| 1601 | break; |
| 1602 | } |
| 1603 | case TOKEN_ENUM: { |
| 1604 | p->mRender |= RENDER_BLOCK; |
| 1605 | if( p->wikiList!=MARKUP_OL ){ |
| 1606 | if( p->wikiList ){ |
| 1607 | popStackToTag(p, p->wikiList); |
| 1608 | } |
| 1609 | endAutoParagraph(p); |
| 1610 | pushStack(p, MARKUP_OL); |
| 1611 | blob_append_string(p->pOut, "<ol>"); |
| 1612 | p->wikiList = MARKUP_OL; |
| 1613 | } |
| 1614 | popStackToTag(p, MARKUP_LI); |
| 1615 | startAutoParagraph(p); |
| 1616 | pushStack(p, MARKUP_LI); |
| 1617 | blob_appendf(p->pOut, "<li value=\"%d\">", atoi(z)); |
| 1618 | break; |
| 1619 | } |
| 1620 | case TOKEN_INDENT: { |
| 1621 | p->mRender |= RENDER_BLOCK; |
| 1622 | assert( p->wikiList==0 ); |
| 1623 | pushStack(p, MARKUP_BLOCKQUOTE); |
| 1624 | blob_append_string(p->pOut, "<blockquote>"); |
| 1625 | p->wantAutoParagraph = 0; |
| 1626 | p->wikiList = MARKUP_BLOCKQUOTE; |
| 1627 | break; |
| 1628 | } |
| 1629 | case TOKEN_CHARACTER: { |
| 1630 | startAutoParagraph(p); |
| 1631 | if( p->state & WIKI_MARK ){ |
| 1632 | blob_append_string(p->pOut, "<mark>"); |
| 1633 | p->mRender |= RENDER_MARK; |
| 1634 | } |
| 1635 | if( z[0]=='<' ){ |
| 1636 | p->mRender |= RENDER_BADTAG; |
| 1637 | blob_append_string(p->pOut, "<"); |
| 1638 | }else if( z[0]=='&' ){ |
| 1639 | p->mRender |= RENDER_BADENTITY; |
| 1640 | blob_append_string(p->pOut, "&"); |
| 1641 | } |
| 1642 | if( p->state & WIKI_MARK ){ |
| 1643 | if( fossil_isalnum(z[1]) || (z[1]=='/' && fossil_isalnum(z[2])) ){ |
| 1644 | int kk; |
| 1645 | for(kk=2; fossil_isalnum(z[kk]); kk++){} |
| 1646 | blob_append(p->pOut, &z[1], kk-1); |
| 1647 | n = kk; |
| 1648 | } |
| 1649 | blob_append_string(p->pOut, "</mark>"); |
| 1650 | } |
| 1651 | break; |
| 1652 | } |
| 1653 | case TOKEN_LINK: { |
| 1654 | char *zTarget; |
| 1655 | char *zDisplay = 0; |
| @@ -1581,10 +1658,11 @@ | |
| 1658 | char zClose[20]; |
| 1659 | char cS1 = 0; |
| 1660 | int iS1 = 0; |
| 1661 | |
| 1662 | startAutoParagraph(p); |
| 1663 | p->mRender |= RENDER_LINK; |
| 1664 | zTarget = &z[1]; |
| 1665 | for(i=1; z[i] && z[i]!=']'; i++){ |
| 1666 | if( z[i]=='|' && zDisplay==0 ){ |
| 1667 | zDisplay = &z[i+1]; |
| 1668 | for(j=i; j>0 && fossil_isspace(z[j-1]); j--){} |
| @@ -1597,11 +1675,11 @@ | |
| 1675 | if( zDisplay==0 ){ |
| 1676 | zDisplay = zTarget + interwiki_removable_prefix(zTarget); |
| 1677 | }else{ |
| 1678 | while( fossil_isspace(*zDisplay) ) zDisplay++; |
| 1679 | } |
| 1680 | p->mRender |= wiki_resolve_hyperlink(p->pOut, p->state, |
| 1681 | zTarget, zClose, sizeof(zClose), zOrig, 0); |
| 1682 | if( linksOnly || zClose[0]==0 || p->inVerbatim ){ |
| 1683 | if( cS1 ) z[iS1] = cS1; |
| 1684 | if( zClose[0]!=']' ){ |
| 1685 | blob_appendf(p->pOut, "[%h]%s", zTarget, zClose); |
| @@ -1687,14 +1765,22 @@ | |
| 1765 | |
| 1766 | /* Render invalid markup literally. The markup appears in the |
| 1767 | ** final output as plain text. |
| 1768 | */ |
| 1769 | if( markup.iCode==MARKUP_INVALID ){ |
| 1770 | p->mRender |= RENDER_BADTAG; |
| 1771 | unparseMarkup(&markup); |
| 1772 | startAutoParagraph(p); |
| 1773 | if( p->state & WIKI_MARK ){ |
| 1774 | p->mRender |= RENDER_MARK; |
| 1775 | blob_append_string(p->pOut, "<mark>"); |
| 1776 | htmlize_to_blob(p->pOut, z, n); |
| 1777 | blob_append_string(p->pOut, "</mark>"); |
| 1778 | }else{ |
| 1779 | blob_append_string(p->pOut, "<"); |
| 1780 | htmlize_to_blob(p->pOut, z+1, n-1); |
| 1781 | } |
| 1782 | }else |
| 1783 | |
| 1784 | /* If the markup is not font-change markup ignore it if the |
| 1785 | ** font-change-only flag is set. |
| 1786 | */ |
| @@ -1708,16 +1794,10 @@ | |
| 1794 | }else{ |
| 1795 | p->state &= ~ALLOW_WIKI; |
| 1796 | } |
| 1797 | }else |
| 1798 | |
| 1799 | /* Generate end-tags */ |
| 1800 | if( markup.endTag ){ |
| 1801 | popStackToTag(p, markup.iCode); |
| 1802 | }else |
| 1803 | |
| @@ -1799,10 +1879,11 @@ | |
| 1879 | }else |
| 1880 | { |
| 1881 | if( markup.iType==MUTYPE_FONT ){ |
| 1882 | startAutoParagraph(p); |
| 1883 | }else if( markup.iType==MUTYPE_BLOCK || markup.iType==MUTYPE_LIST ){ |
| 1884 | p->mRender |= RENDER_BLOCKTAG; |
| 1885 | p->wantAutoParagraph = 0; |
| 1886 | } |
| 1887 | if( markup.iCode==MARKUP_HR |
| 1888 | || markup.iCode==MARKUP_H1 |
| 1889 | || markup.iCode==MARKUP_H2 |
| @@ -1829,12 +1910,14 @@ | |
| 1910 | ** Transform the text in the pIn blob. Write the results |
| 1911 | ** into the pOut blob. The pOut blob should already be |
| 1912 | ** initialized. The output is merely appended to pOut. |
| 1913 | ** If pOut is NULL, then the output is appended to the CGI |
| 1914 | ** reply. |
| 1915 | ** |
| 1916 | ** Return a mask of RENDER_ flags indicating what happened. |
| 1917 | */ |
| 1918 | int wiki_convert(Blob *pIn, Blob *pOut, int flags){ |
| 1919 | Renderer renderer; |
| 1920 | |
| 1921 | memset(&renderer, 0, sizeof(renderer)); |
| 1922 | renderer.renderFlags = flags; |
| 1923 | renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH|flags; |
| @@ -1858,10 +1941,11 @@ | |
| 1941 | while( renderer.nStack ){ |
| 1942 | popStack(&renderer); |
| 1943 | } |
| 1944 | blob_append_char(renderer.pOut, '\n'); |
| 1945 | free(renderer.aStack); |
| 1946 | return renderer.mRender; |
| 1947 | } |
| 1948 | |
| 1949 | /* |
| 1950 | ** COMMAND: test-wiki-render |
| 1951 | ** |
| @@ -1870,36 +1954,85 @@ | |
| 1954 | ** Translate the input FILE from Fossil-wiki into HTML and write |
| 1955 | ** the resulting HTML on standard output. |
| 1956 | ** |
| 1957 | ** Options: |
| 1958 | ** --buttons Set the WIKI_BUTTONS flag |
| 1959 | ** --dark-pikchr Render pikchrs in dark mode |
| 1960 | ** --flow Render as text using comment_format |
| 1961 | ** --htmlonly Set the WIKI_HTMLONLY flag |
| 1962 | ** --inline Set the WIKI_INLINE flag |
| 1963 | ** --linksonly Set the WIKI_LINKSONLY flag |
| 1964 | ** -m TEXT Use TEXT in place of the content of FILE |
| 1965 | ** --mark Add <mark>...</mark> around problems |
| 1966 | ** --nobadlinks Set the WIKI_NOBADLINKS flag |
| 1967 | ** --text Run the output through html_to_plaintext() |
| 1968 | ** --type Break down the return code from wiki_convert() |
| 1969 | */ |
| 1970 | void test_wiki_render(void){ |
| 1971 | Blob in, out; |
| 1972 | int flags = 0; |
| 1973 | int bText; |
| 1974 | int bFlow = 0; |
| 1975 | int showType = 0; |
| 1976 | int mType; |
| 1977 | const char *zIn; |
| 1978 | if( find_option("buttons",0,0)!=0 ) flags |= WIKI_BUTTONS; |
| 1979 | if( find_option("htmlonly",0,0)!=0 ) flags |= WIKI_HTMLONLY; |
| 1980 | if( find_option("linksonly",0,0)!=0 ) flags |= WIKI_LINKSONLY; |
| 1981 | if( find_option("nobadlinks",0,0)!=0 ) flags |= WIKI_NOBADLINKS; |
| 1982 | if( find_option("inline",0,0)!=0 ) flags |= WIKI_INLINE; |
| 1983 | if( find_option("mark",0,0)!=0 ) flags |= WIKI_MARK; |
| 1984 | if( find_option("dark-pikchr",0,0)!=0 ){ |
| 1985 | pikchr_to_html_add_flags( PIKCHR_PROCESS_DARK_MODE ); |
| 1986 | } |
| 1987 | bText = find_option("text",0,0)!=0; |
| 1988 | bFlow = find_option("flow",0,0)!=0; |
| 1989 | showType = find_option("type",0,0)!=0; |
| 1990 | zIn = find_option("msg","m",1); |
| 1991 | db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0); |
| 1992 | verify_all_options(); |
| 1993 | if( (zIn==0 && g.argc!=3) || (zIn!=0 && g.argc!=2) ) usage("FILE"); |
| 1994 | blob_zero(&out); |
| 1995 | if( zIn ){ |
| 1996 | blob_init(&in, zIn, -1); |
| 1997 | }else{ |
| 1998 | blob_read_from_file(&in, g.argv[2], ExtFILE); |
| 1999 | } |
| 2000 | mType = wiki_convert(&in, &out, flags); |
| 2001 | if( bText ){ |
| 2002 | Blob txt; |
| 2003 | int htot = HTOT_TRIM; |
| 2004 | if( terminal_is_vt100() ) htot |= HTOT_VT100; |
| 2005 | if( bFlow ) htot |= HTOT_FLOW; |
| 2006 | blob_init(&txt, 0, 0); |
| 2007 | html_to_plaintext(blob_str(&out),&txt, htot); |
| 2008 | blob_reset(&out); |
| 2009 | out = txt; |
| 2010 | } |
| 2011 | if( bFlow ){ |
| 2012 | fossil_print(" "); |
| 2013 | comment_print(blob_str(&out), 0, 3, terminal_get_width(80)-3, |
| 2014 | get_comment_format()); |
| 2015 | }else{ |
| 2016 | blob_write_to_file(&out, "-"); |
| 2017 | } |
| 2018 | if( showType ){ |
| 2019 | fossil_print("%.*c\nResult Codes:", terminal_get_width(80)-1, '*'); |
| 2020 | if( mType & RENDER_LINK ) fossil_print(" LINK"); |
| 2021 | if( mType & RENDER_ENTITY ) fossil_print(" ENTITY"); |
| 2022 | if( mType & RENDER_TAG ) fossil_print(" TAG"); |
| 2023 | if( mType & RENDER_BLOCKTAG ) fossil_print(" BLOCKTAG"); |
| 2024 | if( mType & RENDER_BLOCK ) fossil_print(" BLOCK"); |
| 2025 | if( mType & RENDER_MARK ) fossil_print(" MARK"); |
| 2026 | if( mType & RENDER_BADLINK ) fossil_print(" BADLINK"); |
| 2027 | if( mType & RENDER_BADTARGET ) fossil_print(" BADTARGET"); |
| 2028 | if( mType & RENDER_BADTAG ) fossil_print(" BADTAG"); |
| 2029 | if( mType & RENDER_BADENTITY ) fossil_print(" BADENTITY"); |
| 2030 | if( mType & RENDER_BADHTML ) fossil_print(" BADHTML"); |
| 2031 | if( mType & RENDER_ERROR ) fossil_print(" ERROR"); |
| 2032 | fossil_print("\n"); |
| 2033 | } |
| 2034 | } |
| 2035 | |
| 2036 | /* |
| 2037 | ** COMMAND: test-markdown-render |
| 2038 | ** |
| @@ -1906,24 +2039,26 @@ | |
| 2039 | ** Usage: %fossil test-markdown-render FILE ... |
| 2040 | ** |
| 2041 | ** Render markdown in FILE as HTML on stdout. |
| 2042 | ** Options: |
| 2043 | ** |
| 2044 | ** --dark-pikchr Render pikchrs in dark mode |
| 2045 | ** --lint-footnotes Print stats for footnotes-related issues |
| 2046 | ** --safe Restrict the output to use only "safe" HTML |
| 2047 | ** --text Run the output through html_to_plaintext(). |
| 2048 | */ |
| 2049 | void test_markdown_render(void){ |
| 2050 | Blob in, out; |
| 2051 | int i; |
| 2052 | int bSafe = 0, bFnLint = 0, bText = 0; |
| 2053 | db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0); |
| 2054 | bSafe = find_option("safe",0,0)!=0; |
| 2055 | bFnLint = find_option("lint-footnotes",0,0)!=0; |
| 2056 | if( find_option("dark-pikchr",0,0)!=0 ){ |
| 2057 | pikchr_to_html_add_flags( PIKCHR_PROCESS_DARK_MODE ); |
| 2058 | } |
| 2059 | bText = find_option("text",0,0)!=0; |
| 2060 | verify_all_options(); |
| 2061 | for(i=2; i<g.argc; i++){ |
| 2062 | blob_zero(&out); |
| 2063 | blob_read_from_file(&in, g.argv[i], ExtFILE); |
| 2064 | if( g.argc>3 ){ |
| @@ -1930,10 +2065,17 @@ | |
| 2065 | fossil_print("<!------ %h ------->\n", g.argv[i]); |
| 2066 | } |
| 2067 | markdown_to_html(&in, 0, &out); |
| 2068 | safe_html_context( bSafe ? DOCSRC_UNTRUSTED : DOCSRC_TRUSTED ); |
| 2069 | safe_html(&out); |
| 2070 | if( bText ){ |
| 2071 | Blob txt; |
| 2072 | blob_init(&txt, 0, 0); |
| 2073 | html_to_plaintext(blob_str(&out), &txt, HTOT_VT100); |
| 2074 | blob_reset(&out); |
| 2075 | out = txt; |
| 2076 | } |
| 2077 | blob_write_to_file(&out, "-"); |
| 2078 | blob_reset(&in); |
| 2079 | blob_reset(&out); |
| 2080 | } |
| 2081 | if( bFnLint && (g.ftntsIssues[0] || g.ftntsIssues[1] |
| @@ -1990,33 +2132,30 @@ | |
| 2132 | ** [target|...] |
| 2133 | ** |
| 2134 | ** Where "target" can be either an artifact ID prefix or a wiki page |
| 2135 | ** name. For each such hyperlink found, add an entry to the |
| 2136 | ** backlink table. |
| 2137 | ** |
| 2138 | ** The return value is a mask of RENDER_ flags. |
| 2139 | */ |
| 2140 | int wiki_extract_links( |
| 2141 | char *z, /* The wiki text from which to extract links */ |
| 2142 | Backlink *pBklnk, /* Backlink extraction context */ |
| 2143 | int flags /* wiki parsing flags */ |
| 2144 | ){ |
| 2145 | Renderer renderer; |
| 2146 | int tokenType; |
| 2147 | ParsedMarkup markup; |
| 2148 | int n; |
| 2149 | int wikiHtmlOnly = 0; |
| 2150 | |
| 2151 | memset(&renderer, 0, sizeof(renderer)); |
| 2152 | renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH; |
| 2153 | if( wikiUsesHtml() ){ |
| 2154 | renderer.state |= WIKI_HTMLONLY; |
| 2155 | wikiHtmlOnly = 1; |
| 2156 | } |
| 2157 | |
| 2158 | while( z[0] ){ |
| 2159 | if( wikiHtmlOnly ){ |
| 2160 | n = nextRawToken(z, &renderer, &tokenType); |
| 2161 | }else{ |
| @@ -2093,16 +2232,10 @@ | |
| 2232 | }else{ |
| 2233 | renderer.state &= ~ALLOW_WIKI; |
| 2234 | } |
| 2235 | }else |
| 2236 | |
| 2237 | /* Generate end-tags */ |
| 2238 | if( markup.endTag ){ |
| 2239 | popStackToTag(&renderer, markup.iCode); |
| 2240 | }else |
| 2241 | |
| @@ -2141,10 +2274,11 @@ | |
| 2274 | } |
| 2275 | } |
| 2276 | z += n; |
| 2277 | } |
| 2278 | free(renderer.aStack); |
| 2279 | return renderer.mRender; |
| 2280 | } |
| 2281 | |
| 2282 | /* |
| 2283 | ** Return the length, in bytes, of the HTML token that z is pointing to. |
| 2284 | */ |
| @@ -2393,33 +2527,61 @@ | |
| 2527 | blob_reset(&in); |
| 2528 | fossil_puts(blob_buffer(&out), 0, blob_size(&out)); |
| 2529 | blob_reset(&out); |
| 2530 | } |
| 2531 | } |
| 2532 | |
| 2533 | #if INTERFACE |
| 2534 | /* |
| 2535 | ** Allowed flag options for html_to_plaintext(). |
| 2536 | */ |
| 2537 | #define HTOT_VT100 0x01 /* <mark> becomes ^[[91m */ |
| 2538 | #define HTOT_FLOW 0x02 /* Collapse internal whitespace to a single space */ |
| 2539 | #define HTOT_TRIM 0x04 /* Trim off leading and trailing whitespace */ |
| 2540 | |
| 2541 | #endif /* INTERFACE */ |
| 2542 | |
| 2543 | /* |
| 2544 | ** Add <MARK> or </MARK> to the output, or similar VT-100 escape |
| 2545 | ** codes. |
| 2546 | */ |
| 2547 | static void addMark(Blob *pOut, int mFlags, int isClose){ |
| 2548 | static const char *az[4] = { "<MARK>", "</MARK>", "\033[91m", "\033[0m" }; |
| 2549 | int i = 0; |
| 2550 | if( isClose ) i++; |
| 2551 | if( mFlags & HTOT_VT100 ) i += 2; |
| 2552 | blob_append(pOut, az[i], -1); |
| 2553 | } |
| 2554 | |
| 2555 | /* |
| 2556 | ** Remove all HTML markup from the input text. The output written into |
| 2557 | ** pOut is pure text. |
| 2558 | ** |
| 2559 | ** Put the title on the first line, if there is any <title> markup. |
| 2560 | ** If there is no <title>, then create a blank first line. |
| 2561 | */ |
| 2562 | void html_to_plaintext(const char *zIn, Blob *pOut, int mFlags){ |
| 2563 | int n; |
| 2564 | int i, j; |
| 2565 | int bFlow = 0; /* Transform internal WS into a single space */ |
| 2566 | int prevWS = 1; /* Previous output was whitespace or start of msg */ |
| 2567 | int nMark = 0; /* True if inside of <mark>..</mark> */ |
| 2568 | |
| 2569 | for(i=0; fossil_isspace(zIn[i]); i++){} |
| 2570 | if( i>0 && (mFlags & HTOT_TRIM)==0 ){ |
| 2571 | blob_append(pOut, zIn, i); |
| 2572 | } |
| 2573 | zIn += i; |
| 2574 | if( mFlags & HTOT_FLOW ) bFlow = 1; |
| 2575 | while( zIn[0] ){ |
| 2576 | n = html_token_length(zIn); |
| 2577 | if( zIn[0]=='<' && n>1 ){ |
| 2578 | int isCloseTag; |
| 2579 | int eTag; |
| 2580 | int eType; |
| 2581 | char zTag[32]; |
| 2582 | prevWS = 0; |
| 2583 | isCloseTag = zIn[1]=='/'; |
| 2584 | for(i=0, j=1+isCloseTag; i<30 && fossil_isalnum(zIn[j]); i++, j++){ |
| 2585 | zTag[i] = fossil_tolower(zIn[j]); |
| 2586 | } |
| 2587 | zTag[i] = 0; |
| @@ -2433,92 +2595,116 @@ | |
| 2595 | zIn += n; |
| 2596 | } |
| 2597 | if( zIn[0]=='<' ) zIn += n; |
| 2598 | continue; |
| 2599 | } |
| 2600 | if( eTag==MARKUP_INVALID && strcmp(zTag,"mark")==0 ){ |
| 2601 | if( isCloseTag && nMark ){ |
| 2602 | addMark(pOut, mFlags, 1); |
| 2603 | nMark = 0; |
| 2604 | }else if( !isCloseTag && !nMark ){ |
| 2605 | addMark(pOut, mFlags, 0); |
| 2606 | nMark = 1; |
| 2607 | } |
| 2608 | zIn += n; |
| 2609 | continue; |
| 2610 | } |
| 2611 | if( eTag==MARKUP_TITLE ){ |
| 2612 | if( isCloseTag && (mFlags & HTOT_FLOW)==0 ){ |
| 2613 | bFlow = 0; |
| 2614 | }else{ |
| 2615 | bFlow = 1; |
| 2616 | } |
| 2617 | } |
| 2618 | if( !isCloseTag && (eType & (MUTYPE_BLOCK|MUTYPE_TABLE))!=0 ){ |
| 2619 | blob_append_char(pOut, '\n'); |
| 2620 | } |
| 2621 | }else if( fossil_isspace(zIn[0]) ){ |
| 2622 | if( bFlow==0 ){ |
| 2623 | if( zIn[n]==0 && (mFlags & HTOT_TRIM) ) break; |
| 2624 | blob_append(pOut, zIn, n); |
| 2625 | }else if( !prevWS ){ |
| 2626 | prevWS = 1; |
| 2627 | blob_append_char(pOut, ' '); |
| 2628 | zIn += n; |
| 2629 | n = 0; |
| 2630 | } |
| 2631 | }else if( zIn[0]=='&' ){ |
| 2632 | u32 c = '?'; |
| 2633 | prevWS = 0; |
| 2634 | if( zIn[1]=='#' ){ |
| 2635 | c = atoi(&zIn[2]); |
| 2636 | if( c==0 ) c = '?'; |
| 2637 | }else{ |
| 2638 | static const struct { int n; u32 c; char *z; } aEntity[] = { |
| 2639 | { 5, '&', "&" }, |
| 2640 | { 4, '<', "<" }, |
| 2641 | { 4, '>', ">" }, |
| 2642 | { 6, ' ', " " }, |
| 2643 | { 6, '"', """ }, |
| 2644 | }; |
| 2645 | int jj; |
| 2646 | for(jj=0; jj<count(aEntity); jj++){ |
| 2647 | if( aEntity[jj].n==n && strncmp(aEntity[jj].z,zIn,n)==0 ){ |
| 2648 | c = aEntity[jj].c; |
| 2649 | break; |
| 2650 | } |
| 2651 | } |
| 2652 | } |
| 2653 | if( c<0x00080 ){ |
| 2654 | blob_append_char(pOut, c & 0xff); |
| 2655 | }else if( c<0x00800 ){ |
| 2656 | blob_append_char(pOut, 0xc0 + (u8)((c>>6)&0x1f)); |
| 2657 | blob_append_char(pOut, 0x80 + (u8)(c&0x3f)); |
| 2658 | }else if( c<0x10000 ){ |
| 2659 | blob_append_char(pOut, 0xe0 + (u8)((c>>12)&0x0f)); |
| 2660 | blob_append_char(pOut, 0x80 + (u8)((c>>6)&0x3f)); |
| 2661 | blob_append_char(pOut, 0x80 + (u8)(c&0x3f)); |
| 2662 | }else{ |
| 2663 | blob_append_char(pOut, 0xf0 + (u8)((c>>18)&0x07)); |
| 2664 | blob_append_char(pOut, 0x80 + (u8)((c>>12)&0x3f)); |
| 2665 | blob_append_char(pOut, 0x80 + (u8)((c>>6)&0x3f)); |
| 2666 | blob_append_char(pOut, 0x80 + (u8)(c&0x3f)); |
| 2667 | } |
| 2668 | }else{ |
| 2669 | prevWS = 0; |
| 2670 | blob_append(pOut, zIn, n); |
| 2671 | } |
| 2672 | zIn += n; |
| 2673 | } |
| 2674 | if( nMark ){ |
| 2675 | addMark(pOut, mFlags, 1); |
| 2676 | } |
| 2677 | } |
| 2678 | |
| 2679 | /* |
| 2680 | ** COMMAND: test-html-to-text |
| 2681 | ** |
| 2682 | ** Usage: %fossil test-html-to-text [OPTIONS] FILE ... |
| 2683 | ** |
| 2684 | ** Read all files named on the command-line. Convert the file |
| 2685 | ** content from HTML to text and write the results on standard |
| 2686 | ** output. |
| 2687 | ** |
| 2688 | ** This command is intended as a test and debug interface for |
| 2689 | ** the html_to_plaintext() routine. |
| 2690 | ** |
| 2691 | ** Options: |
| 2692 | ** |
| 2693 | ** --vt100 Translate <mark> and </mark> into ANSI/VT100 |
| 2694 | ** escapes to highlight the contained text. |
| 2695 | */ |
| 2696 | void test_html_to_text(void){ |
| 2697 | Blob in, out; |
| 2698 | int i; |
| 2699 | int mFlags = 0; |
| 2700 | if( find_option("vt100",0,0)!=0 ) mFlags |= HTOT_VT100; |
| 2701 | |
| 2702 | for(i=2; i<g.argc; i++){ |
| 2703 | blob_read_from_file(&in, g.argv[i], ExtFILE); |
| 2704 | blob_zero(&out); |
| 2705 | html_to_plaintext(blob_str(&in), &out, mFlags); |
| 2706 | blob_reset(&in); |
| 2707 | fossil_puts(blob_buffer(&out), 0, blob_size(&out)); |
| 2708 | blob_reset(&out); |
| 2709 | } |
| 2710 | } |
| 2711 |
+2
-2
| --- src/winhttp.c | ||
| +++ src/winhttp.c | ||
| @@ -996,11 +996,11 @@ | ||
| 996 | 996 | ** |
| 997 | 997 | ** -D|--display DISPLAY-NAME |
| 998 | 998 | ** |
| 999 | 999 | ** Sets the display name of the service. This name is shown |
| 1000 | 1000 | ** by graphical interface programs. By default, the display name |
| 1001 | -** equals to the service name. | |
| 1001 | +** is equal to the service name. | |
| 1002 | 1002 | ** |
| 1003 | 1003 | ** -S|--start TYPE |
| 1004 | 1004 | ** |
| 1005 | 1005 | ** Sets the start type of the service. TYPE can be "manual", |
| 1006 | 1006 | ** which means you need to start the service yourself with the |
| @@ -1019,11 +1019,11 @@ | ||
| 1019 | 1019 | ** -W|--password PASSWORD |
| 1020 | 1020 | ** |
| 1021 | 1021 | ** Password for the user account. |
| 1022 | 1022 | ** |
| 1023 | 1023 | ** The following options are more or less the same as for the "server" |
| 1024 | -** command and influence the behaviour of the http server: | |
| 1024 | +** command and influence the behavior of the http server: | |
| 1025 | 1025 | ** |
| 1026 | 1026 | ** --baseurl URL |
| 1027 | 1027 | ** |
| 1028 | 1028 | ** Use URL as the base (useful for reverse proxies) |
| 1029 | 1029 | ** |
| 1030 | 1030 |
| --- src/winhttp.c | |
| +++ src/winhttp.c | |
| @@ -996,11 +996,11 @@ | |
| 996 | ** |
| 997 | ** -D|--display DISPLAY-NAME |
| 998 | ** |
| 999 | ** Sets the display name of the service. This name is shown |
| 1000 | ** by graphical interface programs. By default, the display name |
| 1001 | ** equals to the service name. |
| 1002 | ** |
| 1003 | ** -S|--start TYPE |
| 1004 | ** |
| 1005 | ** Sets the start type of the service. TYPE can be "manual", |
| 1006 | ** which means you need to start the service yourself with the |
| @@ -1019,11 +1019,11 @@ | |
| 1019 | ** -W|--password PASSWORD |
| 1020 | ** |
| 1021 | ** Password for the user account. |
| 1022 | ** |
| 1023 | ** The following options are more or less the same as for the "server" |
| 1024 | ** command and influence the behaviour of the http server: |
| 1025 | ** |
| 1026 | ** --baseurl URL |
| 1027 | ** |
| 1028 | ** Use URL as the base (useful for reverse proxies) |
| 1029 | ** |
| 1030 |
| --- src/winhttp.c | |
| +++ src/winhttp.c | |
| @@ -996,11 +996,11 @@ | |
| 996 | ** |
| 997 | ** -D|--display DISPLAY-NAME |
| 998 | ** |
| 999 | ** Sets the display name of the service. This name is shown |
| 1000 | ** by graphical interface programs. By default, the display name |
| 1001 | ** is equal to the service name. |
| 1002 | ** |
| 1003 | ** -S|--start TYPE |
| 1004 | ** |
| 1005 | ** Sets the start type of the service. TYPE can be "manual", |
| 1006 | ** which means you need to start the service yourself with the |
| @@ -1019,11 +1019,11 @@ | |
| 1019 | ** -W|--password PASSWORD |
| 1020 | ** |
| 1021 | ** Password for the user account. |
| 1022 | ** |
| 1023 | ** The following options are more or less the same as for the "server" |
| 1024 | ** command and influence the behavior of the http server: |
| 1025 | ** |
| 1026 | ** --baseurl URL |
| 1027 | ** |
| 1028 | ** Use URL as the base (useful for reverse proxies) |
| 1029 | ** |
| 1030 |
+1
-1
| --- src/zip.c | ||
| +++ src/zip.c | ||
| @@ -652,11 +652,11 @@ | ||
| 652 | 652 | pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0); |
| 653 | 653 | if( pManifest ){ |
| 654 | 654 | int flg, eflg = 0; |
| 655 | 655 | char *zName = 0; |
| 656 | 656 | zip_set_timedate(pManifest->rDate); |
| 657 | - flg = db_get_manifest_setting(); | |
| 657 | + flg = db_get_manifest_setting(blob_str(&hash)); | |
| 658 | 658 | if( flg ){ |
| 659 | 659 | /* eflg is the effective flags, taking include/exclude into account */ |
| 660 | 660 | if( (pInclude==0 || glob_match(pInclude, "manifest")) |
| 661 | 661 | && !glob_match(pExclude, "manifest") |
| 662 | 662 | && (flg & MFESTFLG_RAW) ){ |
| 663 | 663 | |
| 664 | 664 | DELETED test/comment.test |
| --- src/zip.c | |
| +++ src/zip.c | |
| @@ -652,11 +652,11 @@ | |
| 652 | pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0); |
| 653 | if( pManifest ){ |
| 654 | int flg, eflg = 0; |
| 655 | char *zName = 0; |
| 656 | zip_set_timedate(pManifest->rDate); |
| 657 | flg = db_get_manifest_setting(); |
| 658 | if( flg ){ |
| 659 | /* eflg is the effective flags, taking include/exclude into account */ |
| 660 | if( (pInclude==0 || glob_match(pInclude, "manifest")) |
| 661 | && !glob_match(pExclude, "manifest") |
| 662 | && (flg & MFESTFLG_RAW) ){ |
| 663 | |
| 664 | ELETED test/comment.test |
| --- src/zip.c | |
| +++ src/zip.c | |
| @@ -652,11 +652,11 @@ | |
| 652 | pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0); |
| 653 | if( pManifest ){ |
| 654 | int flg, eflg = 0; |
| 655 | char *zName = 0; |
| 656 | zip_set_timedate(pManifest->rDate); |
| 657 | flg = db_get_manifest_setting(blob_str(&hash)); |
| 658 | if( flg ){ |
| 659 | /* eflg is the effective flags, taking include/exclude into account */ |
| 660 | if( (pInclude==0 || glob_match(pInclude, "manifest")) |
| 661 | && !glob_match(pExclude, "manifest") |
| 662 | && (flg & MFESTFLG_RAW) ){ |
| 663 | |
| 664 | ELETED test/comment.test |
D
test/comment.test
-346
| --- a/test/comment.test | ||
| +++ b/test/comment.test | ||
| @@ -1,346 +0,0 @@ | ||
| 1 | -# | |
| 2 | -# Copyright (c) 2014 D. Richard Hipp | |
| 3 | -# | |
| 4 | -# This program is free software; you can redistribute it and/or | |
| 5 | -# modify it under the terms of the Simplified BSD License (also | |
| 6 | -# known as the "2-Clause License" or "FreeBSD License".) | |
| 7 | -# | |
| 8 | -# This program is distributed in the hope that it will be useful, | |
| 9 | -# but without any warranty; without even the implied warranty of | |
| 10 | -# merchantability or fitness for a particular purpose. | |
| 11 | -# | |
| 12 | -# Author contact information: | |
| 13 | -# [email protected] | |
| 14 | -# http://www.hwaci.com/drh/ | |
| 15 | -# | |
| 16 | -############################################################################ | |
| 17 | -# | |
| 18 | -# Test comment formatting and printing. | |
| 19 | -# | |
| 20 | - | |
| 21 | -test_setup "" | |
| 22 | - | |
| 23 | -############################################################################### | |
| 24 | - | |
| 25 | -fossil test-comment-format "" "" | |
| 26 | -test comment-1 {$RESULT eq "\n(1 lines output)"} | |
| 27 | - | |
| 28 | -############################################################################### | |
| 29 | - | |
| 30 | -fossil test-comment-format --decode "" "" | |
| 31 | -test comment-2 {$RESULT eq "\n(1 lines output)"} | |
| 32 | - | |
| 33 | -############################################################################### | |
| 34 | - | |
| 35 | -fossil test-comment-format --width 26 " " "this is a short comment." | |
| 36 | -test comment-3 {$RESULT eq " this is a short comment.\n(1 lines output)"} | |
| 37 | - | |
| 38 | -############################################################################### | |
| 39 | - | |
| 40 | -fossil test-comment-format --width 26 --decode " " "this is a short comment." | |
| 41 | -test comment-4 {$RESULT eq " this is a short comment.\n(1 lines output)"} | |
| 42 | - | |
| 43 | -############################################################################### | |
| 44 | - | |
| 45 | -fossil test-comment-format --width 26 "*PREFIX* " "this is a short comment." | |
| 46 | -test comment-5 {$RESULT eq "*PREFIX* this is a short c\n omment.\n(2 lines output)"} | |
| 47 | - | |
| 48 | -############################################################################### | |
| 49 | - | |
| 50 | -fossil test-comment-format --width 26 --decode "*PREFIX* " "this is a short comment." | |
| 51 | -test comment-6 {$RESULT eq "*PREFIX* this is a short c\n omment.\n(2 lines output)"} | |
| 52 | - | |
| 53 | -############################################################################### | |
| 54 | - | |
| 55 | -fossil test-comment-format --width 26 "" "this\\sis\\sa\\sshort\\scomment." | |
| 56 | -test comment-7 {$RESULT eq "this\\sis\\sa\\sshort\\scommen\nt.\n(2 lines output)"} | |
| 57 | - | |
| 58 | -############################################################################### | |
| 59 | - | |
| 60 | -fossil test-comment-format --width 26 --decode "" "this\\sis\\sa\\sshort\\scomment." | |
| 61 | -test comment-8 {$RESULT eq "this is a short comment.\n(1 lines output)"} | |
| 62 | - | |
| 63 | -############################################################################### | |
| 64 | - | |
| 65 | -fossil test-comment-format --width 78 --decode --trimspace "HH:MM:SS " "this is a long comment that should span multiple lines if the test is working correctly." | |
| 66 | -test comment-9 {$RESULT eq "HH:MM:SS this is a long comment that should span multiple lines if the test is\n working correctly.\n(2 lines output)"} | |
| 67 | - | |
| 68 | -############################################################################### | |
| 69 | - | |
| 70 | -fossil test-comment-format --width 78 --decode --trimspace "HH:MM:SS " "this is a long comment that should span multiple lines if the test is working correctly. more text here describing the issue.\\nanother line here..................................................................................*" | |
| 71 | -test comment-10 {$RESULT eq "HH:MM:SS this is a long comment that should span multiple lines if the test is\n working correctly. more text here describing the issue.\n another line here....................................................\n ..............................*\n(4 lines output)"} | |
| 72 | - | |
| 73 | -############################################################################### | |
| 74 | - | |
| 75 | -fossil test-comment-format --width 78 "HH:MM:SS " "....................................................................................*" | |
| 76 | -test comment-11 {$RESULT eq "HH:MM:SS .....................................................................\n ...............*\n(2 lines output)"} | |
| 77 | - | |
| 78 | -############################################################################### | |
| 79 | - | |
| 80 | -fossil test-comment-format --width 78 "HH:MM:SS " ".....................................................................*" 78 | |
| 81 | -test comment-12 {$RESULT eq "HH:MM:SS .....................................................................\n *\n(2 lines output)"} | |
| 82 | - | |
| 83 | -############################################################################### | |
| 84 | - | |
| 85 | -fossil test-comment-format --width 26 "*TEST* " "this\tis a test." | |
| 86 | -test comment-13 {$RESULT eq "*TEST* this\tis a te\n st.\n(2 lines output)"} | |
| 87 | - | |
| 88 | -############################################################################### | |
| 89 | - | |
| 90 | -fossil test-comment-format --width 60 "*TEST* " "this is a test......................................................................................................................." | |
| 91 | -test comment-14 {$RESULT eq "*TEST* this is a test.......................................\n .....................................................\n ...........................\n(3 lines output)"} | |
| 92 | - | |
| 93 | -############################################################################### | |
| 94 | - | |
| 95 | -fossil test-comment-format --width 60 --wordbreak "*TEST* " "this is a test......................................................................................................................." | |
| 96 | -test comment-15 {$RESULT eq "*TEST* this is a\n test.................................................\n .....................................................\n .................\n(4 lines output)"} | |
| 97 | - | |
| 98 | -############################################################################### | |
| 99 | - | |
| 100 | -fossil test-comment-format --width 60 "*TEST* " "this is a test......................................................................................................................." | |
| 101 | -test comment-16 {$RESULT eq "*TEST* this is a\n test.................................................\n .....................................................\n .................\n(4 lines output)"} | |
| 102 | - | |
| 103 | -############################################################################### | |
| 104 | - | |
| 105 | -fossil test-comment-format --width 60 --wordbreak "*TEST* " "this is a test......................................................................................................................." | |
| 106 | -test comment-17 {$RESULT eq "*TEST* this is a\n test.................................................\n .....................................................\n .................\n(4 lines output)"} | |
| 107 | - | |
| 108 | -############################################################################### | |
| 109 | - | |
| 110 | -fossil test-comment-format --width 60 "*TEST* " "one two three four five six seven eight nine ten eleven twelve" | |
| 111 | -test comment-18 {$RESULT eq "*TEST* one two three four five six seven eight nine ten elev\n en twelve\n(2 lines output)"} | |
| 112 | - | |
| 113 | -############################################################################### | |
| 114 | - | |
| 115 | -fossil test-comment-format --width 60 --wordbreak "*TEST* " "one two three four five six seven eight nine ten eleven twelve" | |
| 116 | -test comment-19 {$RESULT eq "*TEST* one two three four five six seven eight nine ten\n eleven twelve\n(2 lines output)"} | |
| 117 | - | |
| 118 | -############################################################################### | |
| 119 | - | |
| 120 | -fossil test-comment-format --width 60 "*TEST* " "one two three four five six seven eight nine ten eleven twelve" | |
| 121 | -test comment-20 {$RESULT eq "*TEST* one two three four five\n six seven eight nine ten\n eleven twelve\n(3 lines output)"} | |
| 122 | - | |
| 123 | -############################################################################### | |
| 124 | - | |
| 125 | -fossil test-comment-format --width 60 --wordbreak "*TEST* " "one two three four five six seven eight nine ten eleven twelve" | |
| 126 | -test comment-21 {$RESULT eq "*TEST* one two three four five\n six seven eight nine ten\n eleven twelve\n(3 lines output)"} | |
| 127 | - | |
| 128 | -############################################################################### | |
| 129 | - | |
| 130 | -fossil test-comment-format --legacy "" "" | |
| 131 | -test comment-22 {$RESULT eq "\n(1 lines output)"} | |
| 132 | - | |
| 133 | -############################################################################### | |
| 134 | - | |
| 135 | -fossil test-comment-format --legacy --decode "" "" | |
| 136 | -test comment-23 {$RESULT eq "\n(1 lines output)"} | |
| 137 | - | |
| 138 | -############################################################################### | |
| 139 | - | |
| 140 | -fossil test-comment-format --width 26 --legacy " " "this is a short comment." | |
| 141 | -test comment-24 {$RESULT eq " this is a short comment.\n(1 lines output)"} | |
| 142 | - | |
| 143 | -############################################################################### | |
| 144 | - | |
| 145 | -fossil test-comment-format --width 26 --legacy --decode " " "this is a short comment." | |
| 146 | -test comment-25 {$RESULT eq " this is a short comment.\n(1 lines output)"} | |
| 147 | - | |
| 148 | -############################################################################### | |
| 149 | - | |
| 150 | -fossil test-comment-format --width 25 --legacy "*PREFIX* " "this is a short comment." | |
| 151 | -test comment-26 {$RESULT eq "*PREFIX* this is a short\n comment.\n(2 lines output)"} | |
| 152 | - | |
| 153 | -############################################################################### | |
| 154 | - | |
| 155 | -fossil test-comment-format --width 25 --legacy --decode "*PREFIX* " "this is a short comment." | |
| 156 | -test comment-27 {$RESULT eq "*PREFIX* this is a short\n comment.\n(2 lines output)"} | |
| 157 | - | |
| 158 | -############################################################################### | |
| 159 | - | |
| 160 | -fossil test-comment-format --width 26 --legacy "" "this\\sis\\sa\\sshort\\scomment." | |
| 161 | -test comment-28 {$RESULT eq "this\\sis\\sa\\sshort\\scommen\nt.\n(2 lines output)"} | |
| 162 | - | |
| 163 | -############################################################################### | |
| 164 | - | |
| 165 | -fossil test-comment-format --width 26 --legacy --decode "" "this\\sis\\sa\\sshort\\scomment." | |
| 166 | -test comment-29 {$RESULT eq "this is a short comment.\n(1 lines output)"} | |
| 167 | - | |
| 168 | -############################################################################### | |
| 169 | - | |
| 170 | -fossil test-comment-format --width 78 --legacy --decode "HH:MM:SS " "this is a long comment that should span multiple lines if the test is working correctly." | |
| 171 | -test comment-30 {$RESULT eq "HH:MM:SS this is a long comment that should span multiple lines if the test\n is working correctly.\n(2 lines output)"} | |
| 172 | - | |
| 173 | -############################################################################### | |
| 174 | - | |
| 175 | -fossil test-comment-format --width 78 --legacy --decode "HH:MM:SS " "this is a long comment that should span multiple lines if the test is working correctly. more text here describing the issue.\\nanother line here..................................................................................*" | |
| 176 | -test comment-31 {$RESULT eq "HH:MM:SS this is a long comment that should span multiple lines if the test\n is working correctly. more text here describing the issue. another\n line\n here.................................................................\n .................*\n(5 lines output)"} | |
| 177 | - | |
| 178 | -############################################################################### | |
| 179 | - | |
| 180 | -fossil test-comment-format --width 78 --legacy "HH:MM:SS " "....................................................................................*" | |
| 181 | -test comment-32 {$RESULT eq "HH:MM:SS .....................................................................\n ...............*\n(2 lines output)"} | |
| 182 | - | |
| 183 | -############################################################################### | |
| 184 | - | |
| 185 | -fossil test-comment-format --width 78 --legacy "HH:MM:SS " ".....................................................................*" | |
| 186 | -test comment-33 {$RESULT eq "HH:MM:SS .....................................................................\n *\n(2 lines output)"} | |
| 187 | - | |
| 188 | -############################################################################### | |
| 189 | - | |
| 190 | -fossil test-comment-format --width 26 --legacy "*TEST* " "this\tis a test." | |
| 191 | -test comment-34 {$RESULT eq "*TEST* this is a test.\n(1 lines output)"} | |
| 192 | - | |
| 193 | -############################################################################### | |
| 194 | - | |
| 195 | -fossil test-comment-format --width 60 --legacy "*TEST* " "this is a test......................................................................................................................." | |
| 196 | -test comment-35 {$RESULT eq "*TEST* this is a\n test.................................................\n .....................................................\n .................\n(4 lines output)"} | |
| 197 | - | |
| 198 | -############################################################################### | |
| 199 | - | |
| 200 | -fossil test-comment-format --width 60 --legacy --wordbreak "*TEST* " "this is a test......................................................................................................................." | |
| 201 | -test comment-36 {$RESULT eq "*TEST* this is a\n test.................................................\n .....................................................\n .................\n(4 lines output)"} | |
| 202 | - | |
| 203 | -############################################################################### | |
| 204 | - | |
| 205 | -fossil test-comment-format --width 60 --legacy "*TEST* " "this is a test......................................................................................................................." | |
| 206 | -test comment-37 {$RESULT eq "*TEST* this is a\n test.................................................\n .....................................................\n .................\n(4 lines output)"} | |
| 207 | - | |
| 208 | -############################################################################### | |
| 209 | - | |
| 210 | -fossil test-comment-format --width 60 --legacy --wordbreak "*TEST* " "this is a test......................................................................................................................." | |
| 211 | -test comment-38 {$RESULT eq "*TEST* this is a\n test.................................................\n .....................................................\n .................\n(4 lines output)"} | |
| 212 | - | |
| 213 | -############################################################################### | |
| 214 | - | |
| 215 | -fossil test-comment-format --width 60 --legacy "*TEST* " "one two three four five six seven eight nine ten eleven twelve" | |
| 216 | -test comment-39 {$RESULT eq "*TEST* one two three four five six seven eight nine ten\n eleven twelve\n(2 lines output)"} | |
| 217 | - | |
| 218 | -############################################################################### | |
| 219 | - | |
| 220 | -fossil test-comment-format --width 60 --legacy --wordbreak "*TEST* " "one two three four five six seven eight nine ten eleven twelve" | |
| 221 | -test comment-40 {$RESULT eq "*TEST* one two three four five six seven eight nine ten\n eleven twelve\n(2 lines output)"} | |
| 222 | - | |
| 223 | -############################################################################### | |
| 224 | - | |
| 225 | -fossil test-comment-format --width 60 --legacy "*TEST* " "one two three four five six seven eight nine ten eleven twelve" | |
| 226 | -test comment-41 {$RESULT eq "*TEST* one two three four five six seven eight nine ten\n eleven twelve\n(2 lines output)"} | |
| 227 | - | |
| 228 | -############################################################################### | |
| 229 | - | |
| 230 | -fossil test-comment-format --width 60 --legacy --wordbreak "*TEST* " "one two three four five six seven eight nine ten eleven twelve" | |
| 231 | -test comment-42 {$RESULT eq "*TEST* one two three four five six seven eight nine ten\n eleven twelve\n(2 lines output)"} | |
| 232 | - | |
| 233 | -############################################################################### | |
| 234 | - | |
| 235 | -set orig "xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\\nxxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\\nxxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\\nxxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\\nxxxxxxx." | |
| 236 | -fossil test-comment-format --width 73 --decode --origbreak "" $orig | |
| 237 | -test comment-43 {$RESULT eq "xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\nxxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\nxxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\nxxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\nxxxxxxx.\n(5 lines output)"} | |
| 238 | - | |
| 239 | -############################################################################### | |
| 240 | - | |
| 241 | -fossil test-comment-format --width 73 --decode --origbreak "" $orig $orig | |
| 242 | -test comment-44 {$RESULT eq "xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\nxxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\nxxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\nxxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\nxxxxxxx.\n(5 lines output)"} | |
| 243 | - | |
| 244 | -############################################################################### | |
| 245 | - | |
| 246 | -fossil test-comment-format --width 73 --decode --origbreak "" "00:00:00 \[0000000000\] *CURRENT* $orig" $orig | |
| 247 | -test comment-45 {$RESULT eq "00:00:00 \[0000000000\] *CURRENT* \nxxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\nxxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\nxxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\nxxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\nxxxxxxx.\n(6 lines output)"} | |
| 248 | - | |
| 249 | -############################################################################### | |
| 250 | - | |
| 251 | -fossil test-comment-format --width 82 --indent 9 --decode --origbreak " " $orig | |
| 252 | -test comment-46 {$RESULT eq " xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n xxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\n xxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\n xxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\n xxxxxxx.\n(5 lines output)"} | |
| 253 | - | |
| 254 | -############################################################################### | |
| 255 | - | |
| 256 | -fossil test-comment-format --width 82 --indent 9 --decode --origbreak " " $orig $orig | |
| 257 | -test comment-47 {$RESULT eq " xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n xxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\n xxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\n xxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\n xxxxxxx.\n(5 lines output)"} | |
| 258 | - | |
| 259 | -############################################################################### | |
| 260 | - | |
| 261 | -fossil test-comment-format --width 82 --indent 9 --decode --origbreak "00:00:00 " "\[0000000000\] *CURRENT* $orig" $orig | |
| 262 | -test comment-48 {$RESULT eq "00:00:00 \[0000000000\] *CURRENT* \n xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n xxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\n xxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\n xxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\n xxxxxxx.\n(6 lines output)"} | |
| 263 | - | |
| 264 | -############################################################################### | |
| 265 | - | |
| 266 | -fossil test-comment-format --width 72 --decode --trimspace --origbreak "" $orig | |
| 267 | -test comment-49 {$RESULT eq "xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\nxxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\nxxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\nxxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\nxxxxxxx.\n(5 lines output)"} | |
| 268 | - | |
| 269 | -############################################################################### | |
| 270 | - | |
| 271 | -fossil test-comment-format --width 72 --decode --trimspace --origbreak "" $orig $orig | |
| 272 | -test comment-50 {$RESULT eq "xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\nxxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\nxxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\nxxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\nxxxxxxx.\n(5 lines output)"} | |
| 273 | - | |
| 274 | -############################################################################### | |
| 275 | - | |
| 276 | -fossil test-comment-format --width 72 --decode --trimspace --origbreak "" "00:00:00 \[0000000000\] *CURRENT* $orig" $orig | |
| 277 | -test comment-51 {$RESULT eq "00:00:00 \[0000000000\] *CURRENT* \nxxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\nxxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\nxxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\nxxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\nxxxxxxx.\n(6 lines output)"} | |
| 278 | - | |
| 279 | -############################################################################### | |
| 280 | - | |
| 281 | -fossil test-comment-format --width 81 --indent 9 --decode --trimspace --origbreak " " $orig | |
| 282 | -test comment-52 {$RESULT eq " xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n xxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\n xxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\n xxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\n xxxxxxx.\n(5 lines output)"} | |
| 283 | - | |
| 284 | -############################################################################### | |
| 285 | - | |
| 286 | -fossil test-comment-format --width 81 --indent 9 --decode --trimspace --origbreak " " $orig $orig | |
| 287 | -test comment-53 {$RESULT eq " xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n xxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\n xxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\n xxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\n xxxxxxx.\n(5 lines output)"} | |
| 288 | - | |
| 289 | -############################################################################### | |
| 290 | - | |
| 291 | -fossil test-comment-format --width 81 --indent 9 --decode --trimspace --origbreak "00:00:00 " "\[0000000000\] *CURRENT* $orig" $orig | |
| 292 | -test comment-54 {$RESULT eq "00:00:00 \[0000000000\] *CURRENT* \n xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n xxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\n xxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\n xxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\n xxxxxxx.\n(6 lines output)"} | |
| 293 | - | |
| 294 | -############################################################################### | |
| 295 | - | |
| 296 | -fossil test-comment-format --width 72 --decode --trimcrlf --origbreak "" $orig | |
| 297 | -test comment-55 {$RESULT eq "xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\nxxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\nxxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\nxxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\nxxxxxxx.\n(5 lines output)"} | |
| 298 | - | |
| 299 | -############################################################################### | |
| 300 | - | |
| 301 | -fossil test-comment-format --width 72 --decode --trimcrlf --origbreak "" $orig $orig | |
| 302 | -test comment-56 {$RESULT eq "xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\nxxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\nxxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\nxxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\nxxxxxxx.\n(5 lines output)"} | |
| 303 | - | |
| 304 | -############################################################################### | |
| 305 | - | |
| 306 | -fossil test-comment-format --width 72 --decode --trimcrlf --origbreak "" "00:00:00 \[0000000000\] *CURRENT* $orig" $orig | |
| 307 | -test comment-57 {$RESULT eq "00:00:00 \[0000000000\] *CURRENT* \nxxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\nxxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\nxxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\nxxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\nxxxxxxx.\n(6 lines output)"} | |
| 308 | - | |
| 309 | -############################################################################### | |
| 310 | - | |
| 311 | -fossil test-comment-format --width 81 --indent 9 --decode --trimcrlf --origbreak " " $orig | |
| 312 | -test comment-58 {$RESULT eq " xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n xxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\n xxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\n xxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\n xxxxxxx.\n(5 lines output)"} | |
| 313 | - | |
| 314 | -############################################################################### | |
| 315 | - | |
| 316 | -fossil test-comment-format --width 81 --indent 9 --decode --trimcrlf --origbreak " " $orig $orig | |
| 317 | -test comment-59 {$RESULT eq " xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n xxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\n xxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\n xxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\n xxxxxxx.\n(5 lines output)"} | |
| 318 | - | |
| 319 | -############################################################################### | |
| 320 | - | |
| 321 | -fossil test-comment-format --width 81 --indent 9 --decode --trimcrlf --origbreak "00:00:00 " "\[0000000000\] *CURRENT* $orig" $orig | |
| 322 | -test comment-60 {$RESULT eq "00:00:00 \[0000000000\] *CURRENT* \n xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n xxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\n xxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\n xxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\n xxxxxxx.\n(6 lines output)"} | |
| 323 | - | |
| 324 | -############################################################################### | |
| 325 | - | |
| 326 | -fossil test-comment-format --width 72 --file "" [file join $testdir "utf8-comment.txt"] | |
| 327 | -test comment-61 {$RESULT eq "The comment formatter handles fullwidth and multi-byte \[äöü\] an\nd symbols \[☃\] and emoji \[💾\] characters!\n(2 lines output)"} | |
| 328 | - | |
| 329 | -############################################################################### | |
| 330 | - | |
| 331 | -fossil test-comment-format --width 72 --wordbreak --file "" [file join $testdir "utf8-comment.txt"] | |
| 332 | -test comment-62 {$RESULT eq "The comment formatter handles fullwidth and multi-byte \[äöü\]\nand symbols \[☃\] and emoji \[💾\] characters!\n(2 lines output)"} | |
| 333 | - | |
| 334 | -############################################################################### | |
| 335 | - | |
| 336 | -fossil test-comment-format --width 72 --legacy --file "" [file join $testdir "utf8-comment.txt"] | |
| 337 | -test comment-63 {$RESULT eq "The comment formatter handles fullwidth and multi-byte \[äöü\]\nand symbols \[☃\] and emoji \[💾\] characters!\n(2 lines output)"} | |
| 338 | - | |
| 339 | -############################################################################### | |
| 340 | - | |
| 341 | -fossil test-comment-format --width 72 --legacy --wordbreak --file "" [file join $testdir "utf8-comment.txt"] | |
| 342 | -test comment-64 {$RESULT eq "The comment formatter handles fullwidth and multi-byte \[äöü\]\nand symbols \[☃\] and emoji \[💾\] characters!\n(2 lines output)"} | |
| 343 | - | |
| 344 | -############################################################################### | |
| 345 | - | |
| 346 | -test_cleanup |
| --- a/test/comment.test | |
| +++ b/test/comment.test | |
| @@ -1,346 +0,0 @@ | |
| 1 | # |
| 2 | # Copyright (c) 2014 D. Richard Hipp |
| 3 | # |
| 4 | # This program is free software; you can redistribute it and/or |
| 5 | # modify it under the terms of the Simplified BSD License (also |
| 6 | # known as the "2-Clause License" or "FreeBSD License".) |
| 7 | # |
| 8 | # This program is distributed in the hope that it will be useful, |
| 9 | # but without any warranty; without even the implied warranty of |
| 10 | # merchantability or fitness for a particular purpose. |
| 11 | # |
| 12 | # Author contact information: |
| 13 | # [email protected] |
| 14 | # http://www.hwaci.com/drh/ |
| 15 | # |
| 16 | ############################################################################ |
| 17 | # |
| 18 | # Test comment formatting and printing. |
| 19 | # |
| 20 | |
| 21 | test_setup "" |
| 22 | |
| 23 | ############################################################################### |
| 24 | |
| 25 | fossil test-comment-format "" "" |
| 26 | test comment-1 {$RESULT eq "\n(1 lines output)"} |
| 27 | |
| 28 | ############################################################################### |
| 29 | |
| 30 | fossil test-comment-format --decode "" "" |
| 31 | test comment-2 {$RESULT eq "\n(1 lines output)"} |
| 32 | |
| 33 | ############################################################################### |
| 34 | |
| 35 | fossil test-comment-format --width 26 " " "this is a short comment." |
| 36 | test comment-3 {$RESULT eq " this is a short comment.\n(1 lines output)"} |
| 37 | |
| 38 | ############################################################################### |
| 39 | |
| 40 | fossil test-comment-format --width 26 --decode " " "this is a short comment." |
| 41 | test comment-4 {$RESULT eq " this is a short comment.\n(1 lines output)"} |
| 42 | |
| 43 | ############################################################################### |
| 44 | |
| 45 | fossil test-comment-format --width 26 "*PREFIX* " "this is a short comment." |
| 46 | test comment-5 {$RESULT eq "*PREFIX* this is a short c\n omment.\n(2 lines output)"} |
| 47 | |
| 48 | ############################################################################### |
| 49 | |
| 50 | fossil test-comment-format --width 26 --decode "*PREFIX* " "this is a short comment." |
| 51 | test comment-6 {$RESULT eq "*PREFIX* this is a short c\n omment.\n(2 lines output)"} |
| 52 | |
| 53 | ############################################################################### |
| 54 | |
| 55 | fossil test-comment-format --width 26 "" "this\\sis\\sa\\sshort\\scomment." |
| 56 | test comment-7 {$RESULT eq "this\\sis\\sa\\sshort\\scommen\nt.\n(2 lines output)"} |
| 57 | |
| 58 | ############################################################################### |
| 59 | |
| 60 | fossil test-comment-format --width 26 --decode "" "this\\sis\\sa\\sshort\\scomment." |
| 61 | test comment-8 {$RESULT eq "this is a short comment.\n(1 lines output)"} |
| 62 | |
| 63 | ############################################################################### |
| 64 | |
| 65 | fossil test-comment-format --width 78 --decode --trimspace "HH:MM:SS " "this is a long comment that should span multiple lines if the test is working correctly." |
| 66 | test comment-9 {$RESULT eq "HH:MM:SS this is a long comment that should span multiple lines if the test is\n working correctly.\n(2 lines output)"} |
| 67 | |
| 68 | ############################################################################### |
| 69 | |
| 70 | fossil test-comment-format --width 78 --decode --trimspace "HH:MM:SS " "this is a long comment that should span multiple lines if the test is working correctly. more text here describing the issue.\\nanother line here..................................................................................*" |
| 71 | test comment-10 {$RESULT eq "HH:MM:SS this is a long comment that should span multiple lines if the test is\n working correctly. more text here describing the issue.\n another line here....................................................\n ..............................*\n(4 lines output)"} |
| 72 | |
| 73 | ############################################################################### |
| 74 | |
| 75 | fossil test-comment-format --width 78 "HH:MM:SS " "....................................................................................*" |
| 76 | test comment-11 {$RESULT eq "HH:MM:SS .....................................................................\n ...............*\n(2 lines output)"} |
| 77 | |
| 78 | ############################################################################### |
| 79 | |
| 80 | fossil test-comment-format --width 78 "HH:MM:SS " ".....................................................................*" 78 |
| 81 | test comment-12 {$RESULT eq "HH:MM:SS .....................................................................\n *\n(2 lines output)"} |
| 82 | |
| 83 | ############################################################################### |
| 84 | |
| 85 | fossil test-comment-format --width 26 "*TEST* " "this\tis a test." |
| 86 | test comment-13 {$RESULT eq "*TEST* this\tis a te\n st.\n(2 lines output)"} |
| 87 | |
| 88 | ############################################################################### |
| 89 | |
| 90 | fossil test-comment-format --width 60 "*TEST* " "this is a test......................................................................................................................." |
| 91 | test comment-14 {$RESULT eq "*TEST* this is a test.......................................\n .....................................................\n ...........................\n(3 lines output)"} |
| 92 | |
| 93 | ############################################################################### |
| 94 | |
| 95 | fossil test-comment-format --width 60 --wordbreak "*TEST* " "this is a test......................................................................................................................." |
| 96 | test comment-15 {$RESULT eq "*TEST* this is a\n test.................................................\n .....................................................\n .................\n(4 lines output)"} |
| 97 | |
| 98 | ############################################################################### |
| 99 | |
| 100 | fossil test-comment-format --width 60 "*TEST* " "this is a test......................................................................................................................." |
| 101 | test comment-16 {$RESULT eq "*TEST* this is a\n test.................................................\n .....................................................\n .................\n(4 lines output)"} |
| 102 | |
| 103 | ############################################################################### |
| 104 | |
| 105 | fossil test-comment-format --width 60 --wordbreak "*TEST* " "this is a test......................................................................................................................." |
| 106 | test comment-17 {$RESULT eq "*TEST* this is a\n test.................................................\n .....................................................\n .................\n(4 lines output)"} |
| 107 | |
| 108 | ############################################################################### |
| 109 | |
| 110 | fossil test-comment-format --width 60 "*TEST* " "one two three four five six seven eight nine ten eleven twelve" |
| 111 | test comment-18 {$RESULT eq "*TEST* one two three four five six seven eight nine ten elev\n en twelve\n(2 lines output)"} |
| 112 | |
| 113 | ############################################################################### |
| 114 | |
| 115 | fossil test-comment-format --width 60 --wordbreak "*TEST* " "one two three four five six seven eight nine ten eleven twelve" |
| 116 | test comment-19 {$RESULT eq "*TEST* one two three four five six seven eight nine ten\n eleven twelve\n(2 lines output)"} |
| 117 | |
| 118 | ############################################################################### |
| 119 | |
| 120 | fossil test-comment-format --width 60 "*TEST* " "one two three four five six seven eight nine ten eleven twelve" |
| 121 | test comment-20 {$RESULT eq "*TEST* one two three four five\n six seven eight nine ten\n eleven twelve\n(3 lines output)"} |
| 122 | |
| 123 | ############################################################################### |
| 124 | |
| 125 | fossil test-comment-format --width 60 --wordbreak "*TEST* " "one two three four five six seven eight nine ten eleven twelve" |
| 126 | test comment-21 {$RESULT eq "*TEST* one two three four five\n six seven eight nine ten\n eleven twelve\n(3 lines output)"} |
| 127 | |
| 128 | ############################################################################### |
| 129 | |
| 130 | fossil test-comment-format --legacy "" "" |
| 131 | test comment-22 {$RESULT eq "\n(1 lines output)"} |
| 132 | |
| 133 | ############################################################################### |
| 134 | |
| 135 | fossil test-comment-format --legacy --decode "" "" |
| 136 | test comment-23 {$RESULT eq "\n(1 lines output)"} |
| 137 | |
| 138 | ############################################################################### |
| 139 | |
| 140 | fossil test-comment-format --width 26 --legacy " " "this is a short comment." |
| 141 | test comment-24 {$RESULT eq " this is a short comment.\n(1 lines output)"} |
| 142 | |
| 143 | ############################################################################### |
| 144 | |
| 145 | fossil test-comment-format --width 26 --legacy --decode " " "this is a short comment." |
| 146 | test comment-25 {$RESULT eq " this is a short comment.\n(1 lines output)"} |
| 147 | |
| 148 | ############################################################################### |
| 149 | |
| 150 | fossil test-comment-format --width 25 --legacy "*PREFIX* " "this is a short comment." |
| 151 | test comment-26 {$RESULT eq "*PREFIX* this is a short\n comment.\n(2 lines output)"} |
| 152 | |
| 153 | ############################################################################### |
| 154 | |
| 155 | fossil test-comment-format --width 25 --legacy --decode "*PREFIX* " "this is a short comment." |
| 156 | test comment-27 {$RESULT eq "*PREFIX* this is a short\n comment.\n(2 lines output)"} |
| 157 | |
| 158 | ############################################################################### |
| 159 | |
| 160 | fossil test-comment-format --width 26 --legacy "" "this\\sis\\sa\\sshort\\scomment." |
| 161 | test comment-28 {$RESULT eq "this\\sis\\sa\\sshort\\scommen\nt.\n(2 lines output)"} |
| 162 | |
| 163 | ############################################################################### |
| 164 | |
| 165 | fossil test-comment-format --width 26 --legacy --decode "" "this\\sis\\sa\\sshort\\scomment." |
| 166 | test comment-29 {$RESULT eq "this is a short comment.\n(1 lines output)"} |
| 167 | |
| 168 | ############################################################################### |
| 169 | |
| 170 | fossil test-comment-format --width 78 --legacy --decode "HH:MM:SS " "this is a long comment that should span multiple lines if the test is working correctly." |
| 171 | test comment-30 {$RESULT eq "HH:MM:SS this is a long comment that should span multiple lines if the test\n is working correctly.\n(2 lines output)"} |
| 172 | |
| 173 | ############################################################################### |
| 174 | |
| 175 | fossil test-comment-format --width 78 --legacy --decode "HH:MM:SS " "this is a long comment that should span multiple lines if the test is working correctly. more text here describing the issue.\\nanother line here..................................................................................*" |
| 176 | test comment-31 {$RESULT eq "HH:MM:SS this is a long comment that should span multiple lines if the test\n is working correctly. more text here describing the issue. another\n line\n here.................................................................\n .................*\n(5 lines output)"} |
| 177 | |
| 178 | ############################################################################### |
| 179 | |
| 180 | fossil test-comment-format --width 78 --legacy "HH:MM:SS " "....................................................................................*" |
| 181 | test comment-32 {$RESULT eq "HH:MM:SS .....................................................................\n ...............*\n(2 lines output)"} |
| 182 | |
| 183 | ############################################################################### |
| 184 | |
| 185 | fossil test-comment-format --width 78 --legacy "HH:MM:SS " ".....................................................................*" |
| 186 | test comment-33 {$RESULT eq "HH:MM:SS .....................................................................\n *\n(2 lines output)"} |
| 187 | |
| 188 | ############################################################################### |
| 189 | |
| 190 | fossil test-comment-format --width 26 --legacy "*TEST* " "this\tis a test." |
| 191 | test comment-34 {$RESULT eq "*TEST* this is a test.\n(1 lines output)"} |
| 192 | |
| 193 | ############################################################################### |
| 194 | |
| 195 | fossil test-comment-format --width 60 --legacy "*TEST* " "this is a test......................................................................................................................." |
| 196 | test comment-35 {$RESULT eq "*TEST* this is a\n test.................................................\n .....................................................\n .................\n(4 lines output)"} |
| 197 | |
| 198 | ############################################################################### |
| 199 | |
| 200 | fossil test-comment-format --width 60 --legacy --wordbreak "*TEST* " "this is a test......................................................................................................................." |
| 201 | test comment-36 {$RESULT eq "*TEST* this is a\n test.................................................\n .....................................................\n .................\n(4 lines output)"} |
| 202 | |
| 203 | ############################################################################### |
| 204 | |
| 205 | fossil test-comment-format --width 60 --legacy "*TEST* " "this is a test......................................................................................................................." |
| 206 | test comment-37 {$RESULT eq "*TEST* this is a\n test.................................................\n .....................................................\n .................\n(4 lines output)"} |
| 207 | |
| 208 | ############################################################################### |
| 209 | |
| 210 | fossil test-comment-format --width 60 --legacy --wordbreak "*TEST* " "this is a test......................................................................................................................." |
| 211 | test comment-38 {$RESULT eq "*TEST* this is a\n test.................................................\n .....................................................\n .................\n(4 lines output)"} |
| 212 | |
| 213 | ############################################################################### |
| 214 | |
| 215 | fossil test-comment-format --width 60 --legacy "*TEST* " "one two three four five six seven eight nine ten eleven twelve" |
| 216 | test comment-39 {$RESULT eq "*TEST* one two three four five six seven eight nine ten\n eleven twelve\n(2 lines output)"} |
| 217 | |
| 218 | ############################################################################### |
| 219 | |
| 220 | fossil test-comment-format --width 60 --legacy --wordbreak "*TEST* " "one two three four five six seven eight nine ten eleven twelve" |
| 221 | test comment-40 {$RESULT eq "*TEST* one two three four five six seven eight nine ten\n eleven twelve\n(2 lines output)"} |
| 222 | |
| 223 | ############################################################################### |
| 224 | |
| 225 | fossil test-comment-format --width 60 --legacy "*TEST* " "one two three four five six seven eight nine ten eleven twelve" |
| 226 | test comment-41 {$RESULT eq "*TEST* one two three four five six seven eight nine ten\n eleven twelve\n(2 lines output)"} |
| 227 | |
| 228 | ############################################################################### |
| 229 | |
| 230 | fossil test-comment-format --width 60 --legacy --wordbreak "*TEST* " "one two three four five six seven eight nine ten eleven twelve" |
| 231 | test comment-42 {$RESULT eq "*TEST* one two three four five six seven eight nine ten\n eleven twelve\n(2 lines output)"} |
| 232 | |
| 233 | ############################################################################### |
| 234 | |
| 235 | set orig "xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\\nxxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\\nxxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\\nxxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\\nxxxxxxx." |
| 236 | fossil test-comment-format --width 73 --decode --origbreak "" $orig |
| 237 | test comment-43 {$RESULT eq "xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\nxxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\nxxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\nxxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\nxxxxxxx.\n(5 lines output)"} |
| 238 | |
| 239 | ############################################################################### |
| 240 | |
| 241 | fossil test-comment-format --width 73 --decode --origbreak "" $orig $orig |
| 242 | test comment-44 {$RESULT eq "xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\nxxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\nxxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\nxxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\nxxxxxxx.\n(5 lines output)"} |
| 243 | |
| 244 | ############################################################################### |
| 245 | |
| 246 | fossil test-comment-format --width 73 --decode --origbreak "" "00:00:00 \[0000000000\] *CURRENT* $orig" $orig |
| 247 | test comment-45 {$RESULT eq "00:00:00 \[0000000000\] *CURRENT* \nxxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\nxxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\nxxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\nxxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\nxxxxxxx.\n(6 lines output)"} |
| 248 | |
| 249 | ############################################################################### |
| 250 | |
| 251 | fossil test-comment-format --width 82 --indent 9 --decode --origbreak " " $orig |
| 252 | test comment-46 {$RESULT eq " xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n xxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\n xxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\n xxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\n xxxxxxx.\n(5 lines output)"} |
| 253 | |
| 254 | ############################################################################### |
| 255 | |
| 256 | fossil test-comment-format --width 82 --indent 9 --decode --origbreak " " $orig $orig |
| 257 | test comment-47 {$RESULT eq " xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n xxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\n xxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\n xxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\n xxxxxxx.\n(5 lines output)"} |
| 258 | |
| 259 | ############################################################################### |
| 260 | |
| 261 | fossil test-comment-format --width 82 --indent 9 --decode --origbreak "00:00:00 " "\[0000000000\] *CURRENT* $orig" $orig |
| 262 | test comment-48 {$RESULT eq "00:00:00 \[0000000000\] *CURRENT* \n xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n xxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\n xxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\n xxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\n xxxxxxx.\n(6 lines output)"} |
| 263 | |
| 264 | ############################################################################### |
| 265 | |
| 266 | fossil test-comment-format --width 72 --decode --trimspace --origbreak "" $orig |
| 267 | test comment-49 {$RESULT eq "xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\nxxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\nxxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\nxxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\nxxxxxxx.\n(5 lines output)"} |
| 268 | |
| 269 | ############################################################################### |
| 270 | |
| 271 | fossil test-comment-format --width 72 --decode --trimspace --origbreak "" $orig $orig |
| 272 | test comment-50 {$RESULT eq "xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\nxxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\nxxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\nxxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\nxxxxxxx.\n(5 lines output)"} |
| 273 | |
| 274 | ############################################################################### |
| 275 | |
| 276 | fossil test-comment-format --width 72 --decode --trimspace --origbreak "" "00:00:00 \[0000000000\] *CURRENT* $orig" $orig |
| 277 | test comment-51 {$RESULT eq "00:00:00 \[0000000000\] *CURRENT* \nxxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\nxxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\nxxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\nxxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\nxxxxxxx.\n(6 lines output)"} |
| 278 | |
| 279 | ############################################################################### |
| 280 | |
| 281 | fossil test-comment-format --width 81 --indent 9 --decode --trimspace --origbreak " " $orig |
| 282 | test comment-52 {$RESULT eq " xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n xxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\n xxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\n xxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\n xxxxxxx.\n(5 lines output)"} |
| 283 | |
| 284 | ############################################################################### |
| 285 | |
| 286 | fossil test-comment-format --width 81 --indent 9 --decode --trimspace --origbreak " " $orig $orig |
| 287 | test comment-53 {$RESULT eq " xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n xxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\n xxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\n xxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\n xxxxxxx.\n(5 lines output)"} |
| 288 | |
| 289 | ############################################################################### |
| 290 | |
| 291 | fossil test-comment-format --width 81 --indent 9 --decode --trimspace --origbreak "00:00:00 " "\[0000000000\] *CURRENT* $orig" $orig |
| 292 | test comment-54 {$RESULT eq "00:00:00 \[0000000000\] *CURRENT* \n xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n xxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\n xxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\n xxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\n xxxxxxx.\n(6 lines output)"} |
| 293 | |
| 294 | ############################################################################### |
| 295 | |
| 296 | fossil test-comment-format --width 72 --decode --trimcrlf --origbreak "" $orig |
| 297 | test comment-55 {$RESULT eq "xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\nxxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\nxxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\nxxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\nxxxxxxx.\n(5 lines output)"} |
| 298 | |
| 299 | ############################################################################### |
| 300 | |
| 301 | fossil test-comment-format --width 72 --decode --trimcrlf --origbreak "" $orig $orig |
| 302 | test comment-56 {$RESULT eq "xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\nxxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\nxxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\nxxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\nxxxxxxx.\n(5 lines output)"} |
| 303 | |
| 304 | ############################################################################### |
| 305 | |
| 306 | fossil test-comment-format --width 72 --decode --trimcrlf --origbreak "" "00:00:00 \[0000000000\] *CURRENT* $orig" $orig |
| 307 | test comment-57 {$RESULT eq "00:00:00 \[0000000000\] *CURRENT* \nxxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\nxxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\nxxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\nxxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\nxxxxxxx.\n(6 lines output)"} |
| 308 | |
| 309 | ############################################################################### |
| 310 | |
| 311 | fossil test-comment-format --width 81 --indent 9 --decode --trimcrlf --origbreak " " $orig |
| 312 | test comment-58 {$RESULT eq " xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n xxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\n xxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\n xxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\n xxxxxxx.\n(5 lines output)"} |
| 313 | |
| 314 | ############################################################################### |
| 315 | |
| 316 | fossil test-comment-format --width 81 --indent 9 --decode --trimcrlf --origbreak " " $orig $orig |
| 317 | test comment-59 {$RESULT eq " xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n xxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\n xxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\n xxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\n xxxxxxx.\n(5 lines output)"} |
| 318 | |
| 319 | ############################################################################### |
| 320 | |
| 321 | fossil test-comment-format --width 81 --indent 9 --decode --trimcrlf --origbreak "00:00:00 " "\[0000000000\] *CURRENT* $orig" $orig |
| 322 | test comment-60 {$RESULT eq "00:00:00 \[0000000000\] *CURRENT* \n xxxx xx xxxxxxx xxxx xxxxxx xxxxxxx, xxxxxxx, x xxxx xxxxxx xx xxxx xxxx\n xxxxxxx xxxxx xxxx xxxx xx xxxxxxx xxxxxxx (xxxxxx xxxxxxxxx x xxxxx).\n xxx'x xxx xxx xx xxxxx xxxx xxx xxx --xxxxxxxxxxx xxxxxx xx xx xxxx. x\n xxxxx x xxxxxx xxxx xxxx xxxx xxxx xxxx x xxxxx xx xxx x xxxxxxxx\n xxxxxxx.\n(6 lines output)"} |
| 323 | |
| 324 | ############################################################################### |
| 325 | |
| 326 | fossil test-comment-format --width 72 --file "" [file join $testdir "utf8-comment.txt"] |
| 327 | test comment-61 {$RESULT eq "The comment formatter handles fullwidth and multi-byte \[äöü\] an\nd symbols \[☃\] and emoji \[💾\] characters!\n(2 lines output)"} |
| 328 | |
| 329 | ############################################################################### |
| 330 | |
| 331 | fossil test-comment-format --width 72 --wordbreak --file "" [file join $testdir "utf8-comment.txt"] |
| 332 | test comment-62 {$RESULT eq "The comment formatter handles fullwidth and multi-byte \[äöü\]\nand symbols \[☃\] and emoji \[💾\] characters!\n(2 lines output)"} |
| 333 | |
| 334 | ############################################################################### |
| 335 | |
| 336 | fossil test-comment-format --width 72 --legacy --file "" [file join $testdir "utf8-comment.txt"] |
| 337 | test comment-63 {$RESULT eq "The comment formatter handles fullwidth and multi-byte \[äöü\]\nand symbols \[☃\] and emoji \[💾\] characters!\n(2 lines output)"} |
| 338 | |
| 339 | ############################################################################### |
| 340 | |
| 341 | fossil test-comment-format --width 72 --legacy --wordbreak --file "" [file join $testdir "utf8-comment.txt"] |
| 342 | test comment-64 {$RESULT eq "The comment formatter handles fullwidth and multi-byte \[äöü\]\nand symbols \[☃\] and emoji \[💾\] characters!\n(2 lines output)"} |
| 343 | |
| 344 | ############################################################################### |
| 345 | |
| 346 | test_cleanup |
| --- a/test/comment.test | |
| +++ b/test/comment.test | |
| @@ -1,346 +0,0 @@ | |
+4
-3
| --- tools/makemake.tcl | ||
| +++ tools/makemake.tcl | ||
| @@ -567,18 +567,19 @@ | ||
| 567 | 567 | $(XTCC) $(PIKCHR_OPTIONS) -c $(SRCDIR_extsrc)/pikchr.c -o $@ |
| 568 | 568 | |
| 569 | 569 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c |
| 570 | 570 | $(XTCC) -c $(SRCDIR_extsrc)/cson_amalgamation.c -o $@ |
| 571 | 571 | |
| 572 | -$(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c | |
| 572 | +$(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c $(MAKEFILE_LIST) | |
| 573 | 573 | $(EMCC_WRAPPER) -o $@ $(EMCC_OPT) --no-entry <<<NEXT_LINE>>> |
| 574 | - -sEXPORTED_RUNTIME_METHODS=cwrap,setValue,getValue,stackSave,stackRestore <<<NEXT_LINE>>> | |
| 575 | - -sEXPORTED_FUNCTIONS=_pikchr $(SRCDIR_extsrc)/pikchr.c <<<NEXT_LINE>>> | |
| 574 | + -sEXPORTED_RUNTIME_METHODS=cwrap,ccall,setValue,getValue,stackSave,stackAlloc,stackRestore <<<NEXT_LINE>>> | |
| 575 | + -sEXPORTED_FUNCTIONS=_pikchr,_pikchr_version $(SRCDIR_extsrc)/pikchr.c <<<NEXT_LINE>>> | |
| 576 | 576 | -sENVIRONMENT=web <<<NEXT_LINE>>> |
| 577 | 577 | -sMODULARIZE <<<NEXT_LINE>>> |
| 578 | 578 | -sEXPORT_NAME=initPikchrModule <<<NEXT_LINE>>> |
| 579 | 579 | --minify 0 |
| 580 | + $(TCLSH) $(TOPDIR)/tools/randomize-js-names.tcl $(SRCDIR_extsrc) | |
| 580 | 581 | @chmod -x $(SRCDIR_extsrc)/pikchr.wasm |
| 581 | 582 | wasm: $(SRCDIR_extsrc)/pikchr.js |
| 582 | 583 | |
| 583 | 584 | # |
| 584 | 585 | # compile_commands.json support... |
| 585 | 586 |
| --- tools/makemake.tcl | |
| +++ tools/makemake.tcl | |
| @@ -567,18 +567,19 @@ | |
| 567 | $(XTCC) $(PIKCHR_OPTIONS) -c $(SRCDIR_extsrc)/pikchr.c -o $@ |
| 568 | |
| 569 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c |
| 570 | $(XTCC) -c $(SRCDIR_extsrc)/cson_amalgamation.c -o $@ |
| 571 | |
| 572 | $(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c |
| 573 | $(EMCC_WRAPPER) -o $@ $(EMCC_OPT) --no-entry <<<NEXT_LINE>>> |
| 574 | -sEXPORTED_RUNTIME_METHODS=cwrap,setValue,getValue,stackSave,stackRestore <<<NEXT_LINE>>> |
| 575 | -sEXPORTED_FUNCTIONS=_pikchr $(SRCDIR_extsrc)/pikchr.c <<<NEXT_LINE>>> |
| 576 | -sENVIRONMENT=web <<<NEXT_LINE>>> |
| 577 | -sMODULARIZE <<<NEXT_LINE>>> |
| 578 | -sEXPORT_NAME=initPikchrModule <<<NEXT_LINE>>> |
| 579 | --minify 0 |
| 580 | @chmod -x $(SRCDIR_extsrc)/pikchr.wasm |
| 581 | wasm: $(SRCDIR_extsrc)/pikchr.js |
| 582 | |
| 583 | # |
| 584 | # compile_commands.json support... |
| 585 |
| --- tools/makemake.tcl | |
| +++ tools/makemake.tcl | |
| @@ -567,18 +567,19 @@ | |
| 567 | $(XTCC) $(PIKCHR_OPTIONS) -c $(SRCDIR_extsrc)/pikchr.c -o $@ |
| 568 | |
| 569 | $(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c |
| 570 | $(XTCC) -c $(SRCDIR_extsrc)/cson_amalgamation.c -o $@ |
| 571 | |
| 572 | $(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c $(MAKEFILE_LIST) |
| 573 | $(EMCC_WRAPPER) -o $@ $(EMCC_OPT) --no-entry <<<NEXT_LINE>>> |
| 574 | -sEXPORTED_RUNTIME_METHODS=cwrap,ccall,setValue,getValue,stackSave,stackAlloc,stackRestore <<<NEXT_LINE>>> |
| 575 | -sEXPORTED_FUNCTIONS=_pikchr,_pikchr_version $(SRCDIR_extsrc)/pikchr.c <<<NEXT_LINE>>> |
| 576 | -sENVIRONMENT=web <<<NEXT_LINE>>> |
| 577 | -sMODULARIZE <<<NEXT_LINE>>> |
| 578 | -sEXPORT_NAME=initPikchrModule <<<NEXT_LINE>>> |
| 579 | --minify 0 |
| 580 | $(TCLSH) $(TOPDIR)/tools/randomize-js-names.tcl $(SRCDIR_extsrc) |
| 581 | @chmod -x $(SRCDIR_extsrc)/pikchr.wasm |
| 582 | wasm: $(SRCDIR_extsrc)/pikchr.js |
| 583 | |
| 584 | # |
| 585 | # compile_commands.json support... |
| 586 |
+19
-15
| --- tools/mkindex.c | ||
| +++ tools/mkindex.c | ||
| @@ -85,25 +85,26 @@ | ||
| 85 | 85 | |
| 86 | 86 | /*************************************************************************** |
| 87 | 87 | ** These macros must match similar macros in dispatch.c. |
| 88 | 88 | ** |
| 89 | 89 | ** Allowed values for CmdOrPage.eCmdFlags. */ |
| 90 | -#define CMDFLAG_1ST_TIER 0x0001 /* Most important commands */ | |
| 91 | -#define CMDFLAG_2ND_TIER 0x0002 /* Obscure and seldom used commands */ | |
| 92 | -#define CMDFLAG_TEST 0x0004 /* Commands for testing only */ | |
| 93 | -#define CMDFLAG_WEBPAGE 0x0008 /* Web pages */ | |
| 94 | -#define CMDFLAG_COMMAND 0x0010 /* A command */ | |
| 95 | -#define CMDFLAG_SETTING 0x0020 /* A setting */ | |
| 96 | -#define CMDFLAG_VERSIONABLE 0x0040 /* A versionable setting */ | |
| 97 | -#define CMDFLAG_BLOCKTEXT 0x0080 /* Multi-line text setting */ | |
| 98 | -#define CMDFLAG_BOOLEAN 0x0100 /* A boolean setting */ | |
| 99 | -#define CMDFLAG_RAWCONTENT 0x0200 /* Do not interpret webpage content */ | |
| 100 | -#define CMDFLAG_SENSITIVE 0x0400 /* Security-sensitive setting */ | |
| 101 | -#define CMDFLAG_HIDDEN 0x0800 /* Elide from most listings */ | |
| 102 | -#define CMDFLAG_LDAVG_EXEMPT 0x1000 /* Exempt from load_control() */ | |
| 103 | -#define CMDFLAG_ALIAS 0x2000 /* Command aliases */ | |
| 104 | -#define CMDFLAG_KEEPEMPTY 0x4000 /* Do not unset empty settings */ | |
| 90 | +#define CMDFLAG_1ST_TIER 0x00001 /* Most important commands */ | |
| 91 | +#define CMDFLAG_2ND_TIER 0x00002 /* Obscure and seldom used commands */ | |
| 92 | +#define CMDFLAG_TEST 0x00004 /* Commands for testing only */ | |
| 93 | +#define CMDFLAG_WEBPAGE 0x00008 /* Web pages */ | |
| 94 | +#define CMDFLAG_COMMAND 0x00010 /* A command */ | |
| 95 | +#define CMDFLAG_SETTING 0x00020 /* A setting */ | |
| 96 | +#define CMDFLAG_VERSIONABLE 0x00040 /* A versionable setting */ | |
| 97 | +#define CMDFLAG_BLOCKTEXT 0x00080 /* Multi-line text setting */ | |
| 98 | +#define CMDFLAG_BOOLEAN 0x00100 /* A boolean setting */ | |
| 99 | +#define CMDFLAG_RAWCONTENT 0x00200 /* Do not interpret webpage content */ | |
| 100 | +#define CMDFLAG_SENSITIVE 0x00400 /* Security-sensitive setting */ | |
| 101 | +#define CMDFLAG_HIDDEN 0x00800 /* Elide from most listings */ | |
| 102 | +#define CMDFLAG_LDAVG_EXEMPT 0x01000 /* Exempt from load_control() */ | |
| 103 | +#define CMDFLAG_ALIAS 0x02000 /* Command aliases */ | |
| 104 | +#define CMDFLAG_KEEPEMPTY 0x04000 /* Do not unset empty settings */ | |
| 105 | +#define CMDFLAG_ABBREVSUBCMD 0x08000 /* Abbreviated subcmd in help text */ | |
| 105 | 106 | /**************************************************************************/ |
| 106 | 107 | |
| 107 | 108 | /* |
| 108 | 109 | ** Each entry looks like this: |
| 109 | 110 | */ |
| @@ -280,10 +281,13 @@ | ||
| 280 | 281 | aEntry[nUsed].zVar = string_dup(&zLine[i+9], j-9); |
| 281 | 282 | }else if( j==6 && strncmp(&zLine[i], "hidden", 6)==0 ){ |
| 282 | 283 | aEntry[nUsed].eType |= CMDFLAG_HIDDEN; |
| 283 | 284 | }else if( j==14 && strncmp(&zLine[i], "loadavg-exempt", 14)==0 ){ |
| 284 | 285 | aEntry[nUsed].eType |= CMDFLAG_LDAVG_EXEMPT; |
| 286 | + }else if( (j==23 && strncmp(&zLine[i], "abbreviated-subcommands", 23)==0) | |
| 287 | + || (j==12 && strncmp(&zLine[i], "abbrv-subcom", 12)==0) ){ | |
| 288 | + aEntry[nUsed].eType |= CMDFLAG_ABBREVSUBCMD; | |
| 285 | 289 | }else{ |
| 286 | 290 | fprintf(stderr, "%s:%d: unknown option: '%.*s'\n", |
| 287 | 291 | zFile, nLine, j, &zLine[i]); |
| 288 | 292 | nErr++; |
| 289 | 293 | } |
| 290 | 294 | |
| 291 | 295 | ADDED tools/randomize-js-names.tcl |
| 292 | 296 | ADDED win/build32.bat |
| 293 | 297 | ADDED win/build64.bat |
| --- tools/mkindex.c | |
| +++ tools/mkindex.c | |
| @@ -85,25 +85,26 @@ | |
| 85 | |
| 86 | /*************************************************************************** |
| 87 | ** These macros must match similar macros in dispatch.c. |
| 88 | ** |
| 89 | ** Allowed values for CmdOrPage.eCmdFlags. */ |
| 90 | #define CMDFLAG_1ST_TIER 0x0001 /* Most important commands */ |
| 91 | #define CMDFLAG_2ND_TIER 0x0002 /* Obscure and seldom used commands */ |
| 92 | #define CMDFLAG_TEST 0x0004 /* Commands for testing only */ |
| 93 | #define CMDFLAG_WEBPAGE 0x0008 /* Web pages */ |
| 94 | #define CMDFLAG_COMMAND 0x0010 /* A command */ |
| 95 | #define CMDFLAG_SETTING 0x0020 /* A setting */ |
| 96 | #define CMDFLAG_VERSIONABLE 0x0040 /* A versionable setting */ |
| 97 | #define CMDFLAG_BLOCKTEXT 0x0080 /* Multi-line text setting */ |
| 98 | #define CMDFLAG_BOOLEAN 0x0100 /* A boolean setting */ |
| 99 | #define CMDFLAG_RAWCONTENT 0x0200 /* Do not interpret webpage content */ |
| 100 | #define CMDFLAG_SENSITIVE 0x0400 /* Security-sensitive setting */ |
| 101 | #define CMDFLAG_HIDDEN 0x0800 /* Elide from most listings */ |
| 102 | #define CMDFLAG_LDAVG_EXEMPT 0x1000 /* Exempt from load_control() */ |
| 103 | #define CMDFLAG_ALIAS 0x2000 /* Command aliases */ |
| 104 | #define CMDFLAG_KEEPEMPTY 0x4000 /* Do not unset empty settings */ |
| 105 | /**************************************************************************/ |
| 106 | |
| 107 | /* |
| 108 | ** Each entry looks like this: |
| 109 | */ |
| @@ -280,10 +281,13 @@ | |
| 280 | aEntry[nUsed].zVar = string_dup(&zLine[i+9], j-9); |
| 281 | }else if( j==6 && strncmp(&zLine[i], "hidden", 6)==0 ){ |
| 282 | aEntry[nUsed].eType |= CMDFLAG_HIDDEN; |
| 283 | }else if( j==14 && strncmp(&zLine[i], "loadavg-exempt", 14)==0 ){ |
| 284 | aEntry[nUsed].eType |= CMDFLAG_LDAVG_EXEMPT; |
| 285 | }else{ |
| 286 | fprintf(stderr, "%s:%d: unknown option: '%.*s'\n", |
| 287 | zFile, nLine, j, &zLine[i]); |
| 288 | nErr++; |
| 289 | } |
| 290 | |
| 291 | DDED tools/randomize-js-names.tcl |
| 292 | DDED win/build32.bat |
| 293 | DDED win/build64.bat |
| --- tools/mkindex.c | |
| +++ tools/mkindex.c | |
| @@ -85,25 +85,26 @@ | |
| 85 | |
| 86 | /*************************************************************************** |
| 87 | ** These macros must match similar macros in dispatch.c. |
| 88 | ** |
| 89 | ** Allowed values for CmdOrPage.eCmdFlags. */ |
| 90 | #define CMDFLAG_1ST_TIER 0x00001 /* Most important commands */ |
| 91 | #define CMDFLAG_2ND_TIER 0x00002 /* Obscure and seldom used commands */ |
| 92 | #define CMDFLAG_TEST 0x00004 /* Commands for testing only */ |
| 93 | #define CMDFLAG_WEBPAGE 0x00008 /* Web pages */ |
| 94 | #define CMDFLAG_COMMAND 0x00010 /* A command */ |
| 95 | #define CMDFLAG_SETTING 0x00020 /* A setting */ |
| 96 | #define CMDFLAG_VERSIONABLE 0x00040 /* A versionable setting */ |
| 97 | #define CMDFLAG_BLOCKTEXT 0x00080 /* Multi-line text setting */ |
| 98 | #define CMDFLAG_BOOLEAN 0x00100 /* A boolean setting */ |
| 99 | #define CMDFLAG_RAWCONTENT 0x00200 /* Do not interpret webpage content */ |
| 100 | #define CMDFLAG_SENSITIVE 0x00400 /* Security-sensitive setting */ |
| 101 | #define CMDFLAG_HIDDEN 0x00800 /* Elide from most listings */ |
| 102 | #define CMDFLAG_LDAVG_EXEMPT 0x01000 /* Exempt from load_control() */ |
| 103 | #define CMDFLAG_ALIAS 0x02000 /* Command aliases */ |
| 104 | #define CMDFLAG_KEEPEMPTY 0x04000 /* Do not unset empty settings */ |
| 105 | #define CMDFLAG_ABBREVSUBCMD 0x08000 /* Abbreviated subcmd in help text */ |
| 106 | /**************************************************************************/ |
| 107 | |
| 108 | /* |
| 109 | ** Each entry looks like this: |
| 110 | */ |
| @@ -280,10 +281,13 @@ | |
| 281 | aEntry[nUsed].zVar = string_dup(&zLine[i+9], j-9); |
| 282 | }else if( j==6 && strncmp(&zLine[i], "hidden", 6)==0 ){ |
| 283 | aEntry[nUsed].eType |= CMDFLAG_HIDDEN; |
| 284 | }else if( j==14 && strncmp(&zLine[i], "loadavg-exempt", 14)==0 ){ |
| 285 | aEntry[nUsed].eType |= CMDFLAG_LDAVG_EXEMPT; |
| 286 | }else if( (j==23 && strncmp(&zLine[i], "abbreviated-subcommands", 23)==0) |
| 287 | || (j==12 && strncmp(&zLine[i], "abbrv-subcom", 12)==0) ){ |
| 288 | aEntry[nUsed].eType |= CMDFLAG_ABBREVSUBCMD; |
| 289 | }else{ |
| 290 | fprintf(stderr, "%s:%d: unknown option: '%.*s'\n", |
| 291 | zFile, nLine, j, &zLine[i]); |
| 292 | nErr++; |
| 293 | } |
| 294 | |
| 295 | DDED tools/randomize-js-names.tcl |
| 296 | DDED win/build32.bat |
| 297 | DDED win/build64.bat |
| --- a/tools/randomize-js-names.tcl | ||
| +++ b/tools/randomize-js-names.tcl | ||
| @@ -0,0 +1,36 @@ | ||
| 1 | +#!/usr/bin/tclsh | |
| 2 | +# | |
| 3 | +# This script is run as part of "make wasm". After emcc has | |
| 4 | +# run to generate extsrc/pikchr.wasm and extsrc/pikchr.js from | |
| 5 | +# extsrc/pikchr.c, we need to make changes to these filenames to | |
| 6 | +# work around caching problems. | |
| 7 | +# | |
| 8 | +# (1) in extsrc/pikchr.js -> change "pikchr.wasm" into | |
| 9 | +# "pikchr-vNNNNNNNN.wasm" where Ns are random digits. | |
| 10 | +# | |
| 11 | +# (2) in extsrc/pikchr-worker.js -> change "pikchr-vNNNNNNNN.js" | |
| 12 | +# by altering the random digits N. | |
| 13 | +# | |
| 14 | +set DIR extsrc | |
| 15 | +if {[llength $argv]>0} { | |
| 16 | + set DIR [lindex $argv 0] | |
| 17 | +} | |
| 18 | + | |
| 19 | +set R [expr {int(rand()*10000000000)+1000000000}] | |
| 20 | +set in [open $DIR/pikchr.js rb] | |
| 21 | +set f1 [read $in] | |
| 22 | +close $in | |
| 23 | +set f1mod [regsub {\ypikchr(-v\d+)?\.wasm\y} $f1 "pikchr-v$R.wasm"] | |
| 24 | +set out [open $DIR/pikchr.js wb] | |
| 25 | +puts -nonewline $out $f1mod | |
| 26 | +close $out | |
| 27 | +puts "modified $DIR/pikchr.js to reference \"pikchr-v$R.wasm\"" | |
| 28 | + | |
| 29 | +set in [open $DIR/pikchr-worker.js rb] | |
| 30 | +set f1 [read $in] | |
| 31 | +close $in | |
| 32 | +set f1mod [regsub {\ypikchr(-v\d+)?\.js\y} $f1 "pikchr-v$R.js"] | |
| 33 | +set out [open $DIR/pikchr-worker.js wb] | |
| 34 | +puts -nonewline $out $f1mod | |
| 35 | +close $out | |
| 36 | +puts "modified $DIR/pikchr-worker.js to reference \"pikchr-v$R.js\"" |
| --- a/tools/randomize-js-names.tcl | |
| +++ b/tools/randomize-js-names.tcl | |
| @@ -0,0 +1,36 @@ | |
| --- a/tools/randomize-js-names.tcl | |
| +++ b/tools/randomize-js-names.tcl | |
| @@ -0,0 +1,36 @@ | |
| 1 | #!/usr/bin/tclsh |
| 2 | # |
| 3 | # This script is run as part of "make wasm". After emcc has |
| 4 | # run to generate extsrc/pikchr.wasm and extsrc/pikchr.js from |
| 5 | # extsrc/pikchr.c, we need to make changes to these filenames to |
| 6 | # work around caching problems. |
| 7 | # |
| 8 | # (1) in extsrc/pikchr.js -> change "pikchr.wasm" into |
| 9 | # "pikchr-vNNNNNNNN.wasm" where Ns are random digits. |
| 10 | # |
| 11 | # (2) in extsrc/pikchr-worker.js -> change "pikchr-vNNNNNNNN.js" |
| 12 | # by altering the random digits N. |
| 13 | # |
| 14 | set DIR extsrc |
| 15 | if {[llength $argv]>0} { |
| 16 | set DIR [lindex $argv 0] |
| 17 | } |
| 18 | |
| 19 | set R [expr {int(rand()*10000000000)+1000000000}] |
| 20 | set in [open $DIR/pikchr.js rb] |
| 21 | set f1 [read $in] |
| 22 | close $in |
| 23 | set f1mod [regsub {\ypikchr(-v\d+)?\.wasm\y} $f1 "pikchr-v$R.wasm"] |
| 24 | set out [open $DIR/pikchr.js wb] |
| 25 | puts -nonewline $out $f1mod |
| 26 | close $out |
| 27 | puts "modified $DIR/pikchr.js to reference \"pikchr-v$R.wasm\"" |
| 28 | |
| 29 | set in [open $DIR/pikchr-worker.js rb] |
| 30 | set f1 [read $in] |
| 31 | close $in |
| 32 | set f1mod [regsub {\ypikchr(-v\d+)?\.js\y} $f1 "pikchr-v$R.js"] |
| 33 | set out [open $DIR/pikchr-worker.js wb] |
| 34 | puts -nonewline $out $f1mod |
| 35 | close $out |
| 36 | puts "modified $DIR/pikchr-worker.js to reference \"pikchr-v$R.js\"" |
+3
| --- a/win/build32.bat | ||
| +++ b/win/build32.bat | ||
| @@ -0,0 +1,3 @@ | ||
| 1 | +REM Based on /wiki/Release%20Build%20How-To | |
| 2 | +nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 FOSSIL_ENABLE_WINXP=1 OPTIMIZATIONS=4 clean fossil.exe | |
| 3 | +dumpbin /dependents fossil.exe |
| --- a/win/build32.bat | |
| +++ b/win/build32.bat | |
| @@ -0,0 +1,3 @@ | |
| --- a/win/build32.bat | |
| +++ b/win/build32.bat | |
| @@ -0,0 +1,3 @@ | |
| 1 | REM Based on /wiki/Release%20Build%20How-To |
| 2 | nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 FOSSIL_ENABLE_WINXP=1 OPTIMIZATIONS=4 clean fossil.exe |
| 3 | dumpbin /dependents fossil.exe |
+3
| --- a/win/build64.bat | ||
| +++ b/win/build64.bat | ||
| @@ -0,0 +1,3 @@ | ||
| 1 | +REM Based on /wiki/Release%20Build%20How-To | |
| 2 | +nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 OPTIMIZATIONS=4 clean fossil.exe | |
| 3 | +dumpbin /dependents fossil.exe |
| --- a/win/build64.bat | |
| +++ b/win/build64.bat | |
| @@ -0,0 +1,3 @@ | |
| --- a/win/build64.bat | |
| +++ b/win/build64.bat | |
| @@ -0,0 +1,3 @@ | |
| 1 | REM Based on /wiki/Release%20Build%20How-To |
| 2 | nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 OPTIMIZATIONS=4 clean fossil.exe |
| 3 | dumpbin /dependents fossil.exe |
+5
| --- www/alerts.md | ||
| +++ www/alerts.md | ||
| @@ -7,10 +7,11 @@ | ||
| 7 | 7 | |
| 8 | 8 | * New [checkins](/help?cmd=ci) |
| 9 | 9 | * [Ticket](./tickets.wiki) changes |
| 10 | 10 | * [Wiki](./wikitheory.wiki) page changes |
| 11 | 11 | * New and edited [forum](./forum.wiki) posts |
| 12 | + * Users receiving [new permissions](./caps/index.md) (admins only) | |
| 12 | 13 | * Announcements |
| 13 | 14 | |
| 14 | 15 | Subscribers can elect to receive emails as soon as these events happen, |
| 15 | 16 | or they can receive a daily digest of the events instead. |
| 16 | 17 | |
| @@ -515,10 +516,14 @@ | ||
| 515 | 516 | email addresses until the user clicks the link in the verification |
| 516 | 517 | email. This checkbox lets the Fossil Admin user manually verify the |
| 517 | 518 | user, such as in the case where the verification email message got |
| 518 | 519 | lost. Unchecking this box does not cause another verification email |
| 519 | 520 | to be sent. |
| 521 | + | |
| 522 | +* Admin users (only) may activate the "user elevation" subscription, | |
| 523 | + which sends a notification when a user is created or is explicitly | |
| 524 | + assigned permission they did not formerly have. | |
| 520 | 525 | |
| 521 | 526 | This screen also allows a Fossil Admin user to perform other activities |
| 522 | 527 | on behalf of a subscriber which they could do themselves, such as to |
| 523 | 528 | [unsubscribe](#unsub) them. |
| 524 | 529 | |
| 525 | 530 |
| --- www/alerts.md | |
| +++ www/alerts.md | |
| @@ -7,10 +7,11 @@ | |
| 7 | |
| 8 | * New [checkins](/help?cmd=ci) |
| 9 | * [Ticket](./tickets.wiki) changes |
| 10 | * [Wiki](./wikitheory.wiki) page changes |
| 11 | * New and edited [forum](./forum.wiki) posts |
| 12 | * Announcements |
| 13 | |
| 14 | Subscribers can elect to receive emails as soon as these events happen, |
| 15 | or they can receive a daily digest of the events instead. |
| 16 | |
| @@ -515,10 +516,14 @@ | |
| 515 | email addresses until the user clicks the link in the verification |
| 516 | email. This checkbox lets the Fossil Admin user manually verify the |
| 517 | user, such as in the case where the verification email message got |
| 518 | lost. Unchecking this box does not cause another verification email |
| 519 | to be sent. |
| 520 | |
| 521 | This screen also allows a Fossil Admin user to perform other activities |
| 522 | on behalf of a subscriber which they could do themselves, such as to |
| 523 | [unsubscribe](#unsub) them. |
| 524 | |
| 525 |
| --- www/alerts.md | |
| +++ www/alerts.md | |
| @@ -7,10 +7,11 @@ | |
| 7 | |
| 8 | * New [checkins](/help?cmd=ci) |
| 9 | * [Ticket](./tickets.wiki) changes |
| 10 | * [Wiki](./wikitheory.wiki) page changes |
| 11 | * New and edited [forum](./forum.wiki) posts |
| 12 | * Users receiving [new permissions](./caps/index.md) (admins only) |
| 13 | * Announcements |
| 14 | |
| 15 | Subscribers can elect to receive emails as soon as these events happen, |
| 16 | or they can receive a daily digest of the events instead. |
| 17 | |
| @@ -515,10 +516,14 @@ | |
| 516 | email addresses until the user clicks the link in the verification |
| 517 | email. This checkbox lets the Fossil Admin user manually verify the |
| 518 | user, such as in the case where the verification email message got |
| 519 | lost. Unchecking this box does not cause another verification email |
| 520 | to be sent. |
| 521 | |
| 522 | * Admin users (only) may activate the "user elevation" subscription, |
| 523 | which sends a notification when a user is created or is explicitly |
| 524 | assigned permission they did not formerly have. |
| 525 | |
| 526 | This screen also allows a Fossil Admin user to perform other activities |
| 527 | on behalf of a subscriber which they could do themselves, such as to |
| 528 | [unsubscribe](#unsub) them. |
| 529 | |
| 530 |
+10
-20
| --- www/build.wiki | ||
| +++ www/build.wiki | ||
| @@ -340,48 +340,36 @@ | ||
| 340 | 340 | found in the help text for the [/help?cmd=test-fuzz|test-fuzz |
| 341 | 341 | command]. |
| 342 | 342 | |
| 343 | 343 | Fuzzing requires: |
| 344 | 344 | |
| 345 | - * Customizing the build of fossil a small bit. | |
| 346 | 345 | * The clang C compiler. |
| 347 | 346 | * libfuzzer. On Ubuntu-derived systems, it can be installed with |
| 348 | 347 | <tt>apt install libfuzzer-XYZ</tt>, where XYZ is a version number |
| 349 | 348 | (several versions may be available on any given system) |
| 350 | 349 | |
| 351 | 350 | |
| 352 | -First, modify the top-level <tt>Makefile.in</tt>: | |
| 353 | - | |
| 354 | - * Extend the <tt>TCCFLAGS</tt> variable with: <tt>-fsanitize=fuzzer | |
| 355 | - -DFOSSIL_FUZZ</tt> (and see [/finfo/src/fuzz.c | src/fuzz.c] for | |
| 356 | - more options). | |
| 357 | - * Rename <tt>APPNAME</tt> from <tt>fossil</tt> to <tt>fossil-fuzz</tt>. | |
| 358 | - | |
| 359 | -Then rebuild: | |
| 360 | - | |
| 361 | -<pre></code>$ make clean | |
| 362 | -$ ./configure CC=/path/to/clang | |
| 363 | -$ make | |
| 351 | +Compile as follows: | |
| 352 | + | |
| 353 | +<pre><code>make clean | |
| 354 | +make TCCFLAGS='-DFOSSIL_FUZZ -fsanitize=fuzzer,address,undefined -O0 -g' CC=clang | |
| 364 | 355 | </code></pre> |
| 365 | 356 | |
| 366 | -If clang is your default compiler, the <tt>CC</tt> configure option is | |
| 367 | -not required. | |
| 368 | - | |
| 369 | -The resulting <tt>fossil-fuzz</tt> binary differs from the standard | |
| 357 | +The resulting <tt>fossil</tt> binary differs from the standard | |
| 370 | 358 | one primarily in that it runs the <tt>test-fuzz</tt> command by |
| 371 | 359 | default. It needs to be told what to fuzz and needs to be given a |
| 372 | 360 | directory of input files to seed the fuzzer with: |
| 373 | 361 | |
| 374 | 362 | |
| 375 | -<pre></code>$ mkdir cases | |
| 363 | +<pre><code>$ mkdir cases | |
| 376 | 364 | # Copy input files into ./cases. e.g. when fuzzing the markdown |
| 377 | 365 | # processor, copy any to-be-tested .md files into that directory. |
| 378 | 366 | # Then start the fuzzer: |
| 379 | 367 | $ ./fossil-fuzz --fuzztype markdown cases |
| 380 | 368 | </code></pre> |
| 381 | 369 | |
| 382 | -As it works, it writes its mutated test files into the test-input | |
| 370 | +As it works, it writes its mutated test files into the "cases" | |
| 383 | 371 | directory, each one named in the form of a hash. When it finds a |
| 384 | 372 | problem it will produce a stack trace for the offending code, will |
| 385 | 373 | output the name of the file which triggered the crash (named |
| 386 | 374 | <tt>cases/SOME_HASH</tt>) and may, depending on the nature of the |
| 387 | 375 | problem, produce a file named <tt>crash-SOMETHING</tt>. In theory the |
| @@ -503,11 +491,13 @@ | ||
| 503 | 491 | When a new version of <tt>extsrc/pikchr.c</tt> is installed, the |
| 504 | 492 | files <tt>pikchr.{js,wasm}</tt> will need to be recompiled to account |
| 505 | 493 | for that. Running <tt>make wasm</tt> will, if the build is set up for |
| 506 | 494 | the emsdk, recompile those: |
| 507 | 495 | |
| 508 | -<pre><code>$ make wasm | |
| 496 | +<pre><code>$ rm extsrc/pikchr.{js,wasm} | |
| 497 | +# ^^^^ that rm has proven necessary in order to ensure rebuilds | |
| 498 | +$ make wasm | |
| 509 | 499 | ./tools/emcc.sh -o extsrc/pikchr.js ... |
| 510 | 500 | $ ls -la extsrc/pikchr.{js,wasm} |
| 511 | 501 | -rw-rw-r-- 1 stephan stephan 17263 Jun 8 03:59 extsrc/pikchr.js |
| 512 | 502 | -rw-rw-r-- 1 stephan stephan 97578 Jun 8 03:59 extsrc/pikchr.wasm |
| 513 | 503 | </code></pre> |
| 514 | 504 |
| --- www/build.wiki | |
| +++ www/build.wiki | |
| @@ -340,48 +340,36 @@ | |
| 340 | found in the help text for the [/help?cmd=test-fuzz|test-fuzz |
| 341 | command]. |
| 342 | |
| 343 | Fuzzing requires: |
| 344 | |
| 345 | * Customizing the build of fossil a small bit. |
| 346 | * The clang C compiler. |
| 347 | * libfuzzer. On Ubuntu-derived systems, it can be installed with |
| 348 | <tt>apt install libfuzzer-XYZ</tt>, where XYZ is a version number |
| 349 | (several versions may be available on any given system) |
| 350 | |
| 351 | |
| 352 | First, modify the top-level <tt>Makefile.in</tt>: |
| 353 | |
| 354 | * Extend the <tt>TCCFLAGS</tt> variable with: <tt>-fsanitize=fuzzer |
| 355 | -DFOSSIL_FUZZ</tt> (and see [/finfo/src/fuzz.c | src/fuzz.c] for |
| 356 | more options). |
| 357 | * Rename <tt>APPNAME</tt> from <tt>fossil</tt> to <tt>fossil-fuzz</tt>. |
| 358 | |
| 359 | Then rebuild: |
| 360 | |
| 361 | <pre></code>$ make clean |
| 362 | $ ./configure CC=/path/to/clang |
| 363 | $ make |
| 364 | </code></pre> |
| 365 | |
| 366 | If clang is your default compiler, the <tt>CC</tt> configure option is |
| 367 | not required. |
| 368 | |
| 369 | The resulting <tt>fossil-fuzz</tt> binary differs from the standard |
| 370 | one primarily in that it runs the <tt>test-fuzz</tt> command by |
| 371 | default. It needs to be told what to fuzz and needs to be given a |
| 372 | directory of input files to seed the fuzzer with: |
| 373 | |
| 374 | |
| 375 | <pre></code>$ mkdir cases |
| 376 | # Copy input files into ./cases. e.g. when fuzzing the markdown |
| 377 | # processor, copy any to-be-tested .md files into that directory. |
| 378 | # Then start the fuzzer: |
| 379 | $ ./fossil-fuzz --fuzztype markdown cases |
| 380 | </code></pre> |
| 381 | |
| 382 | As it works, it writes its mutated test files into the test-input |
| 383 | directory, each one named in the form of a hash. When it finds a |
| 384 | problem it will produce a stack trace for the offending code, will |
| 385 | output the name of the file which triggered the crash (named |
| 386 | <tt>cases/SOME_HASH</tt>) and may, depending on the nature of the |
| 387 | problem, produce a file named <tt>crash-SOMETHING</tt>. In theory the |
| @@ -503,11 +491,13 @@ | |
| 503 | When a new version of <tt>extsrc/pikchr.c</tt> is installed, the |
| 504 | files <tt>pikchr.{js,wasm}</tt> will need to be recompiled to account |
| 505 | for that. Running <tt>make wasm</tt> will, if the build is set up for |
| 506 | the emsdk, recompile those: |
| 507 | |
| 508 | <pre><code>$ make wasm |
| 509 | ./tools/emcc.sh -o extsrc/pikchr.js ... |
| 510 | $ ls -la extsrc/pikchr.{js,wasm} |
| 511 | -rw-rw-r-- 1 stephan stephan 17263 Jun 8 03:59 extsrc/pikchr.js |
| 512 | -rw-rw-r-- 1 stephan stephan 97578 Jun 8 03:59 extsrc/pikchr.wasm |
| 513 | </code></pre> |
| 514 |
| --- www/build.wiki | |
| +++ www/build.wiki | |
| @@ -340,48 +340,36 @@ | |
| 340 | found in the help text for the [/help?cmd=test-fuzz|test-fuzz |
| 341 | command]. |
| 342 | |
| 343 | Fuzzing requires: |
| 344 | |
| 345 | * The clang C compiler. |
| 346 | * libfuzzer. On Ubuntu-derived systems, it can be installed with |
| 347 | <tt>apt install libfuzzer-XYZ</tt>, where XYZ is a version number |
| 348 | (several versions may be available on any given system) |
| 349 | |
| 350 | |
| 351 | Compile as follows: |
| 352 | |
| 353 | <pre><code>make clean |
| 354 | make TCCFLAGS='-DFOSSIL_FUZZ -fsanitize=fuzzer,address,undefined -O0 -g' CC=clang |
| 355 | </code></pre> |
| 356 | |
| 357 | The resulting <tt>fossil</tt> binary differs from the standard |
| 358 | one primarily in that it runs the <tt>test-fuzz</tt> command by |
| 359 | default. It needs to be told what to fuzz and needs to be given a |
| 360 | directory of input files to seed the fuzzer with: |
| 361 | |
| 362 | |
| 363 | <pre><code>$ mkdir cases |
| 364 | # Copy input files into ./cases. e.g. when fuzzing the markdown |
| 365 | # processor, copy any to-be-tested .md files into that directory. |
| 366 | # Then start the fuzzer: |
| 367 | $ ./fossil-fuzz --fuzztype markdown cases |
| 368 | </code></pre> |
| 369 | |
| 370 | As it works, it writes its mutated test files into the "cases" |
| 371 | directory, each one named in the form of a hash. When it finds a |
| 372 | problem it will produce a stack trace for the offending code, will |
| 373 | output the name of the file which triggered the crash (named |
| 374 | <tt>cases/SOME_HASH</tt>) and may, depending on the nature of the |
| 375 | problem, produce a file named <tt>crash-SOMETHING</tt>. In theory the |
| @@ -503,11 +491,13 @@ | |
| 491 | When a new version of <tt>extsrc/pikchr.c</tt> is installed, the |
| 492 | files <tt>pikchr.{js,wasm}</tt> will need to be recompiled to account |
| 493 | for that. Running <tt>make wasm</tt> will, if the build is set up for |
| 494 | the emsdk, recompile those: |
| 495 | |
| 496 | <pre><code>$ rm extsrc/pikchr.{js,wasm} |
| 497 | # ^^^^ that rm has proven necessary in order to ensure rebuilds |
| 498 | $ make wasm |
| 499 | ./tools/emcc.sh -o extsrc/pikchr.js ... |
| 500 | $ ls -la extsrc/pikchr.{js,wasm} |
| 501 | -rw-rw-r-- 1 stephan stephan 17263 Jun 8 03:59 extsrc/pikchr.js |
| 502 | -rw-rw-r-- 1 stephan stephan 97578 Jun 8 03:59 extsrc/pikchr.wasm |
| 503 | </code></pre> |
| 504 |
+5
-1
| --- www/cgi.wiki | ||
| +++ www/cgi.wiki | ||
| @@ -73,12 +73,16 @@ | ||
| 73 | 73 | of available Fossil repositories. |
| 74 | 74 | |
| 75 | 75 | The "skin" of the reply is determined by the first |
| 76 | 76 | repository in the list that has a non-zero |
| 77 | 77 | [/help?cmd=repolist-skin|repolist-skin] setting. |
| 78 | + | |
| 78 | 79 | If no repository has such a non-zero repolist-skin setting, then |
| 79 | -the repository list is generic HTML without any decoration. | |
| 80 | +the repository list is generic HTML without any decoration, with | |
| 81 | +the page title taken from the <tt>FOSSIL_REPOLIST_TITLE</tt> | |
| 82 | +environment variable. The variable can be defined in the CGI | |
| 83 | +control file using the [#setenv|<tt>setenv:</tt>] statement. | |
| 80 | 84 | |
| 81 | 85 | The repolist-generated page recurses into subdirectories and will list |
| 82 | 86 | all <tt>*.fossil</tt> files found, with the following exceptions: |
| 83 | 87 | |
| 84 | 88 | * Filenames starting with a period are treated as "hidden" and skipped. |
| 85 | 89 |
| --- www/cgi.wiki | |
| +++ www/cgi.wiki | |
| @@ -73,12 +73,16 @@ | |
| 73 | of available Fossil repositories. |
| 74 | |
| 75 | The "skin" of the reply is determined by the first |
| 76 | repository in the list that has a non-zero |
| 77 | [/help?cmd=repolist-skin|repolist-skin] setting. |
| 78 | If no repository has such a non-zero repolist-skin setting, then |
| 79 | the repository list is generic HTML without any decoration. |
| 80 | |
| 81 | The repolist-generated page recurses into subdirectories and will list |
| 82 | all <tt>*.fossil</tt> files found, with the following exceptions: |
| 83 | |
| 84 | * Filenames starting with a period are treated as "hidden" and skipped. |
| 85 |
| --- www/cgi.wiki | |
| +++ www/cgi.wiki | |
| @@ -73,12 +73,16 @@ | |
| 73 | of available Fossil repositories. |
| 74 | |
| 75 | The "skin" of the reply is determined by the first |
| 76 | repository in the list that has a non-zero |
| 77 | [/help?cmd=repolist-skin|repolist-skin] setting. |
| 78 | |
| 79 | If no repository has such a non-zero repolist-skin setting, then |
| 80 | the repository list is generic HTML without any decoration, with |
| 81 | the page title taken from the <tt>FOSSIL_REPOLIST_TITLE</tt> |
| 82 | environment variable. The variable can be defined in the CGI |
| 83 | control file using the [#setenv|<tt>setenv:</tt>] statement. |
| 84 | |
| 85 | The repolist-generated page recurses into subdirectories and will list |
| 86 | all <tt>*.fossil</tt> files found, with the following exceptions: |
| 87 | |
| 88 | * Filenames starting with a period are treated as "hidden" and skipped. |
| 89 |
+98
-24
| --- www/changes.wiki | ||
| +++ www/changes.wiki | ||
| @@ -1,28 +1,63 @@ | ||
| 1 | 1 | <title>Change Log</title> |
| 2 | 2 | |
| 3 | 3 | <h2 id='v2_26'>Changes for version 2.26 (pending)</h2> |
| 4 | 4 | |
| 5 | - * Enhanced the --from option on "[/help?cmd=diff|fossil diff]" so that | |
| 6 | - it optionally accepts a directory name as its argument, and uses files | |
| 7 | - under that directory as the baseline for the diff. | |
| 5 | + * Enhancements to [/help?cmd=diff|fossil diff] and similar: | |
| 6 | + <ol type="a"> | |
| 7 | + <li> The --from can optionally accepts a directory name as its argument, | |
| 8 | + and uses files under that directory as the baseline for the diff. | |
| 9 | + <li> For "gdiff", if no [/help?cmd=gdiff-command|gdiff-command setting] | |
| 10 | + is defined, Fossil tries to do a --tk diff if "tclsh" and "wish" | |
| 11 | + are available, or a --by diff if not. | |
| 12 | + <li> The "Reload" button is added to --tk diffs, to bring the displayed | |
| 13 | + diff up to date with the latest changes on disk. | |
| 14 | + <li> Add the "Hide diffs/Show diffs" toggle to web-UI diff pages that show | |
| 15 | + diffs of multiple files. | |
| 16 | + </ol> | |
| 8 | 17 | * Added the [/help?cmd=/ckout|/ckout web page] to provide information |
| 9 | 18 | about pending changes in a working check-out |
| 10 | - * The [/help?cmd=ui|fossil ui] command defaults to using the | |
| 11 | - [/help?cmd=/ckout|/ckout page] as its start page. Or, if the | |
| 12 | - "--from PATH" option is present, the default start page becomes | |
| 13 | - "/ckout?exbase=PATH". | |
| 14 | - * Added the [/help?cmd=merge-info|fossil merge-info] command and especially | |
| 15 | - the --tk option to that command, to provide analysis of the most recent | |
| 16 | - merge or update operation. | |
| 17 | - * Added the ability to sign check-ins with SSH keys. | |
| 18 | - * Issue a warning if a user tries to commit on a check-in where the | |
| 19 | - branch has been changed. | |
| 20 | - * When a merge conflict occurs, a new section is added to the conflict | |
| 21 | - text that shows Fossil's suggested resolution to the conflict. | |
| 22 | - * Add the "Hide diffs/Show diffs" toggle to web-UI diff pages that show | |
| 23 | - diffs of multiple files. | |
| 19 | + * Enhancements to the [/help?cmd=ui|fossil ui] command: | |
| 20 | + <ol type="a"> | |
| 21 | + <li> Defaults to using the new [/help?cmd=/ckout|/ckout page] as its | |
| 22 | + start page. Or, if the new "--from PATH" option is present, the | |
| 23 | + default start page becomes "/ckout?exbase=PATH". | |
| 24 | + <li> The new "--extpage FILENAME" option opens the named file as if it | |
| 25 | + where in a [./serverext.wiki|CGI extension]. Example usage: the | |
| 26 | + person editing this change log has | |
| 27 | + "fossil ui --extpage www/changes.wiki" running and hence can | |
| 28 | + press "Reload" on the web browser to view edits. | |
| 29 | + </ol> | |
| 30 | + * Enhancements to [/help?cmd=merge|fossil merge]: | |
| 31 | + <ol type="a"> | |
| 32 | + <li> Added the [/help?cmd=merge-info|fossil merge-info] command and | |
| 33 | + especially the --tk option to that command, to provide analysis | |
| 34 | + of the most recent merge or update operation. | |
| 35 | + <li> When a merge conflict occurs, a new section is added to the conflict | |
| 36 | + text that shows Fossil's suggested resolution to the conflict. | |
| 37 | + </ol> | |
| 38 | + * Enhancements to [/help?cmd=commit|fossil commit]: | |
| 39 | + <ol type="a"> | |
| 40 | + <li> If Fossil sees potential formatting mistakes (ex: bad hyperlinks) | |
| 41 | + in the check-in comment, it will alert the developer and give | |
| 42 | + him or her the opportunity to edit the comment before continuing. | |
| 43 | + This feature is controllable by the | |
| 44 | + [/help?cmd=verify-comments|verify-comments setting]. | |
| 45 | + <li> The new "--if-changes" option causes the commit to become | |
| 46 | + a quiet no-op if there are no pending changes. | |
| 47 | + <li> Added the ability to sign check-ins with SSH keys. | |
| 48 | + <li> Issue a warning if a user tries to commit on a check-in where the | |
| 49 | + branch has been changed. | |
| 50 | + <li> The interactive checkin comment prompt shows the formatting rules | |
| 51 | + set for that repository. | |
| 52 | + </ol> | |
| 53 | + * Deprecate the --comfmtflags and --comment-format global options and | |
| 54 | + no longer list them in the built-in help, but keep them working for | |
| 55 | + backwards compatibility. | |
| 56 | + Alternative TTY comment formatting can still be specified using the | |
| 57 | + [/help?cmd=comment-format|comment-format setting], if desired. The | |
| 58 | + default comment format is now called "canonical", not "legacy". | |
| 24 | 59 | * Enhancements to the [/help?cmd=/timeline|/timeline page]: |
| 25 | 60 | <ol type="a"> |
| 26 | 61 | <li> Added the "ml=" ("Merge-in List") query parameter that works |
| 27 | 62 | like "rl=" ("Related List") but adds "mionly" style related |
| 28 | 63 | check-ins instead of the full "rel" style. |
| @@ -43,20 +78,59 @@ | ||
| 43 | 78 | <li> Enhance the "ymd" query parameter so that when used like |
| 44 | 79 | "ymd=YYYYMMDD-YYYYMMDD" it shows all events in the range of |
| 45 | 80 | dates specified. |
| 46 | 81 | <li> Accept the "Z" (Zulu-time) suffix on date arguments for the |
| 47 | 82 | "ymd" and "yw" query parameters. |
| 83 | + <li> The new "min" query parameter, when added to a from=,to= query, | |
| 84 | + collapses long runs of check-ins on the same branch into just | |
| 85 | + end-points. | |
| 86 | + <li> The p= and d= parameters an reference different check-ins, which | |
| 87 | + case the timeline shows those check-ins that are both ancestors | |
| 88 | + of p= and descendants of d=. | |
| 89 | + <li> The saturation and intensity of user-specified checkin and branch | |
| 90 | + colors are automatically adjusted to keep the colors | |
| 91 | + compatible with the current skin, unless the | |
| 92 | + [/help?cmd=raw-bgcolor|raw-bgcolor setting] is turned on. The | |
| 93 | + /test-bgcolor page was added to | |
| 94 | + test and visualize how these adjustments. | |
| 48 | 95 | </ol> |
| 49 | - * Add the "--if-changes" option to the [/help?cmd=commit|fossil commit] | |
| 50 | - command that causes the command to become a quiet no-op if there are | |
| 51 | - no pending changes. | |
| 96 | + * The [/help?cmd=/docfile|/docfile webpage] was added. It works like | |
| 97 | + /doc but keeps the title of markdown documents with the document rather | |
| 98 | + that moving it up to the page title. | |
| 52 | 99 | * Added the [/help?cmd=/clusterlist|/clusterlist page] for analysis |
| 53 | 100 | and debugging |
| 54 | - * Fix a bug in [/help?cmd=patch|fossil patch create] that causes | |
| 55 | - [/help?cmd=revert|fossil revert] operations that happened on individual | |
| 56 | - files after a [/help?cmd=merge|fossil merge] to be omitted from the | |
| 57 | - patch. | |
| 101 | + * Added the "artifact_to_json(NAME)" SQL function that returns a JSON | |
| 102 | + decoding of the artifact described by NAME. | |
| 103 | + * Improvements to the [/help?cmd=patch|fossil patch] command: | |
| 104 | + <ol type="a"> | |
| 105 | + <li> Fix a bug in "fossil patch create" that causes | |
| 106 | + [/help?cmd=revert|fossil revert] operations that happened | |
| 107 | + on individualfiles after a [/help?cmd=merge|fossil merge] | |
| 108 | + to be omitted from the patch. | |
| 109 | + <li> Added the [/help?cmd=patch|patch alias] command for managing | |
| 110 | + aliases for remote checkout names. | |
| 111 | + </ol> | |
| 112 | + * Enhancements to on-line help and the [/help?cmd=help|fossil help] command: | |
| 113 | + <ol type="a"> | |
| 114 | + <li> Add the ability to search the help text, either in the UI | |
| 115 | + (on the [/help?cmd=/search|/search page]) or from the command-line | |
| 116 | + (using the "[/help?cmd=search|fossil search -h PATTERN]" command.) | |
| 117 | + <li> Accepts an optional SUBCOMMAND argument following the | |
| 118 | + COMMAND argument and only shows results for the specified | |
| 119 | + subcommand, not the entire command. | |
| 120 | + <li> The -u (--usage) option shows only the command-line syntax | |
| 121 | + <li> The -o (--options) option shows only the command-line options | |
| 122 | + </ol> | |
| 123 | + * Added the ability to attach wiki pages to a ticket for extended | |
| 124 | + descriptions. | |
| 125 | + * Added the "hash" query parameter to the | |
| 126 | + [/help?cmd=/whatis|/whatis webpage]. | |
| 127 | + * Add a "user elevation" [/doc/trunk/www/alerts.md|subscription] | |
| 128 | + which alerts subscribers when an admin creates a new user or | |
| 129 | + adds new permissions to one. | |
| 130 | + * Diverse minor fixes and additions. | |
| 131 | + | |
| 58 | 132 | |
| 59 | 133 | <h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</h2> |
| 60 | 134 | |
| 61 | 135 | * The "[/help?cmd=ui|fossil ui /]" command now works even for repositories |
| 62 | 136 | that have non-ASCII filenames |
| 63 | 137 |
| --- www/changes.wiki | |
| +++ www/changes.wiki | |
| @@ -1,28 +1,63 @@ | |
| 1 | <title>Change Log</title> |
| 2 | |
| 3 | <h2 id='v2_26'>Changes for version 2.26 (pending)</h2> |
| 4 | |
| 5 | * Enhanced the --from option on "[/help?cmd=diff|fossil diff]" so that |
| 6 | it optionally accepts a directory name as its argument, and uses files |
| 7 | under that directory as the baseline for the diff. |
| 8 | * Added the [/help?cmd=/ckout|/ckout web page] to provide information |
| 9 | about pending changes in a working check-out |
| 10 | * The [/help?cmd=ui|fossil ui] command defaults to using the |
| 11 | [/help?cmd=/ckout|/ckout page] as its start page. Or, if the |
| 12 | "--from PATH" option is present, the default start page becomes |
| 13 | "/ckout?exbase=PATH". |
| 14 | * Added the [/help?cmd=merge-info|fossil merge-info] command and especially |
| 15 | the --tk option to that command, to provide analysis of the most recent |
| 16 | merge or update operation. |
| 17 | * Added the ability to sign check-ins with SSH keys. |
| 18 | * Issue a warning if a user tries to commit on a check-in where the |
| 19 | branch has been changed. |
| 20 | * When a merge conflict occurs, a new section is added to the conflict |
| 21 | text that shows Fossil's suggested resolution to the conflict. |
| 22 | * Add the "Hide diffs/Show diffs" toggle to web-UI diff pages that show |
| 23 | diffs of multiple files. |
| 24 | * Enhancements to the [/help?cmd=/timeline|/timeline page]: |
| 25 | <ol type="a"> |
| 26 | <li> Added the "ml=" ("Merge-in List") query parameter that works |
| 27 | like "rl=" ("Related List") but adds "mionly" style related |
| 28 | check-ins instead of the full "rel" style. |
| @@ -43,20 +78,59 @@ | |
| 43 | <li> Enhance the "ymd" query parameter so that when used like |
| 44 | "ymd=YYYYMMDD-YYYYMMDD" it shows all events in the range of |
| 45 | dates specified. |
| 46 | <li> Accept the "Z" (Zulu-time) suffix on date arguments for the |
| 47 | "ymd" and "yw" query parameters. |
| 48 | </ol> |
| 49 | * Add the "--if-changes" option to the [/help?cmd=commit|fossil commit] |
| 50 | command that causes the command to become a quiet no-op if there are |
| 51 | no pending changes. |
| 52 | * Added the [/help?cmd=/clusterlist|/clusterlist page] for analysis |
| 53 | and debugging |
| 54 | * Fix a bug in [/help?cmd=patch|fossil patch create] that causes |
| 55 | [/help?cmd=revert|fossil revert] operations that happened on individual |
| 56 | files after a [/help?cmd=merge|fossil merge] to be omitted from the |
| 57 | patch. |
| 58 | |
| 59 | <h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</h2> |
| 60 | |
| 61 | * The "[/help?cmd=ui|fossil ui /]" command now works even for repositories |
| 62 | that have non-ASCII filenames |
| 63 |
| --- www/changes.wiki | |
| +++ www/changes.wiki | |
| @@ -1,28 +1,63 @@ | |
| 1 | <title>Change Log</title> |
| 2 | |
| 3 | <h2 id='v2_26'>Changes for version 2.26 (pending)</h2> |
| 4 | |
| 5 | * Enhancements to [/help?cmd=diff|fossil diff] and similar: |
| 6 | <ol type="a"> |
| 7 | <li> The --from can optionally accepts a directory name as its argument, |
| 8 | and uses files under that directory as the baseline for the diff. |
| 9 | <li> For "gdiff", if no [/help?cmd=gdiff-command|gdiff-command setting] |
| 10 | is defined, Fossil tries to do a --tk diff if "tclsh" and "wish" |
| 11 | are available, or a --by diff if not. |
| 12 | <li> The "Reload" button is added to --tk diffs, to bring the displayed |
| 13 | diff up to date with the latest changes on disk. |
| 14 | <li> Add the "Hide diffs/Show diffs" toggle to web-UI diff pages that show |
| 15 | diffs of multiple files. |
| 16 | </ol> |
| 17 | * Added the [/help?cmd=/ckout|/ckout web page] to provide information |
| 18 | about pending changes in a working check-out |
| 19 | * Enhancements to the [/help?cmd=ui|fossil ui] command: |
| 20 | <ol type="a"> |
| 21 | <li> Defaults to using the new [/help?cmd=/ckout|/ckout page] as its |
| 22 | start page. Or, if the new "--from PATH" option is present, the |
| 23 | default start page becomes "/ckout?exbase=PATH". |
| 24 | <li> The new "--extpage FILENAME" option opens the named file as if it |
| 25 | where in a [./serverext.wiki|CGI extension]. Example usage: the |
| 26 | person editing this change log has |
| 27 | "fossil ui --extpage www/changes.wiki" running and hence can |
| 28 | press "Reload" on the web browser to view edits. |
| 29 | </ol> |
| 30 | * Enhancements to [/help?cmd=merge|fossil merge]: |
| 31 | <ol type="a"> |
| 32 | <li> Added the [/help?cmd=merge-info|fossil merge-info] command and |
| 33 | especially the --tk option to that command, to provide analysis |
| 34 | of the most recent merge or update operation. |
| 35 | <li> When a merge conflict occurs, a new section is added to the conflict |
| 36 | text that shows Fossil's suggested resolution to the conflict. |
| 37 | </ol> |
| 38 | * Enhancements to [/help?cmd=commit|fossil commit]: |
| 39 | <ol type="a"> |
| 40 | <li> If Fossil sees potential formatting mistakes (ex: bad hyperlinks) |
| 41 | in the check-in comment, it will alert the developer and give |
| 42 | him or her the opportunity to edit the comment before continuing. |
| 43 | This feature is controllable by the |
| 44 | [/help?cmd=verify-comments|verify-comments setting]. |
| 45 | <li> The new "--if-changes" option causes the commit to become |
| 46 | a quiet no-op if there are no pending changes. |
| 47 | <li> Added the ability to sign check-ins with SSH keys. |
| 48 | <li> Issue a warning if a user tries to commit on a check-in where the |
| 49 | branch has been changed. |
| 50 | <li> The interactive checkin comment prompt shows the formatting rules |
| 51 | set for that repository. |
| 52 | </ol> |
| 53 | * Deprecate the --comfmtflags and --comment-format global options and |
| 54 | no longer list them in the built-in help, but keep them working for |
| 55 | backwards compatibility. |
| 56 | Alternative TTY comment formatting can still be specified using the |
| 57 | [/help?cmd=comment-format|comment-format setting], if desired. The |
| 58 | default comment format is now called "canonical", not "legacy". |
| 59 | * Enhancements to the [/help?cmd=/timeline|/timeline page]: |
| 60 | <ol type="a"> |
| 61 | <li> Added the "ml=" ("Merge-in List") query parameter that works |
| 62 | like "rl=" ("Related List") but adds "mionly" style related |
| 63 | check-ins instead of the full "rel" style. |
| @@ -43,20 +78,59 @@ | |
| 78 | <li> Enhance the "ymd" query parameter so that when used like |
| 79 | "ymd=YYYYMMDD-YYYYMMDD" it shows all events in the range of |
| 80 | dates specified. |
| 81 | <li> Accept the "Z" (Zulu-time) suffix on date arguments for the |
| 82 | "ymd" and "yw" query parameters. |
| 83 | <li> The new "min" query parameter, when added to a from=,to= query, |
| 84 | collapses long runs of check-ins on the same branch into just |
| 85 | end-points. |
| 86 | <li> The p= and d= parameters an reference different check-ins, which |
| 87 | case the timeline shows those check-ins that are both ancestors |
| 88 | of p= and descendants of d=. |
| 89 | <li> The saturation and intensity of user-specified checkin and branch |
| 90 | colors are automatically adjusted to keep the colors |
| 91 | compatible with the current skin, unless the |
| 92 | [/help?cmd=raw-bgcolor|raw-bgcolor setting] is turned on. The |
| 93 | /test-bgcolor page was added to |
| 94 | test and visualize how these adjustments. |
| 95 | </ol> |
| 96 | * The [/help?cmd=/docfile|/docfile webpage] was added. It works like |
| 97 | /doc but keeps the title of markdown documents with the document rather |
| 98 | that moving it up to the page title. |
| 99 | * Added the [/help?cmd=/clusterlist|/clusterlist page] for analysis |
| 100 | and debugging |
| 101 | * Added the "artifact_to_json(NAME)" SQL function that returns a JSON |
| 102 | decoding of the artifact described by NAME. |
| 103 | * Improvements to the [/help?cmd=patch|fossil patch] command: |
| 104 | <ol type="a"> |
| 105 | <li> Fix a bug in "fossil patch create" that causes |
| 106 | [/help?cmd=revert|fossil revert] operations that happened |
| 107 | on individualfiles after a [/help?cmd=merge|fossil merge] |
| 108 | to be omitted from the patch. |
| 109 | <li> Added the [/help?cmd=patch|patch alias] command for managing |
| 110 | aliases for remote checkout names. |
| 111 | </ol> |
| 112 | * Enhancements to on-line help and the [/help?cmd=help|fossil help] command: |
| 113 | <ol type="a"> |
| 114 | <li> Add the ability to search the help text, either in the UI |
| 115 | (on the [/help?cmd=/search|/search page]) or from the command-line |
| 116 | (using the "[/help?cmd=search|fossil search -h PATTERN]" command.) |
| 117 | <li> Accepts an optional SUBCOMMAND argument following the |
| 118 | COMMAND argument and only shows results for the specified |
| 119 | subcommand, not the entire command. |
| 120 | <li> The -u (--usage) option shows only the command-line syntax |
| 121 | <li> The -o (--options) option shows only the command-line options |
| 122 | </ol> |
| 123 | * Added the ability to attach wiki pages to a ticket for extended |
| 124 | descriptions. |
| 125 | * Added the "hash" query parameter to the |
| 126 | [/help?cmd=/whatis|/whatis webpage]. |
| 127 | * Add a "user elevation" [/doc/trunk/www/alerts.md|subscription] |
| 128 | which alerts subscribers when an admin creates a new user or |
| 129 | adds new permissions to one. |
| 130 | * Diverse minor fixes and additions. |
| 131 | |
| 132 | |
| 133 | <h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</h2> |
| 134 | |
| 135 | * The "[/help?cmd=ui|fossil ui /]" command now works even for repositories |
| 136 | that have non-ASCII filenames |
| 137 |
+18
-3
| --- www/env-opts.md | ||
| +++ www/env-opts.md | ||
| @@ -39,11 +39,11 @@ | ||
| 39 | 39 | and certain other text outputs are formatted for display. The flags are |
| 40 | 40 | individual bits in `NUMBER`, which must be specified in base 10: |
| 41 | 41 | |
| 42 | 42 | * _0_ — Uses the revised algorithm with no special handling. |
| 43 | 43 | |
| 44 | - * _1_ — Uses the legacy algorithm, other flags are ignored. | |
| 44 | + * _1_ — Uses the canonical algorithm, other flags are ignored. | |
| 45 | 45 | |
| 46 | 46 | * _2_ — Trims leading and trailing carriage-returns and line-feeds |
| 47 | 47 | where they do not materially impact pre-existing formatting |
| 48 | 48 | (i.e. at the start of the comment string _and_ right before |
| 49 | 49 | line indentation). |
| @@ -60,10 +60,15 @@ | ||
| 60 | 60 | preserving more of the pre-existing formatting. |
| 61 | 61 | |
| 62 | 62 | |
| 63 | 63 | `--comment-format NUMBER`: Alias for `--comfmtflags NUMBER`. |
| 64 | 64 | |
| 65 | + | |
| 66 | +> NOTE: As of Fossil version 2.26, use of the `--comfmtflags` and | |
| 67 | +> `--comment-format` options is no longer recommended and they are | |
| 68 | +> no longer documented, but retained for backwards compatibility. | |
| 69 | + | |
| 65 | 70 | |
| 66 | 71 | `--errorlog ERRLOG`: Name a file to which fossil will log panics, |
| 67 | 72 | errors, and warnings. |
| 68 | 73 | |
| 69 | 74 | |
| @@ -143,10 +148,15 @@ | ||
| 143 | 148 | |
| 144 | 149 | |
| 145 | 150 | `FOSSIL_HOME`: Location of [configuration database][configdb]. |
| 146 | 151 | See the [configuration database location][configloc] description |
| 147 | 152 | for additional information. |
| 153 | + | |
| 154 | +`FOSSIL_REPOLIST_TITLE`: The page title of the "Repository List" page | |
| 155 | +loaded by the `fossil all ui` or `fossil ui /` commands. Only used if | |
| 156 | +none of the listed repositories has the `repolist_skin` property set. | |
| 157 | +Can be set from the [CGI control file][cgictlfile]. | |
| 148 | 158 | |
| 149 | 159 | `FOSSIL_USE_SEE_TEXTKEY`: If set, treat the encryption key string for |
| 150 | 160 | SEE as text to be hashed into the actual encryption key. This has no |
| 151 | 161 | effect if Fossil was not compiled with SEE support enabled. |
| 152 | 162 | |
| @@ -210,10 +220,14 @@ | ||
| 210 | 220 | used as the location of the `~/.fossil` file. |
| 211 | 221 | |
| 212 | 222 | `LOGNAME`: Name of the logged in user on many Unix-like platforms. |
| 213 | 223 | Used as the fossil user name if `FOSSIL_USER` is not specified. See |
| 214 | 224 | the discussion of Fossil Username below for a lot more detail. |
| 225 | + | |
| 226 | +`NO_COLOR`: If defined and not set to a `false` value (i.e. "off", "no", | |
| 227 | +"false", "0"), the `fossil search` command skips colorization of console | |
| 228 | +output using ANSI escape codes (VT100). | |
| 215 | 229 | |
| 216 | 230 | `PATH`: Used by most platforms to locate programs invoked without a |
| 217 | 231 | fully qualified name. Explicitly used by `fossil ui` on certain platforms |
| 218 | 232 | to choose the browser to launch. |
| 219 | 233 | |
| @@ -464,12 +478,12 @@ | ||
| 464 | 478 | |
| 465 | 479 | ### Web browser |
| 466 | 480 | |
| 467 | 481 | Occasionally, fossil wants to launch a web browser for the user, most |
| 468 | 482 | obviously as part of the `fossil ui` command. In that specific case, |
| 469 | -the browser is launched pointing at the web server started by `fossil | |
| 470 | -ui` listening on a private TCP port. | |
| 483 | +the browser is launched pointing at the web server started by | |
| 484 | +`fossil ui` listening on a private TCP port. | |
| 471 | 485 | |
| 472 | 486 | On all platforms, if the local or global settings `web-browser` is |
| 473 | 487 | set, that is the command used to open a URL. |
| 474 | 488 | |
| 475 | 489 | Otherwise, the specific actions vary by platform. |
| @@ -484,5 +498,6 @@ | ||
| 484 | 498 | On Windows platforms, it assumes that `start` is the command to open |
| 485 | 499 | a URL in the user's configured default browser. |
| 486 | 500 | |
| 487 | 501 | [configdb]: ./tech_overview.wiki#configdb |
| 488 | 502 | [configloc]: ./tech_overview.wiki#configloc |
| 503 | +[cgictlfile]: ./cgi.wiki | |
| 489 | 504 |
| --- www/env-opts.md | |
| +++ www/env-opts.md | |
| @@ -39,11 +39,11 @@ | |
| 39 | and certain other text outputs are formatted for display. The flags are |
| 40 | individual bits in `NUMBER`, which must be specified in base 10: |
| 41 | |
| 42 | * _0_ — Uses the revised algorithm with no special handling. |
| 43 | |
| 44 | * _1_ — Uses the legacy algorithm, other flags are ignored. |
| 45 | |
| 46 | * _2_ — Trims leading and trailing carriage-returns and line-feeds |
| 47 | where they do not materially impact pre-existing formatting |
| 48 | (i.e. at the start of the comment string _and_ right before |
| 49 | line indentation). |
| @@ -60,10 +60,15 @@ | |
| 60 | preserving more of the pre-existing formatting. |
| 61 | |
| 62 | |
| 63 | `--comment-format NUMBER`: Alias for `--comfmtflags NUMBER`. |
| 64 | |
| 65 | |
| 66 | `--errorlog ERRLOG`: Name a file to which fossil will log panics, |
| 67 | errors, and warnings. |
| 68 | |
| 69 | |
| @@ -143,10 +148,15 @@ | |
| 143 | |
| 144 | |
| 145 | `FOSSIL_HOME`: Location of [configuration database][configdb]. |
| 146 | See the [configuration database location][configloc] description |
| 147 | for additional information. |
| 148 | |
| 149 | `FOSSIL_USE_SEE_TEXTKEY`: If set, treat the encryption key string for |
| 150 | SEE as text to be hashed into the actual encryption key. This has no |
| 151 | effect if Fossil was not compiled with SEE support enabled. |
| 152 | |
| @@ -210,10 +220,14 @@ | |
| 210 | used as the location of the `~/.fossil` file. |
| 211 | |
| 212 | `LOGNAME`: Name of the logged in user on many Unix-like platforms. |
| 213 | Used as the fossil user name if `FOSSIL_USER` is not specified. See |
| 214 | the discussion of Fossil Username below for a lot more detail. |
| 215 | |
| 216 | `PATH`: Used by most platforms to locate programs invoked without a |
| 217 | fully qualified name. Explicitly used by `fossil ui` on certain platforms |
| 218 | to choose the browser to launch. |
| 219 | |
| @@ -464,12 +478,12 @@ | |
| 464 | |
| 465 | ### Web browser |
| 466 | |
| 467 | Occasionally, fossil wants to launch a web browser for the user, most |
| 468 | obviously as part of the `fossil ui` command. In that specific case, |
| 469 | the browser is launched pointing at the web server started by `fossil |
| 470 | ui` listening on a private TCP port. |
| 471 | |
| 472 | On all platforms, if the local or global settings `web-browser` is |
| 473 | set, that is the command used to open a URL. |
| 474 | |
| 475 | Otherwise, the specific actions vary by platform. |
| @@ -484,5 +498,6 @@ | |
| 484 | On Windows platforms, it assumes that `start` is the command to open |
| 485 | a URL in the user's configured default browser. |
| 486 | |
| 487 | [configdb]: ./tech_overview.wiki#configdb |
| 488 | [configloc]: ./tech_overview.wiki#configloc |
| 489 |
| --- www/env-opts.md | |
| +++ www/env-opts.md | |
| @@ -39,11 +39,11 @@ | |
| 39 | and certain other text outputs are formatted for display. The flags are |
| 40 | individual bits in `NUMBER`, which must be specified in base 10: |
| 41 | |
| 42 | * _0_ — Uses the revised algorithm with no special handling. |
| 43 | |
| 44 | * _1_ — Uses the canonical algorithm, other flags are ignored. |
| 45 | |
| 46 | * _2_ — Trims leading and trailing carriage-returns and line-feeds |
| 47 | where they do not materially impact pre-existing formatting |
| 48 | (i.e. at the start of the comment string _and_ right before |
| 49 | line indentation). |
| @@ -60,10 +60,15 @@ | |
| 60 | preserving more of the pre-existing formatting. |
| 61 | |
| 62 | |
| 63 | `--comment-format NUMBER`: Alias for `--comfmtflags NUMBER`. |
| 64 | |
| 65 | |
| 66 | > NOTE: As of Fossil version 2.26, use of the `--comfmtflags` and |
| 67 | > `--comment-format` options is no longer recommended and they are |
| 68 | > no longer documented, but retained for backwards compatibility. |
| 69 | |
| 70 | |
| 71 | `--errorlog ERRLOG`: Name a file to which fossil will log panics, |
| 72 | errors, and warnings. |
| 73 | |
| 74 | |
| @@ -143,10 +148,15 @@ | |
| 148 | |
| 149 | |
| 150 | `FOSSIL_HOME`: Location of [configuration database][configdb]. |
| 151 | See the [configuration database location][configloc] description |
| 152 | for additional information. |
| 153 | |
| 154 | `FOSSIL_REPOLIST_TITLE`: The page title of the "Repository List" page |
| 155 | loaded by the `fossil all ui` or `fossil ui /` commands. Only used if |
| 156 | none of the listed repositories has the `repolist_skin` property set. |
| 157 | Can be set from the [CGI control file][cgictlfile]. |
| 158 | |
| 159 | `FOSSIL_USE_SEE_TEXTKEY`: If set, treat the encryption key string for |
| 160 | SEE as text to be hashed into the actual encryption key. This has no |
| 161 | effect if Fossil was not compiled with SEE support enabled. |
| 162 | |
| @@ -210,10 +220,14 @@ | |
| 220 | used as the location of the `~/.fossil` file. |
| 221 | |
| 222 | `LOGNAME`: Name of the logged in user on many Unix-like platforms. |
| 223 | Used as the fossil user name if `FOSSIL_USER` is not specified. See |
| 224 | the discussion of Fossil Username below for a lot more detail. |
| 225 | |
| 226 | `NO_COLOR`: If defined and not set to a `false` value (i.e. "off", "no", |
| 227 | "false", "0"), the `fossil search` command skips colorization of console |
| 228 | output using ANSI escape codes (VT100). |
| 229 | |
| 230 | `PATH`: Used by most platforms to locate programs invoked without a |
| 231 | fully qualified name. Explicitly used by `fossil ui` on certain platforms |
| 232 | to choose the browser to launch. |
| 233 | |
| @@ -464,12 +478,12 @@ | |
| 478 | |
| 479 | ### Web browser |
| 480 | |
| 481 | Occasionally, fossil wants to launch a web browser for the user, most |
| 482 | obviously as part of the `fossil ui` command. In that specific case, |
| 483 | the browser is launched pointing at the web server started by |
| 484 | `fossil ui` listening on a private TCP port. |
| 485 | |
| 486 | On all platforms, if the local or global settings `web-browser` is |
| 487 | set, that is the command used to open a URL. |
| 488 | |
| 489 | Otherwise, the specific actions vary by platform. |
| @@ -484,5 +498,6 @@ | |
| 498 | On Windows platforms, it assumes that `start` is the command to open |
| 499 | a URL in the user's configured default browser. |
| 500 | |
| 501 | [configdb]: ./tech_overview.wiki#configdb |
| 502 | [configloc]: ./tech_overview.wiki#configloc |
| 503 | [cgictlfile]: ./cgi.wiki |
| 504 |
+12
-5
| --- www/fileformat.wiki | ||
| +++ www/fileformat.wiki | ||
| @@ -61,11 +61,11 @@ | ||
| 61 | 61 | artifacts: |
| 62 | 62 | |
| 63 | 63 | <ul> |
| 64 | 64 | <li> [#manifest | Manifests] </li> |
| 65 | 65 | <li> [#cluster | Clusters] </li> |
| 66 | -<li> [#ctrl | Control Artifacts] </li> | |
| 66 | +<li> [#ctrl | Control (a.k.a. Tag) Artifacts] </li> | |
| 67 | 67 | <li> [#wikichng | Wiki Pages] </li> |
| 68 | 68 | <li> [#tktchng | Ticket Changes] </li> |
| 69 | 69 | <li> [#attachment | Attachments] </li> |
| 70 | 70 | <li> [#event | TechNotes] </li> |
| 71 | 71 | <li> [#forum | Forum Posts] </li> |
| @@ -173,11 +173,14 @@ | ||
| 173 | 173 | same file as it existed in the parent check-in. If the name of the |
| 174 | 174 | file is unchanged from its parent, then the 4th argument is omitted. |
| 175 | 175 | |
| 176 | 176 | A manifest has zero or one <b>N</b> cards. The <b>N</b> card specifies the mimetype for the |
| 177 | 177 | text in the comment of the <b>C</b> card. If the <b>N</b> card is omitted, a default mimetype |
| 178 | -is used. | |
| 178 | +is used. Note that the <b>N</b> card has never actually been used by | |
| 179 | +any Fossil implementation. The implementation has always interpreted | |
| 180 | +check-in comments according to the [/wiki_rules|Fossil Wiki formatting rules]. | |
| 181 | +There are no current plans to ever change that. | |
| 179 | 182 | |
| 180 | 183 | A manifest has zero or one <b>P</b> cards. Most manifests have one <b>P</b> card. |
| 181 | 184 | The <b>P</b> card has a varying number of arguments that |
| 182 | 185 | define other manifests from which the current manifest |
| 183 | 186 | is derived. Each argument is a lowercase |
| @@ -273,22 +276,26 @@ | ||
| 273 | 276 | prior cards in the cluster. The <b>Z</b> card is required. |
| 274 | 277 | |
| 275 | 278 | An example cluster from Fossil can be seen |
| 276 | 279 | [/artifact/d03dbdd73a2a8 | here]. |
| 277 | 280 | |
| 278 | -<h3 id="ctrl">2.3 Control Artifacts</h3> | |
| 281 | +<h3 id="ctrl">2.3 Control (a.k.a. Tag) Artifacts</h3> | |
| 279 | 282 | |
| 280 | 283 | Control artifacts are used to assign properties to other artifacts |
| 281 | -within the repository. | |
| 282 | -Allowed cards in a control artifact are as follows: | |
| 284 | +within the repository. Allowed cards in a control artifact are as | |
| 285 | +follows: | |
| 283 | 286 | |
| 284 | 287 | <div class="indent"> |
| 285 | 288 | <b>D</b> <i>time-and-date-stamp</i><br /> |
| 286 | 289 | <b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name</i> <i>artifact-id</i> ?<i>value</i>?<br /> |
| 287 | 290 | <b>U</b> <i>user-name</i><br /> |
| 288 | 291 | <b>Z</b> <i>checksum</i><br /> |
| 289 | 292 | </div> |
| 293 | + | |
| 294 | +Control articles are also referred to as Tag artifacts, but tags can | |
| 295 | +also be applied via other artifact types, as described in | |
| 296 | +[#summary|the Card Summary table]. | |
| 290 | 297 | |
| 291 | 298 | A control artifact must have one <b>D</b> card, one <b>U</b> card, one <b>Z</b> card and |
| 292 | 299 | one or more <b>T</b> cards. No other cards or other text is |
| 293 | 300 | allowed in a control artifact. Control artifacts might be PGP |
| 294 | 301 | clearsigned. |
| 295 | 302 |
| --- www/fileformat.wiki | |
| +++ www/fileformat.wiki | |
| @@ -61,11 +61,11 @@ | |
| 61 | artifacts: |
| 62 | |
| 63 | <ul> |
| 64 | <li> [#manifest | Manifests] </li> |
| 65 | <li> [#cluster | Clusters] </li> |
| 66 | <li> [#ctrl | Control Artifacts] </li> |
| 67 | <li> [#wikichng | Wiki Pages] </li> |
| 68 | <li> [#tktchng | Ticket Changes] </li> |
| 69 | <li> [#attachment | Attachments] </li> |
| 70 | <li> [#event | TechNotes] </li> |
| 71 | <li> [#forum | Forum Posts] </li> |
| @@ -173,11 +173,14 @@ | |
| 173 | same file as it existed in the parent check-in. If the name of the |
| 174 | file is unchanged from its parent, then the 4th argument is omitted. |
| 175 | |
| 176 | A manifest has zero or one <b>N</b> cards. The <b>N</b> card specifies the mimetype for the |
| 177 | text in the comment of the <b>C</b> card. If the <b>N</b> card is omitted, a default mimetype |
| 178 | is used. |
| 179 | |
| 180 | A manifest has zero or one <b>P</b> cards. Most manifests have one <b>P</b> card. |
| 181 | The <b>P</b> card has a varying number of arguments that |
| 182 | define other manifests from which the current manifest |
| 183 | is derived. Each argument is a lowercase |
| @@ -273,22 +276,26 @@ | |
| 273 | prior cards in the cluster. The <b>Z</b> card is required. |
| 274 | |
| 275 | An example cluster from Fossil can be seen |
| 276 | [/artifact/d03dbdd73a2a8 | here]. |
| 277 | |
| 278 | <h3 id="ctrl">2.3 Control Artifacts</h3> |
| 279 | |
| 280 | Control artifacts are used to assign properties to other artifacts |
| 281 | within the repository. |
| 282 | Allowed cards in a control artifact are as follows: |
| 283 | |
| 284 | <div class="indent"> |
| 285 | <b>D</b> <i>time-and-date-stamp</i><br /> |
| 286 | <b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name</i> <i>artifact-id</i> ?<i>value</i>?<br /> |
| 287 | <b>U</b> <i>user-name</i><br /> |
| 288 | <b>Z</b> <i>checksum</i><br /> |
| 289 | </div> |
| 290 | |
| 291 | A control artifact must have one <b>D</b> card, one <b>U</b> card, one <b>Z</b> card and |
| 292 | one or more <b>T</b> cards. No other cards or other text is |
| 293 | allowed in a control artifact. Control artifacts might be PGP |
| 294 | clearsigned. |
| 295 |
| --- www/fileformat.wiki | |
| +++ www/fileformat.wiki | |
| @@ -61,11 +61,11 @@ | |
| 61 | artifacts: |
| 62 | |
| 63 | <ul> |
| 64 | <li> [#manifest | Manifests] </li> |
| 65 | <li> [#cluster | Clusters] </li> |
| 66 | <li> [#ctrl | Control (a.k.a. Tag) Artifacts] </li> |
| 67 | <li> [#wikichng | Wiki Pages] </li> |
| 68 | <li> [#tktchng | Ticket Changes] </li> |
| 69 | <li> [#attachment | Attachments] </li> |
| 70 | <li> [#event | TechNotes] </li> |
| 71 | <li> [#forum | Forum Posts] </li> |
| @@ -173,11 +173,14 @@ | |
| 173 | same file as it existed in the parent check-in. If the name of the |
| 174 | file is unchanged from its parent, then the 4th argument is omitted. |
| 175 | |
| 176 | A manifest has zero or one <b>N</b> cards. The <b>N</b> card specifies the mimetype for the |
| 177 | text in the comment of the <b>C</b> card. If the <b>N</b> card is omitted, a default mimetype |
| 178 | is used. Note that the <b>N</b> card has never actually been used by |
| 179 | any Fossil implementation. The implementation has always interpreted |
| 180 | check-in comments according to the [/wiki_rules|Fossil Wiki formatting rules]. |
| 181 | There are no current plans to ever change that. |
| 182 | |
| 183 | A manifest has zero or one <b>P</b> cards. Most manifests have one <b>P</b> card. |
| 184 | The <b>P</b> card has a varying number of arguments that |
| 185 | define other manifests from which the current manifest |
| 186 | is derived. Each argument is a lowercase |
| @@ -273,22 +276,26 @@ | |
| 276 | prior cards in the cluster. The <b>Z</b> card is required. |
| 277 | |
| 278 | An example cluster from Fossil can be seen |
| 279 | [/artifact/d03dbdd73a2a8 | here]. |
| 280 | |
| 281 | <h3 id="ctrl">2.3 Control (a.k.a. Tag) Artifacts</h3> |
| 282 | |
| 283 | Control artifacts are used to assign properties to other artifacts |
| 284 | within the repository. Allowed cards in a control artifact are as |
| 285 | follows: |
| 286 | |
| 287 | <div class="indent"> |
| 288 | <b>D</b> <i>time-and-date-stamp</i><br /> |
| 289 | <b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name</i> <i>artifact-id</i> ?<i>value</i>?<br /> |
| 290 | <b>U</b> <i>user-name</i><br /> |
| 291 | <b>Z</b> <i>checksum</i><br /> |
| 292 | </div> |
| 293 | |
| 294 | Control articles are also referred to as Tag artifacts, but tags can |
| 295 | also be applied via other artifact types, as described in |
| 296 | [#summary|the Card Summary table]. |
| 297 | |
| 298 | A control artifact must have one <b>D</b> card, one <b>U</b> card, one <b>Z</b> card and |
| 299 | one or more <b>T</b> cards. No other cards or other text is |
| 300 | allowed in a control artifact. Control artifacts might be PGP |
| 301 | clearsigned. |
| 302 |
+33
-5
| --- www/serverext.wiki | ||
| +++ www/serverext.wiki | ||
| @@ -32,11 +32,11 @@ | ||
| 32 | 32 | If the Fossil server is itself run as |
| 33 | 33 | [./server/any/cgi.md|CGI], then add a line to the |
| 34 | 34 | [./cgi.wiki#extroot|CGI script file] that says: |
| 35 | 35 | |
| 36 | 36 | <pre> |
| 37 | -extroot: <i>DIRECTORY</i> | |
| 37 | + extroot: <i>DIRECTORY</i> | |
| 38 | 38 | </pre> |
| 39 | 39 | |
| 40 | 40 | Or, if the Fossil server is being run using the |
| 41 | 41 | "[./server/any/none.md|fossil server]" or |
| 42 | 42 | "[./server/any/none.md|fossil ui]" or |
| @@ -45,18 +45,21 @@ | ||
| 45 | 45 | |
| 46 | 46 | The <i>DIRECTORY</i> is the DOCUMENT_ROOT for the CGI. |
| 47 | 47 | Files in the DOCUMENT_ROOT are accessed via URLs like this: |
| 48 | 48 | |
| 49 | 49 | <pre> |
| 50 | -https://example-project.org/ext/<i>FILENAME</i> | |
| 50 | + https://example-project.org/ext/<i>FILENAME</i> | |
| 51 | 51 | </pre> |
| 52 | 52 | |
| 53 | 53 | In other words, access files in DOCUMENT_ROOT by appending the filename |
| 54 | 54 | relative to DOCUMENT_ROOT to the [/help?cmd=/ext|/ext] |
| 55 | 55 | page of the Fossil server. |
| 56 | -Files that are readable but not executable are returned as static | |
| 57 | -content. Files that are executable are run as CGI. | |
| 56 | + | |
| 57 | + * Files that are readable but not executable are returned as static | |
| 58 | + content. | |
| 59 | + | |
| 60 | + * Files that are executable are run as CGI. | |
| 58 | 61 | |
| 59 | 62 | <h3>2.1 Example #1</h3> |
| 60 | 63 | |
| 61 | 64 | The source code repository for SQLite is a Fossil server that is run |
| 62 | 65 | as CGI. The URL for the source code repository is [https://sqlite.org/src]. |
| @@ -117,16 +120,41 @@ | ||
| 117 | 120 | of [https://www.tcl.tk|Tcl/Tk] and so he tends to gravitate toward Tcl-based |
| 118 | 121 | technologies like Wapp.) The fileup1 script is a demo program that lets |
| 119 | 122 | the user upload a file using a form, and then displays that file in the reply. |
| 120 | 123 | There is a link on the page that causes the fileup1 script to return a copy |
| 121 | 124 | of its own source-code, so you can see how it works. |
| 125 | + | |
| 126 | +<h3>2.3 Example #3</h3> | |
| 127 | + | |
| 128 | +For Fossil versions dated 2025-03-23 and later, the "--extpage FILENAME" | |
| 129 | +option to the [/help?cmd=ui|fossil ui] command is a short cut that treats | |
| 130 | +FILENAME as a CGI extension. When the ui command starts up a new web browser | |
| 131 | +pages, it points that page to the FILENAME extension. So if FILENAME is | |
| 132 | +a static content file (such as an HTML file or | |
| 133 | +[/md_rules|Markdown] or [/wiki_rules|Wiki] document), then the | |
| 134 | +rendered content of the file is displayed. Meanwhile, the user can be | |
| 135 | +editing the source text for that document in a separate window, and | |
| 136 | +periodically pressing "Reload" on the web browser to instantly view the | |
| 137 | +rendered results. | |
| 138 | + | |
| 139 | +For example, the author of this documentation page is running | |
| 140 | +"<tt>fossil ui --extpage www/serverext.wiki</tt>" while editing this | |
| 141 | +very paragraph, and presses Reload from time to time to view his | |
| 142 | +edits. | |
| 143 | + | |
| 144 | +A same idea applies when developing new CGI applications using a script | |
| 145 | +language (for example using [https://wapp.tcl.tk|Wapp]). Run the | |
| 146 | +command "<tt>fossil ui --extpage SCRIPT</tt>" where SCRIPT is the name | |
| 147 | +of the application script, while editing that script in a separate | |
| 148 | +window, then press Reload periodically on the web browser to test the | |
| 149 | +script. | |
| 122 | 150 | |
| 123 | 151 | <h2 id="cgi-inputs">3.0 CGI Inputs</h2> |
| 124 | 152 | |
| 125 | 153 | The /ext extension mechanism is an ordinary CGI interface. Parameters |
| 126 | 154 | are passed to the CGI program using environment variables. The following |
| 127 | -standard CGI environment variables are supported: | |
| 155 | +standard CGI environment variables are supplied: | |
| 128 | 156 | |
| 129 | 157 | * AUTH_TYPE |
| 130 | 158 | * AUTH_CONTENT |
| 131 | 159 | * CONTENT_LENGTH |
| 132 | 160 | * CONTENT_TYPE |
| 133 | 161 |
| --- www/serverext.wiki | |
| +++ www/serverext.wiki | |
| @@ -32,11 +32,11 @@ | |
| 32 | If the Fossil server is itself run as |
| 33 | [./server/any/cgi.md|CGI], then add a line to the |
| 34 | [./cgi.wiki#extroot|CGI script file] that says: |
| 35 | |
| 36 | <pre> |
| 37 | extroot: <i>DIRECTORY</i> |
| 38 | </pre> |
| 39 | |
| 40 | Or, if the Fossil server is being run using the |
| 41 | "[./server/any/none.md|fossil server]" or |
| 42 | "[./server/any/none.md|fossil ui]" or |
| @@ -45,18 +45,21 @@ | |
| 45 | |
| 46 | The <i>DIRECTORY</i> is the DOCUMENT_ROOT for the CGI. |
| 47 | Files in the DOCUMENT_ROOT are accessed via URLs like this: |
| 48 | |
| 49 | <pre> |
| 50 | https://example-project.org/ext/<i>FILENAME</i> |
| 51 | </pre> |
| 52 | |
| 53 | In other words, access files in DOCUMENT_ROOT by appending the filename |
| 54 | relative to DOCUMENT_ROOT to the [/help?cmd=/ext|/ext] |
| 55 | page of the Fossil server. |
| 56 | Files that are readable but not executable are returned as static |
| 57 | content. Files that are executable are run as CGI. |
| 58 | |
| 59 | <h3>2.1 Example #1</h3> |
| 60 | |
| 61 | The source code repository for SQLite is a Fossil server that is run |
| 62 | as CGI. The URL for the source code repository is [https://sqlite.org/src]. |
| @@ -117,16 +120,41 @@ | |
| 117 | of [https://www.tcl.tk|Tcl/Tk] and so he tends to gravitate toward Tcl-based |
| 118 | technologies like Wapp.) The fileup1 script is a demo program that lets |
| 119 | the user upload a file using a form, and then displays that file in the reply. |
| 120 | There is a link on the page that causes the fileup1 script to return a copy |
| 121 | of its own source-code, so you can see how it works. |
| 122 | |
| 123 | <h2 id="cgi-inputs">3.0 CGI Inputs</h2> |
| 124 | |
| 125 | The /ext extension mechanism is an ordinary CGI interface. Parameters |
| 126 | are passed to the CGI program using environment variables. The following |
| 127 | standard CGI environment variables are supported: |
| 128 | |
| 129 | * AUTH_TYPE |
| 130 | * AUTH_CONTENT |
| 131 | * CONTENT_LENGTH |
| 132 | * CONTENT_TYPE |
| 133 |
| --- www/serverext.wiki | |
| +++ www/serverext.wiki | |
| @@ -32,11 +32,11 @@ | |
| 32 | If the Fossil server is itself run as |
| 33 | [./server/any/cgi.md|CGI], then add a line to the |
| 34 | [./cgi.wiki#extroot|CGI script file] that says: |
| 35 | |
| 36 | <pre> |
| 37 | extroot: <i>DIRECTORY</i> |
| 38 | </pre> |
| 39 | |
| 40 | Or, if the Fossil server is being run using the |
| 41 | "[./server/any/none.md|fossil server]" or |
| 42 | "[./server/any/none.md|fossil ui]" or |
| @@ -45,18 +45,21 @@ | |
| 45 | |
| 46 | The <i>DIRECTORY</i> is the DOCUMENT_ROOT for the CGI. |
| 47 | Files in the DOCUMENT_ROOT are accessed via URLs like this: |
| 48 | |
| 49 | <pre> |
| 50 | https://example-project.org/ext/<i>FILENAME</i> |
| 51 | </pre> |
| 52 | |
| 53 | In other words, access files in DOCUMENT_ROOT by appending the filename |
| 54 | relative to DOCUMENT_ROOT to the [/help?cmd=/ext|/ext] |
| 55 | page of the Fossil server. |
| 56 | |
| 57 | * Files that are readable but not executable are returned as static |
| 58 | content. |
| 59 | |
| 60 | * Files that are executable are run as CGI. |
| 61 | |
| 62 | <h3>2.1 Example #1</h3> |
| 63 | |
| 64 | The source code repository for SQLite is a Fossil server that is run |
| 65 | as CGI. The URL for the source code repository is [https://sqlite.org/src]. |
| @@ -117,16 +120,41 @@ | |
| 120 | of [https://www.tcl.tk|Tcl/Tk] and so he tends to gravitate toward Tcl-based |
| 121 | technologies like Wapp.) The fileup1 script is a demo program that lets |
| 122 | the user upload a file using a form, and then displays that file in the reply. |
| 123 | There is a link on the page that causes the fileup1 script to return a copy |
| 124 | of its own source-code, so you can see how it works. |
| 125 | |
| 126 | <h3>2.3 Example #3</h3> |
| 127 | |
| 128 | For Fossil versions dated 2025-03-23 and later, the "--extpage FILENAME" |
| 129 | option to the [/help?cmd=ui|fossil ui] command is a short cut that treats |
| 130 | FILENAME as a CGI extension. When the ui command starts up a new web browser |
| 131 | pages, it points that page to the FILENAME extension. So if FILENAME is |
| 132 | a static content file (such as an HTML file or |
| 133 | [/md_rules|Markdown] or [/wiki_rules|Wiki] document), then the |
| 134 | rendered content of the file is displayed. Meanwhile, the user can be |
| 135 | editing the source text for that document in a separate window, and |
| 136 | periodically pressing "Reload" on the web browser to instantly view the |
| 137 | rendered results. |
| 138 | |
| 139 | For example, the author of this documentation page is running |
| 140 | "<tt>fossil ui --extpage www/serverext.wiki</tt>" while editing this |
| 141 | very paragraph, and presses Reload from time to time to view his |
| 142 | edits. |
| 143 | |
| 144 | A same idea applies when developing new CGI applications using a script |
| 145 | language (for example using [https://wapp.tcl.tk|Wapp]). Run the |
| 146 | command "<tt>fossil ui --extpage SCRIPT</tt>" where SCRIPT is the name |
| 147 | of the application script, while editing that script in a separate |
| 148 | window, then press Reload periodically on the web browser to test the |
| 149 | script. |
| 150 | |
| 151 | <h2 id="cgi-inputs">3.0 CGI Inputs</h2> |
| 152 | |
| 153 | The /ext extension mechanism is an ordinary CGI interface. Parameters |
| 154 | are passed to the CGI program using environment variables. The following |
| 155 | standard CGI environment variables are supplied: |
| 156 | |
| 157 | * AUTH_TYPE |
| 158 | * AUTH_CONTENT |
| 159 | * CONTENT_LENGTH |
| 160 | * CONTENT_TYPE |
| 161 |
+3
-3
| --- www/settings.wiki | ||
| +++ www/settings.wiki | ||
| @@ -1,18 +1,18 @@ | ||
| 1 | 1 | <title>Fossil Settings</title> |
| 2 | 2 | |
| 3 | -<h2>Using Fossil Settings</h2> | |
| 3 | +<h1>Using Fossil Settings</h1> | |
| 4 | 4 | |
| 5 | 5 | Settings control the behaviour of fossil. They are set with the |
| 6 | 6 | <tt>fossil settings</tt> command, or through the web interface in |
| 7 | 7 | the Settings page in the Admin section. |
| 8 | 8 | |
| 9 | 9 | For a list of all settings, view the Settings page, or type |
| 10 | 10 | <tt>fossil help settings</tt> from the command line. |
| 11 | 11 | |
| 12 | 12 | |
| 13 | -<h3 id="repo">Repository settings</h3> | |
| 13 | +<h2 id="repo">1.0 Repository settings</h2> | |
| 14 | 14 | |
| 15 | 15 | Settings are set on a per-repository basis. When you clone a repository, |
| 16 | 16 | a subset of settings are copied to your local repository. |
| 17 | 17 | |
| 18 | 18 | If you make a change to a setting on your local repository, it is not |
| @@ -24,11 +24,11 @@ | ||
| 24 | 24 | will be used for all repositories cloned to your machine, unless |
| 25 | 25 | overridden explicitly in a particular repository. Global settings can be |
| 26 | 26 | set by using the <tt>-global</tt> option on the <tt>fossil settings</tt> |
| 27 | 27 | command. |
| 28 | 28 | |
| 29 | -<h3 id="versionable">"Versionable" settings</h3> | |
| 29 | +<h2 id="versionable">2.0 "Versionable" settings</h2> | |
| 30 | 30 | |
| 31 | 31 | Most of the settings control the behaviour of fossil on your local |
| 32 | 32 | machine, largely acting to reflect your preference on how you want to |
| 33 | 33 | use Fossil, how you communicate with the server, or options for hosting |
| 34 | 34 | a repository on the web. |
| 35 | 35 |
| --- www/settings.wiki | |
| +++ www/settings.wiki | |
| @@ -1,18 +1,18 @@ | |
| 1 | <title>Fossil Settings</title> |
| 2 | |
| 3 | <h2>Using Fossil Settings</h2> |
| 4 | |
| 5 | Settings control the behaviour of fossil. They are set with the |
| 6 | <tt>fossil settings</tt> command, or through the web interface in |
| 7 | the Settings page in the Admin section. |
| 8 | |
| 9 | For a list of all settings, view the Settings page, or type |
| 10 | <tt>fossil help settings</tt> from the command line. |
| 11 | |
| 12 | |
| 13 | <h3 id="repo">Repository settings</h3> |
| 14 | |
| 15 | Settings are set on a per-repository basis. When you clone a repository, |
| 16 | a subset of settings are copied to your local repository. |
| 17 | |
| 18 | If you make a change to a setting on your local repository, it is not |
| @@ -24,11 +24,11 @@ | |
| 24 | will be used for all repositories cloned to your machine, unless |
| 25 | overridden explicitly in a particular repository. Global settings can be |
| 26 | set by using the <tt>-global</tt> option on the <tt>fossil settings</tt> |
| 27 | command. |
| 28 | |
| 29 | <h3 id="versionable">"Versionable" settings</h3> |
| 30 | |
| 31 | Most of the settings control the behaviour of fossil on your local |
| 32 | machine, largely acting to reflect your preference on how you want to |
| 33 | use Fossil, how you communicate with the server, or options for hosting |
| 34 | a repository on the web. |
| 35 |
| --- www/settings.wiki | |
| +++ www/settings.wiki | |
| @@ -1,18 +1,18 @@ | |
| 1 | <title>Fossil Settings</title> |
| 2 | |
| 3 | <h1>Using Fossil Settings</h1> |
| 4 | |
| 5 | Settings control the behaviour of fossil. They are set with the |
| 6 | <tt>fossil settings</tt> command, or through the web interface in |
| 7 | the Settings page in the Admin section. |
| 8 | |
| 9 | For a list of all settings, view the Settings page, or type |
| 10 | <tt>fossil help settings</tt> from the command line. |
| 11 | |
| 12 | |
| 13 | <h2 id="repo">1.0 Repository settings</h2> |
| 14 | |
| 15 | Settings are set on a per-repository basis. When you clone a repository, |
| 16 | a subset of settings are copied to your local repository. |
| 17 | |
| 18 | If you make a change to a setting on your local repository, it is not |
| @@ -24,11 +24,11 @@ | |
| 24 | will be used for all repositories cloned to your machine, unless |
| 25 | overridden explicitly in a particular repository. Global settings can be |
| 26 | set by using the <tt>-global</tt> option on the <tt>fossil settings</tt> |
| 27 | command. |
| 28 | |
| 29 | <h2 id="versionable">2.0 "Versionable" settings</h2> |
| 30 | |
| 31 | Most of the settings control the behaviour of fossil on your local |
| 32 | machine, largely acting to reflect your preference on how you want to |
| 33 | use Fossil, how you communicate with the server, or options for hosting |
| 34 | a repository on the web. |
| 35 |
+12
-2
| --- www/th1.md | ||
| +++ www/th1.md | ||
| @@ -227,10 +227,11 @@ | ||
| 227 | 227 | * [unversioned list](#unversioned_list) |
| 228 | 228 | * [utime](#utime) |
| 229 | 229 | * [verifyCsrf](#verifyCsrf) |
| 230 | 230 | * [verifyLogin](#verifyLogin) |
| 231 | 231 | * [wiki](#wiki) |
| 232 | + * [wiki_assoc](#wiki_assoc) | |
| 232 | 233 | |
| 233 | 234 | Each of the commands above is documented by a block comment above their |
| 234 | 235 | implementation in the th\_main.c or th\_tcl.c source files. |
| 235 | 236 | |
| 236 | 237 | All commands starting with "tcl", with the exception of "tclReady", |
| @@ -271,11 +272,11 @@ | ||
| 271 | 272 | <a id="bireqjs"></a>TH1 builtin_request_js Command |
| 272 | 273 | -------------------------------------------------- |
| 273 | 274 | |
| 274 | 275 | * builtin_request_js NAME |
| 275 | 276 | |
| 276 | -NAME must be the name of one of the | |
| 277 | +NAME must be the name of one of the | |
| 277 | 278 | [built-in javascript source files](/dir?ci=trunk&type=flat&name=src&re=js$). |
| 278 | 279 | This command causes that javascript file to be appended to the delivered |
| 279 | 280 | document. |
| 280 | 281 | |
| 281 | 282 | |
| @@ -284,11 +285,11 @@ | ||
| 284 | 285 | ----------------------------------------------------- |
| 285 | 286 | |
| 286 | 287 | * capexpr CAPABILITY-EXPR |
| 287 | 288 | |
| 288 | 289 | The capability expression is a list. Each term of the list is a |
| 289 | -cluster of [capability letters](./caps/ref.html). | |
| 290 | +cluster of [capability letters](./caps/ref.html). | |
| 290 | 291 | The overall expression is true if any |
| 291 | 292 | one term is true. A single term is true if all letters within that |
| 292 | 293 | term are true. Or, if the term begins with "!", then the term is true |
| 293 | 294 | if none of the terms are true. Or, if the term begins with "@" then |
| 294 | 295 | the term is true if all of the capability letters in that term are |
| @@ -861,10 +862,19 @@ | ||
| 861 | 862 | ----------------------------------- |
| 862 | 863 | |
| 863 | 864 | * wiki STRING |
| 864 | 865 | |
| 865 | 866 | Renders STRING as wiki content. |
| 867 | + | |
| 868 | +<a id="wiki_assoc"></a>TH1 wiki_assoc Command | |
| 869 | +----------------------------------- | |
| 870 | + | |
| 871 | + * wiki_assoc STRING STRING | |
| 872 | + | |
| 873 | +Renders the special wiki. The first string refers to the namespace | |
| 874 | +(checkin, branch, tag, ticket). The second string specifies the | |
| 875 | +concrete wiki page to be rendered. | |
| 866 | 876 | |
| 867 | 877 | Tcl Integration Commands |
| 868 | 878 | ------------------------ |
| 869 | 879 | |
| 870 | 880 | When the Tcl integration subsystem is enabled, several commands are added |
| 871 | 881 | |
| 872 | 882 | ADDED www/title-test.md |
| 873 | 883 | ADDED www/title-test.wiki |
| --- www/th1.md | |
| +++ www/th1.md | |
| @@ -227,10 +227,11 @@ | |
| 227 | * [unversioned list](#unversioned_list) |
| 228 | * [utime](#utime) |
| 229 | * [verifyCsrf](#verifyCsrf) |
| 230 | * [verifyLogin](#verifyLogin) |
| 231 | * [wiki](#wiki) |
| 232 | |
| 233 | Each of the commands above is documented by a block comment above their |
| 234 | implementation in the th\_main.c or th\_tcl.c source files. |
| 235 | |
| 236 | All commands starting with "tcl", with the exception of "tclReady", |
| @@ -271,11 +272,11 @@ | |
| 271 | <a id="bireqjs"></a>TH1 builtin_request_js Command |
| 272 | -------------------------------------------------- |
| 273 | |
| 274 | * builtin_request_js NAME |
| 275 | |
| 276 | NAME must be the name of one of the |
| 277 | [built-in javascript source files](/dir?ci=trunk&type=flat&name=src&re=js$). |
| 278 | This command causes that javascript file to be appended to the delivered |
| 279 | document. |
| 280 | |
| 281 | |
| @@ -284,11 +285,11 @@ | |
| 284 | ----------------------------------------------------- |
| 285 | |
| 286 | * capexpr CAPABILITY-EXPR |
| 287 | |
| 288 | The capability expression is a list. Each term of the list is a |
| 289 | cluster of [capability letters](./caps/ref.html). |
| 290 | The overall expression is true if any |
| 291 | one term is true. A single term is true if all letters within that |
| 292 | term are true. Or, if the term begins with "!", then the term is true |
| 293 | if none of the terms are true. Or, if the term begins with "@" then |
| 294 | the term is true if all of the capability letters in that term are |
| @@ -861,10 +862,19 @@ | |
| 861 | ----------------------------------- |
| 862 | |
| 863 | * wiki STRING |
| 864 | |
| 865 | Renders STRING as wiki content. |
| 866 | |
| 867 | Tcl Integration Commands |
| 868 | ------------------------ |
| 869 | |
| 870 | When the Tcl integration subsystem is enabled, several commands are added |
| 871 | |
| 872 | DDED www/title-test.md |
| 873 | DDED www/title-test.wiki |
| --- www/th1.md | |
| +++ www/th1.md | |
| @@ -227,10 +227,11 @@ | |
| 227 | * [unversioned list](#unversioned_list) |
| 228 | * [utime](#utime) |
| 229 | * [verifyCsrf](#verifyCsrf) |
| 230 | * [verifyLogin](#verifyLogin) |
| 231 | * [wiki](#wiki) |
| 232 | * [wiki_assoc](#wiki_assoc) |
| 233 | |
| 234 | Each of the commands above is documented by a block comment above their |
| 235 | implementation in the th\_main.c or th\_tcl.c source files. |
| 236 | |
| 237 | All commands starting with "tcl", with the exception of "tclReady", |
| @@ -271,11 +272,11 @@ | |
| 272 | <a id="bireqjs"></a>TH1 builtin_request_js Command |
| 273 | -------------------------------------------------- |
| 274 | |
| 275 | * builtin_request_js NAME |
| 276 | |
| 277 | NAME must be the name of one of the |
| 278 | [built-in javascript source files](/dir?ci=trunk&type=flat&name=src&re=js$). |
| 279 | This command causes that javascript file to be appended to the delivered |
| 280 | document. |
| 281 | |
| 282 | |
| @@ -284,11 +285,11 @@ | |
| 285 | ----------------------------------------------------- |
| 286 | |
| 287 | * capexpr CAPABILITY-EXPR |
| 288 | |
| 289 | The capability expression is a list. Each term of the list is a |
| 290 | cluster of [capability letters](./caps/ref.html). |
| 291 | The overall expression is true if any |
| 292 | one term is true. A single term is true if all letters within that |
| 293 | term are true. Or, if the term begins with "!", then the term is true |
| 294 | if none of the terms are true. Or, if the term begins with "@" then |
| 295 | the term is true if all of the capability letters in that term are |
| @@ -861,10 +862,19 @@ | |
| 862 | ----------------------------------- |
| 863 | |
| 864 | * wiki STRING |
| 865 | |
| 866 | Renders STRING as wiki content. |
| 867 | |
| 868 | <a id="wiki_assoc"></a>TH1 wiki_assoc Command |
| 869 | ----------------------------------- |
| 870 | |
| 871 | * wiki_assoc STRING STRING |
| 872 | |
| 873 | Renders the special wiki. The first string refers to the namespace |
| 874 | (checkin, branch, tag, ticket). The second string specifies the |
| 875 | concrete wiki page to be rendered. |
| 876 | |
| 877 | Tcl Integration Commands |
| 878 | ------------------------ |
| 879 | |
| 880 | When the Tcl integration subsystem is enabled, several commands are added |
| 881 | |
| 882 | DDED www/title-test.md |
| 883 | DDED www/title-test.wiki |
+12
| --- a/www/title-test.md | ||
| +++ b/www/title-test.md | ||
| @@ -0,0 +1,12 @@ | ||
| 1 | +# Markdown Doc Title > & " ' < Test | |
| 2 | + | |
| 3 | +Test of unusual characters in the title of Markdown formatted documents. | |
| 4 | +The title should read: | |
| 5 | + | |
| 6 | +> Markdown Doc Title > & " ' < Test | |
| 7 | + | |
| 8 | +See also: | |
| 9 | + | |
| 10 | + * [](/doc/trunk/www/title-test.wiki) | |
| 11 | + * [](/wiki?name=Test+Wiki+>+%26+%22+%27+%3c+Title&p) | |
| 12 | + * [](/forumpost/481ab1f9) |
| --- a/www/title-test.md | |
| +++ b/www/title-test.md | |
| @@ -0,0 +1,12 @@ | |
| --- a/www/title-test.md | |
| +++ b/www/title-test.md | |
| @@ -0,0 +1,12 @@ | |
| 1 | # Markdown Doc Title > & " ' < Test |
| 2 | |
| 3 | Test of unusual characters in the title of Markdown formatted documents. |
| 4 | The title should read: |
| 5 | |
| 6 | > Markdown Doc Title > & " ' < Test |
| 7 | |
| 8 | See also: |
| 9 | |
| 10 | * [](/doc/trunk/www/title-test.wiki) |
| 11 | * [](/wiki?name=Test+Wiki+>+%26+%22+%27+%3c+Title&p) |
| 12 | * [](/forumpost/481ab1f9) |
+14
| --- a/www/title-test.wiki | ||
| +++ b/www/title-test.wiki | ||
| @@ -0,0 +1,14 @@ | ||
| 1 | +<title>Wiki Doc Title > & " ' < Test</title> | |
| 2 | + | |
| 3 | +Test of unusual characters in the title of Fossil-wiki formatted documents. | |
| 4 | +The title should read: | |
| 5 | + | |
| 6 | +<big><b><verbatim> | |
| 7 | + Wiki Doc Title > & " ' < Test | |
| 8 | +</verbatim></b></big> | |
| 9 | + | |
| 10 | +See also: | |
| 11 | + | |
| 12 | + * [/doc/trunk/www/title-test.md] | |
| 13 | + * [/wiki?name=Test+Wiki+>+%26+%22+%27+%3c+Title&p] | |
| 14 | + * [/forumpost/481ab1f9] |
| --- a/www/title-test.wiki | |
| +++ b/www/title-test.wiki | |
| @@ -0,0 +1,14 @@ | |
| --- a/www/title-test.wiki | |
| +++ b/www/title-test.wiki | |
| @@ -0,0 +1,14 @@ | |
| 1 | <title>Wiki Doc Title > & " ' < Test</title> |
| 2 | |
| 3 | Test of unusual characters in the title of Fossil-wiki formatted documents. |
| 4 | The title should read: |
| 5 | |
| 6 | <big><b><verbatim> |
| 7 | Wiki Doc Title > & " ' < Test |
| 8 | </verbatim></b></big> |
| 9 | |
| 10 | See also: |
| 11 | |
| 12 | * [/doc/trunk/www/title-test.md] |
| 13 | * [/wiki?name=Test+Wiki+>+%26+%22+%27+%3c+Title&p] |
| 14 | * [/forumpost/481ab1f9] |