Fossil SCM

Merge trunk into the merge-info-trunk branch.

stephan 2025-03-27 12:42 merge-info-html merge
Commit 1014ffb553117ca4113dcc5071d3389fe3c664eb5522dd634aab91d69b0e876e
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 @@
11
compat/zlib/*
22
setup/fossil.iss
33
test/th1-docs-input.txt
44
test/th1-hooks-input.txt
5
+win/build32.bat
6
+win/build64.bat
57
win/buildmsvc.bat
68
--- .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 @@
55
fossil
66
fossil.exe
77
win/fossil.exe
88
*shell-see.*
99
*sqlite3-see.*
10
+bld
1011
--- .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

+587 -525
--- auto.def
+++ auto.def
@@ -36,17 +36,17 @@
3636
}
3737
3838
# Update the minimum required SQLite version number here, and also
3939
# in src/main.c near the sqlite3_libversion_number() call. Take care
4040
# that both places agree!
41
-define MINIMUM_SQLITE_VERSION "3.46.0"
41
+define MINIMUM_SQLITE_VERSION "3.49.0"
4242
4343
# This is useful for people wanting Fossil to use an external SQLite library
4444
# to compare the one they have against the minimum required
4545
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
4848
}
4949
5050
# Space characters have never been allowed in either the source
5151
# tree nor the build directory. But the resulting error messages
5252
# could be confusing. The following checks make the reason for the
@@ -67,21 +67,21 @@
6767
set outOfTreeBuild 1
6868
}
6969
7070
# sqlite wants these types if possible
7171
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
7373
}
7474
7575
# Use pread/pwrite system calls in place of seek + read/write if possible
7676
define USE_PREAD [cc-check-functions pread]
7777
7878
# If we have cscope here, we'll use it in the "tags" target
7979
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\]"
8181
} else {
82
- define COLLECT_CSCOPE_DATA ""
82
+ define COLLECT_CSCOPE_DATA ""
8383
}
8484
8585
# Find tclsh for the test suite.
8686
#
8787
# We can't use jimsh for this: the test suite uses features of Tcl that
@@ -94,32 +94,32 @@
9494
# Ironically, this means we may right now be running under either jimsh0
9595
# or a version of tclsh that we find unsuitable below!
9696
cc-check-progs tclsh
9797
set hbtd /usr/local/Cellar/tcl-tk
9898
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
+ }
121121
}
122122
123123
define CFLAGS [get-env CFLAGS "-g -Os"]
124124
define EXTRA_CFLAGS "-Wall"
125125
define EXTRA_LDFLAGS ""
@@ -133,48 +133,48 @@
133133
# SQLITE_OPTIONS_EXT => build-dependent CFLAGS for sqlite3.c and shell.c
134134
135135
# Maintain the C89/C90-style order of variable declarations before statements.
136136
# Check if the compiler supports the respective warning flag.
137137
if {[cctest -cflags -Wdeclaration-after-statement]} {
138
- define-append EXTRA_CFLAGS -Wdeclaration-after-statement
138
+ define-append EXTRA_CFLAGS -Wdeclaration-after-statement
139139
}
140140
141141
142142
# This procedure is a customized version of "cc-check-function-in-lib",
143143
# that does not modify the LIBS variable. Its use prevents prematurely
144144
# pulling in libraries that will be added later anyhow (e.g. "-ldl").
145145
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
176176
}
177177
178178
if {![opt-bool internal-sqlite]} {
179179
proc find_system_sqlite {} {
180180
@@ -219,17 +219,17 @@
219219
set sqlite-version [string cat "-D MINIMUM_SQLITE_VERSION=" [get-define MINIMUM_SQLITE_VERSION]]
220220
lappend cmdline {*}[set sqlite-version]
221221
set ok 1
222222
set err [catch {exec-with-stderr {*}$cmdline} result errinfo]
223223
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
228228
} elseif {$::autosetup(debug)} {
229
- configlog "Compiled OK: [join $cmdline]"
230
- configlog "============"
229
+ configlog "Compiled OK: [join $cmdline]"
230
+ configlog "============"
231231
}
232232
if {!$ok} {
233233
user-error "unable to compile SQLite compatibility test program"
234234
}
235235
set err [catch {exec-with-stderr ./conftest__} result errinfo]
@@ -251,475 +251,516 @@
251251
![file exists "/dev/null"]
252252
}]
253253
}
254254
255255
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"
288288
}
289289
290290
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"
295295
}
296296
297297
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"
305305
}
306306
307307
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"
311311
}
312312
313313
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"
317317
}
318318
319319
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"
323323
}
324324
325325
#if {[opt-bool markdown]} {
326326
# # no-op. Markdown is now enabled by default.
327327
# msg-result "Markdown support enabled"
328328
#}
329329
330330
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"
334334
} 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
337337
}
338338
339339
# Check for libraries that need to be sorted out early
340340
cc-check-function-in-lib iconv iconv
341341
342
+cc-check-function-in-lib sin m
343
+cc-check-function-in-lib dlopen dl
344
+
342345
# Helper for OpenSSL checking
343346
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
+#
377381
# 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"} {
438456
set found 0
439457
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]
449467
} 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 \
458477
/usr/pkg /usr/local /usr /usr/local/opt/openssl \
459478
/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
+ }
488517
}
489518
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 {}
524561
525562
########################################################################
526563
# --with-sqlite=PATH checks for the first it finds of the following...
527564
# - PATH/sqlite3.c and PATH/sqlite3.h
528565
# - PATH/sqlite3.o (and assumes sqlite3.h is with it)
529566
# - 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
713754
714755
# Network functions require libraries on some systems
715756
cc-check-function-in-lib gethostbyname nsl
716757
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
+ }
721762
}
722763
723764
# Some systems (ex: SunOS) require -lrt in order to use nanosleep
724765
cc-check-function-in-lib nanosleep rt
725766
@@ -734,11 +775,11 @@
734775
[cc-check-function-in-lib __ns_name_uncompress {bind resolv}]) &&
735776
([cc-check-function-in-lib ns_parserr {bind resolv}] ||
736777
[cc-check-function-in-lib __ns_parserr {bind resolv}]) &&
737778
([cc-check-function-in-lib res_query {bind resolv}] ||
738779
[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."
740781
}
741782
cc-check-function-in-lib res_9_ns_initparse resolv
742783
743784
# Other nonstandard function checks
744785
cc-check-functions utime
@@ -749,12 +790,12 @@
749790
750791
# Termux on Android adds "getpass(char *)" to unistd.h, so check this so we
751792
# guard against including it again; use cctest as cc-check-functions and
752793
# cctest_function check for "getpass()" with no args and fail
753794
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"
756797
}
757798
758799
# Check for getloadavg(), and if it doesn't exist, define FOSSIL_OMIT_LOAD_AVERAGE
759800
if {![cc-check-functions getloadavg] ||
760801
![cctest -link 1 -includes {unistd.h} -code "double a\[3\]; getloadavg(a,3);"]} {
@@ -762,44 +803,43 @@
762803
msg-result "Load average support unavailable"
763804
}
764805
765806
# Check for getpassphrase() for Solaris 10 where getpass() truncates to 10 chars
766807
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
769810
}
770
-cc-check-function-in-lib sin m
771811
772812
# Check for the FuseFS library
773813
if {[opt-bool fusefs]} {
774814
if {[opt-bool static]} {
775
- msg-result "FuseFS support disabled due to -static"
815
+ msg-result "FuseFS support disabled due to -static"
776816
} 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"
780820
}
781821
}
782822
783823
########################################################################
784824
# Checks the compiler for compile_commands.json support.
785825
#
786826
# Returns 1 if supported, else 0. Defines MAKE_COMPILATION_DB to "yes"
787827
# if supported, "no" if not.
788828
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
+ }
801841
}
802842
803843
define MAKE_COMPILATION_DB no
804844
if {!$outOfTreeBuild} {
805845
if {[opt-bool compile-commands]} {
@@ -816,32 +856,18 @@
816856
# Add -fsanitize compile and link options late: we don't want the C
817857
# checks above to run with those sanitizers enabled. It can not only
818858
# be pointless, it can actually break correct tests.
819859
set fsan [opt-val with-sanitizer]
820860
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
+}
843869
844870
########################################################################
845871
# @proj-check-emsdk
846872
#
847873
# Emscripten is used for doing in-tree builds of web-based WASM stuff,
@@ -918,10 +944,46 @@
918944
} else {
919945
define EMCC_WRAPPER ""
920946
define EMCC_OPT ""
921947
catch {exec rm -f tools/emcc.sh}
922948
}
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
+}}
923985
924986
# Tag container builds with a prefix of the checkin ID of the version
925987
# of Fossil each one contains. This not only allows multiple images
926988
# to coexist and multiple containers to be created unamgiguosly from
927989
# them, it also changes the URL we fetch the source tarball from, so
928990
--- 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
--- autosetup/local.tcl
+++ autosetup/local.tcl
@@ -27,5 +27,35 @@
2727
set tclconfig($name) [string trim $value ']
2828
}
2929
}
3030
return [array get tclconfig]
3131
}
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
+}
3262
3363
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
--- 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(&current) == 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(&current) == 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 @@
892892
const char *prompt;
893893
stringbuf *capture; /* capture buffer, or NULL for none. Always null terminated */
894894
stringbuf *output; /* used only during refreshLine() - output accumulator */
895895
#if defined(USE_TERMIOS)
896896
int fd; /* Terminal fd */
897
+ int pending; /* pending char fd_read_char() */
897898
#elif defined(USE_WINCONSOLE)
898899
HANDLE outh; /* Console output handle */
899900
HANDLE inh; /* Console input handle */
900901
int rows; /* Screen rows */
901902
int x; /* Current column during output */
@@ -1121,28 +1122,31 @@
11211122
raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
11221123
/* control chars - set return condition: min number of bytes and timer.
11231124
* We want read to return every single byte, without timeout. */
11241125
raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
11251126
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) {
11281132
goto fatal;
11291133
}
11301134
rawmode = 1;
11311135
return 0;
11321136
}
11331137
11341138
static void disableRawMode(struct current *current) {
11351139
/* 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)
11371141
rawmode = 0;
11381142
}
11391143
11401144
/* At exit we'll try to fix the terminal to the initial conditions. */
11411145
static void linenoiseAtExit(void) {
11421146
if (rawmode) {
1143
- tcsetattr(STDIN_FILENO, TCSADRAIN, &orig_termios);
1147
+ tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios);
11441148
}
11451149
linenoiseHistoryFree();
11461150
}
11471151
11481152
/* gcc/glibc insists that we care about the return code of write!
@@ -1230,29 +1234,35 @@
12301234
{
12311235
IGNORE_RC(write(STDOUT_FILENO, "\x1b[H\x1b[2J", 7));
12321236
}
12331237
12341238
/**
1235
- * Reads a char from 'fd', waiting at most 'timeout' milliseconds.
1239
+ * Reads a char from 'current->fd', waiting at most 'timeout' milliseconds.
12361240
*
12371241
* A timeout of -1 means to wait forever.
12381242
*
12391243
* Returns -1 if no char is received within the time or an error occurs.
12401244
*/
1241
-static int fd_read_char(int fd, int timeout)
1245
+static int fd_read_char(struct current *current, int timeout)
12421246
{
12431247
struct pollfd p;
12441248
unsigned char c;
12451249
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;
12471257
p.events = POLLIN;
12481258
12491259
if (poll(&p, 1, timeout) == 0) {
12501260
/* timeout */
12511261
return -1;
12521262
}
1253
- if (read(fd, &c, 1) != 1) {
1263
+ if (read(current->fd, &c, 1) != 1) {
12541264
return -1;
12551265
}
12561266
return c;
12571267
}
12581268
@@ -1266,11 +1276,15 @@
12661276
char buf[MAX_UTF8_LEN];
12671277
int n;
12681278
int i;
12691279
int c;
12701280
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) {
12721286
return -1;
12731287
}
12741288
n = utf8_charlen(buf[0]);
12751289
if (n < 1) {
12761290
return -1;
@@ -1282,11 +1296,11 @@
12821296
}
12831297
/* decode and return the character */
12841298
utf8_tounicode(buf, &c);
12851299
return c;
12861300
#else
1287
- return fd_read_char(current->fd, -1);
1301
+ return fd_read_char(current, -1);
12881302
#endif
12891303
}
12901304
12911305
12921306
/**
@@ -1295,20 +1309,29 @@
12951309
*/
12961310
static int queryCursor(struct current *current, int* cols)
12971311
{
12981312
struct esc_parser parser;
12991313
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
+ }
13001323
13011324
/* Should not be buffering this output, it needs to go immediately */
13021325
assert(current->output == NULL);
13031326
13041327
/* control sequence - report cursor location */
13051328
outputChars(current, "\x1b[6n", -1);
13061329
13071330
/* Parse the response: ESC [ rows ; cols R */
13081331
initParseEscapeSeq(&parser, 'R');
1309
- while ((ch = fd_read_char(current->fd, 100)) > 0) {
1332
+ while ((ch = fd_read_char(current, 100)) > 0) {
13101333
switch (parseEscapeSequence(&parser, ch)) {
13111334
default:
13121335
continue;
13131336
case EP_END:
13141337
if (parser.numprops == 2 && parser.props[1] < 1000) {
@@ -1315,15 +1338,18 @@
13151338
*cols = parser.props[1];
13161339
return 1;
13171340
}
13181341
break;
13191342
case EP_ERROR:
1343
+ /* Push back the character that caused the error */
1344
+ current->pending = ch;
13201345
break;
13211346
}
13221347
/* failed */
13231348
break;
13241349
}
1350
+ query_cursor_failed = 1;
13251351
return 0;
13261352
}
13271353
13281354
/**
13291355
* Updates current->cols with the current window size (width)
@@ -1386,13 +1412,13 @@
13861412
* Returns SPECIAL_NONE if unrecognised, or -1 if EOF.
13871413
*
13881414
* If no additional char is received within a short time,
13891415
* CHAR_ESCAPE is returned.
13901416
*/
1391
-static int check_special(int fd)
1417
+static int check_special(struct current *current)
13921418
{
1393
- int c = fd_read_char(fd, 50);
1419
+ int c = fd_read_char(current, 50);
13941420
int c2;
13951421
13961422
if (c < 0) {
13971423
return CHAR_ESCAPE;
13981424
}
@@ -1399,11 +1425,11 @@
13991425
else if (c >= 'a' && c <= 'z') {
14001426
/* esc-a => meta-a */
14011427
return meta(c);
14021428
}
14031429
1404
- c2 = fd_read_char(fd, 50);
1430
+ c2 = fd_read_char(current, 50);
14051431
if (c2 < 0) {
14061432
return c2;
14071433
}
14081434
if (c == '[' || c == 'O') {
14091435
/* Potential arrow key */
@@ -1422,11 +1448,11 @@
14221448
return SPECIAL_HOME;
14231449
}
14241450
}
14251451
if (c == '[' && c2 >= '1' && c2 <= '8') {
14261452
/* extended escape */
1427
- c = fd_read_char(fd, 50);
1453
+ c = fd_read_char(current, 50);
14281454
if (c == '~') {
14291455
switch (c2) {
14301456
case '2':
14311457
return SPECIAL_INSERT;
14321458
case '3':
@@ -1441,11 +1467,11 @@
14411467
return SPECIAL_END;
14421468
}
14431469
}
14441470
while (c != -1 && c != '~') {
14451471
/* .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);
14471473
}
14481474
}
14491475
14501476
return SPECIAL_NONE;
14511477
}
@@ -2234,11 +2260,11 @@
22342260
}
22352261
continue;
22362262
}
22372263
#ifdef USE_TERMIOS
22382264
if (c == CHAR_ESCAPE) {
2239
- c = check_special(current->fd);
2265
+ c = check_special(current);
22402266
}
22412267
#endif
22422268
if (c == ctrl('R')) {
22432269
/* Search for the previous (earlier) match */
22442270
if (searchpos > 0) {
@@ -2346,11 +2372,11 @@
23462372
/* go on to process the returned char normally */
23472373
}
23482374
23492375
#ifdef USE_TERMIOS
23502376
if (c == CHAR_ESCAPE) { /* escape sequence */
2351
- c = check_special(current->fd);
2377
+ c = check_special(current);
23522378
}
23532379
#endif
23542380
if (c == -1) {
23552381
/* Return on errors */
23562382
return sb_len(current->buf);
23572383
--- 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
--- extsrc/pikchr-worker.js
+++ extsrc/pikchr-worker.js
@@ -36,11 +36,11 @@
3636
pikchr: source code for the pikchr,
3737
darkMode: boolean true to adjust colors for a dark color scheme,
3838
cssClass: CSS class name to add to the SVG
3939
}
4040
41
- Workers-to-Main types
41
+ Workers-to-Main message types:
4242
4343
- stdout, stderr: indicate stdout/stderr output from the wasm
4444
layer. The data property is the string of the output, noting
4545
that the emscripten binding emits these one line at a time. Thus,
4646
if a C-side puts() emits multiple lines in a single call, the JS
@@ -50,21 +50,21 @@
5050
5151
- module: Status text. This is intended to alert the main thread
5252
about module loading status so that, e.g., the main thread can
5353
update a progress widget and DTRT when the module is finished
5454
loading and available for work. Status messages come in the form
55
-
55
+
5656
{type:'module', data:{
5757
type:'status',
5858
data: {text:string|null, step:1-based-integer}
5959
}
6060
6161
with an incrementing step value for each subsequent message. When
6262
the module loading is complete, a message with a text value of
6363
null is posted.
6464
65
- - pikchr:
65
+ - pikchr:
6666
6767
{type: 'pikchr',
6868
data:{
6969
pikchr: input text,
7070
result: rendered result (SVG on success, HTML on error),
@@ -162,11 +162,11 @@
162162
}
163163
return;
164164
};
165165
console.warn("Unknown pikchr-worker message type:",ev);
166166
};
167
-
167
+
168168
/**
169169
emscripten module for use with build mode -sMODULARIZE.
170170
*/
171171
const pikchrModule = {
172172
print: function(){wMsg('stdout', Array.prototype.slice.call(arguments));},
@@ -206,16 +206,17 @@
206206
data:{step: ++f.last.step, text: text||null}
207207
});
208208
}
209209
};
210210
211
- importScripts('pikchr.js');
211
+ importScripts('pikchr-v2813665466.js');
212212
/**
213213
initPikchrModule() is installed via pikchr.js due to
214214
building with:
215215
216216
emcc ... -sMODULARIZE=1 -sEXPORT_NAME=initPikchrModule
217217
*/
218218
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'));
220221
});
221222
})();
222223
--- 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 @@
11
/* This file is automatically generated by Lemon from input grammar
22
** source file "pikchr.y".
33
*/
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
+
443
/*
544
** Zero-Clause BSD license:
645
**
746
** Copyright (C) 2020-09-01 by D. Richard Hipp <[email protected]>
847
**
@@ -125,10 +164,22 @@
125164
#include <assert.h>
126165
#define count(X) (sizeof(X)/sizeof(X[0]))
127166
#ifndef M_PI
128167
# define M_PI 3.1415926535897932385
129168
#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
+
130181
131182
/* Limit the number of tokens in a single script to avoid run-away
132183
** macro expansion attacks. See forum post
133184
** https://pikchr.org/home/forumpost/ef8684c6955a411a
134185
*/
@@ -474,11 +525,11 @@
474525
static void pik_bbox_addbox(PBox*,PBox*);
475526
static void pik_bbox_add_xy(PBox*,PNum,PNum);
476527
static void pik_bbox_addellipse(PBox*,PNum x,PNum y,PNum rx,PNum ry);
477528
static void pik_add_txt(Pik*,PToken*,int);
478529
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);
480531
static int pik_text_position(int,PToken*);
481532
static PNum pik_property_of(PObj*,PToken*);
482533
static PNum pik_func(Pik*,PToken*,PNum,PNum);
483534
static PPoint pik_position_between(PNum x, PPoint p1, PPoint p2);
484535
static PPoint pik_position_at_angle(PNum dist, PNum r, PPoint pt);
@@ -492,11 +543,11 @@
492543
static PObj *pik_position_assert(Pik*,PPoint*,PToken*,PPoint*);
493544
static PNum pik_dist(PPoint*,PPoint*);
494545
static void pik_add_macro(Pik*,PToken *pId,PToken *pCode);
495546
496547
497
-#line 523 "pikchr.c"
548
+#line 549 "pikchr.c"
498549
/**************** End of %include directives **********************************/
499550
/* These constants specify the various numeric values for terminal symbols.
500551
***************** Begin token definitions *************************************/
501552
#ifndef T_ID
502553
#define T_ID 1
@@ -522,84 +573,85 @@
522573
#define T_COLOR 21
523574
#define T_THICKNESS 22
524575
#define T_PRINT 23
525576
#define T_STRING 24
526577
#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
601653
#endif
602654
/**************** End token definitions ***************************************/
603655
604656
/* The next sections is a series of control #defines.
605657
** various aspects of the generated parser.
@@ -660,22 +712,22 @@
660712
#ifndef INTERFACE
661713
# define INTERFACE 1
662714
#endif
663715
/************* Begin control #defines *****************************************/
664716
#define YYCODETYPE unsigned char
665
-#define YYNOCODE 136
717
+#define YYNOCODE 138
666718
#define YYACTIONTYPE unsigned short int
667719
#define pik_parserTOKENTYPE PToken
668720
typedef union {
669721
int yyinit;
670722
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;
677729
} YYMINORTYPE;
678730
#ifndef YYSTACKDEPTH
679731
#define YYSTACKDEPTH 100
680732
#endif
681733
#define pik_parserARG_SDECL
@@ -693,21 +745,21 @@
693745
#define pik_parserCTX_STORE yypParser->p=p;
694746
#define YYFALLBACK 1
695747
#define YYNSTATE 164
696748
#define YYNRULE 156
697749
#define YYNRULE_WITH_ACTION 116
698
-#define YYNTOKEN 100
750
+#define YYNTOKEN 101
699751
#define YY_MAX_SHIFT 163
700752
#define YY_MIN_SHIFTREDUCE 287
701753
#define YY_MAX_SHIFTREDUCE 442
702754
#define YY_ERROR_ACTION 443
703755
#define YY_ACCEPT_ACTION 444
704756
#define YY_NO_ACTION 445
705757
#define YY_MIN_REDUCE 446
706758
#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
709761
/************* End control #defines *******************************************/
710762
#define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])))
711763
712764
/* Define the yytestcase() macro to be a no-op if is not already defined
713765
** otherwise.
@@ -786,324 +838,322 @@
786838
** yy_reduce_ofst[] For each state, the offset into yy_action for
787839
** shifting non-terminals after a reduce.
788840
** yy_default[] Default action for each state.
789841
**
790842
*********** Begin parsing tables **********************************************/
791
-#define YY_ACTTAB_COUNT (1313)
843
+#define YY_ACTTAB_COUNT (1305)
792844
static const YYACTIONTYPE yy_action[] = {
793845
/* 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,
925976
};
926977
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,
932983
/* 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,
10691119
};
10701120
#define YY_SHIFT_COUNT (163)
10711121
#define YY_SHIFT_MIN (0)
1072
-#define YY_SHIFT_MAX (1113)
1122
+#define YY_SHIFT_MAX (884)
10731123
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,
10911141
};
10921142
#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)
10951145
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,
11051155
};
11061156
static const YYACTIONTYPE yy_default[] = {
11071157
/* 0 */ 449, 443, 443, 443, 443, 443, 443, 443, 443, 443,
11081158
/* 10 */ 443, 443, 443, 443, 443, 443, 443, 443, 443, 443,
11091159
/* 20 */ 443, 443, 443, 443, 443, 443, 443, 443, 473, 576,
@@ -1164,10 +1214,11 @@
11641214
0, /* COLOR => nothing */
11651215
0, /* THICKNESS => nothing */
11661216
0, /* PRINT => nothing */
11671217
0, /* STRING => nothing */
11681218
0, /* COMMA => nothing */
1219
+ 0, /* ISODATE => nothing */
11691220
0, /* CLASSNAME => nothing */
11701221
0, /* LB => nothing */
11711222
0, /* RB => nothing */
11721223
0, /* UP => nothing */
11731224
0, /* DOWN => nothing */
@@ -1347,120 +1398,122 @@
13471398
/* 21 */ "COLOR",
13481399
/* 22 */ "THICKNESS",
13491400
/* 23 */ "PRINT",
13501401
/* 24 */ "STRING",
13511402
/* 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",
14621515
};
14631516
#endif /* defined(YYCOVERAGE) || !defined(NDEBUG) */
14641517
14651518
#ifndef NDEBUG
14661519
/* For tracing reduce actions, the names of all rules are required.
@@ -1743,24 +1796,24 @@
17431796
** Note: during a reduce, the only symbols destroyed are those
17441797
** which appear on the RHS of the rule, but which are *not* used
17451798
** inside the C code.
17461799
*/
17471800
/********* 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"
17621815
}
17631816
break;
17641817
/********* End destructor definitions *****************************************/
17651818
default: break; /* If no destructor action specified: do nothing */
17661819
}
@@ -1991,14 +2044,14 @@
19912044
#endif
19922045
while( yypParser->yytos>yypParser->yystack ) yy_pop_parser_stack(yypParser);
19932046
/* Here code is inserted which will execute if the parser
19942047
** stack every overflows */
19952048
/******** Begin %stack_overflow code ******************************************/
1996
-#line 545 "pikchr.y"
2049
+#line 559 "pikchr.y"
19972050
19982051
pik_error(p, 0, "parser stack overflow");
1999
-#line 2024 "pikchr.c"
2052
+#line 2052 "pikchr.c"
20002053
/******** End %stack_overflow code ********************************************/
20012054
pik_parserARG_STORE /* Suppress warning about unused %extra_argument var */
20022055
pik_parserCTX_STORE
20032056
}
20042057
@@ -2060,166 +2113,166 @@
20602113
}
20612114
20622115
/* For rule J, yyRuleInfoLhs[J] contains the symbol on the left-hand side
20632116
** of that rule */
20642117
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 */
22212274
};
22222275
22232276
/* For rule J, yyRuleInfoNRhs[J] contains the negative of the number
22242277
** of symbols on the right-hand side of that rule. */
22252278
static const signed char yyRuleInfoNRhs[] = {
@@ -2419,620 +2472,620 @@
24192472
** break;
24202473
*/
24212474
/********** Begin reduce actions **********************************************/
24222475
YYMINORTYPE yylhsminor;
24232476
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"
24272480
break;
24282481
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;
24332486
break;
24342487
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;
24392492
break;
24402493
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"
24442497
break;
24452498
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;
24502503
break;
24512504
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;
24562509
break;
24572510
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;
24622515
break;
24632516
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;
24692522
break;
24702523
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;
24752528
break;
24762529
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"
24802533
break;
24812534
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"
24852538
break;
24862539
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"
24902543
break;
24912544
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"
24952548
break;
24962549
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;
25012554
break;
25022555
case 14: /* pritem ::= FILL */
25032556
case 15: /* pritem ::= COLOR */ yytestcase(yyruleno==15);
25042557
case 16: /* pritem ::= THICKNESS */ yytestcase(yyruleno==16);
2505
-#line 590 "pikchr.y"
2558
+#line 604 "pikchr.y"
25062559
{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"
25082561
break;
25092562
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"
25132566
break;
25142567
case 18: /* pritem ::= STRING */
2515
-#line 594 "pikchr.y"
2568
+#line 608 "pikchr.y"
25162569
{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"
25182571
break;
25192572
case 19: /* prsep ::= COMMA */
2520
-#line 595 "pikchr.y"
2573
+#line 609 "pikchr.y"
25212574
{pik_append(p, " ", 1);}
2522
-#line 2547 "pikchr.c"
2575
+#line 2575 "pikchr.c"
25232576
break;
25242577
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;
25292582
break;
25302583
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;
25352588
break;
25362589
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;
25412594
break;
25422595
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"
25462599
break;
25472600
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"
25512604
break;
25522605
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;
25572610
break;
25582611
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;
25632616
break;
25642617
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"
25682621
break;
25692622
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"
25732626
break;
25742627
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"
25782631
break;
25792632
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"
25832636
break;
25842637
case 31: /* attribute ::= dashproperty */
2585
-#line 627 "pikchr.y"
2638
+#line 643 "pikchr.y"
25862639
{ pik_set_dashed(p,&yymsp[0].minor.yy0,0); }
2587
-#line 2612 "pikchr.c"
2640
+#line 2640 "pikchr.c"
25882641
break;
25892642
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"
25932646
break;
25942647
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"
25982651
break;
25992652
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"
26032656
break;
26042657
case 35: /* attribute ::= CLOSE */
2605
-#line 631 "pikchr.y"
2658
+#line 647 "pikchr.y"
26062659
{ pik_close_path(p,&yymsp[0].minor.yy0); }
2607
-#line 2632 "pikchr.c"
2660
+#line 2660 "pikchr.c"
26082661
break;
26092662
case 36: /* attribute ::= CHOP */
2610
-#line 632 "pikchr.y"
2663
+#line 648 "pikchr.y"
26112664
{ p->cur->bChop = 1; }
2612
-#line 2637 "pikchr.c"
2665
+#line 2665 "pikchr.c"
26132666
break;
26142667
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"
26182671
break;
26192672
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"
26232676
break;
26242677
case 39: /* attribute ::= THEN */
2625
-#line 635 "pikchr.y"
2678
+#line 651 "pikchr.y"
26262679
{ pik_then(p, &yymsp[0].minor.yy0, p->cur); }
2627
-#line 2652 "pikchr.c"
2680
+#line 2680 "pikchr.c"
26282681
break;
26292682
case 40: /* attribute ::= THEN optrelexpr HEADING expr */
26302683
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"
26342687
break;
26352688
case 41: /* attribute ::= THEN optrelexpr EDGEPT */
26362689
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"
26402693
break;
26412694
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"
26452698
break;
26462699
case 45: /* attribute ::= SAME */
2647
-#line 645 "pikchr.y"
2700
+#line 661 "pikchr.y"
26482701
{pik_same(p,0,&yymsp[0].minor.yy0);}
2649
-#line 2674 "pikchr.c"
2702
+#line 2702 "pikchr.c"
26502703
break;
26512704
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"
26552708
break;
26562709
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"
26602713
break;
26612714
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"
26652718
break;
26662719
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"
26702723
break;
26712724
case 50: /* withclause ::= DOT_E edge AT position */
26722725
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"
26762729
break;
26772730
case 52: /* numproperty ::= HEIGHT|WIDTH|RADIUS|DIAMETER|THICKNESS */
2678
-#line 661 "pikchr.y"
2731
+#line 677 "pikchr.y"
26792732
{yylhsminor.yy0 = yymsp[0].minor.yy0;}
2680
-#line 2705 "pikchr.c"
2733
+#line 2733 "pikchr.c"
26812734
yymsp[0].minor.yy0 = yylhsminor.yy0;
26822735
break;
26832736
case 53: /* boolproperty ::= CW */
2684
-#line 672 "pikchr.y"
2737
+#line 688 "pikchr.y"
26852738
{p->cur->cw = 1;}
2686
-#line 2711 "pikchr.c"
2739
+#line 2739 "pikchr.c"
26872740
break;
26882741
case 54: /* boolproperty ::= CCW */
2689
-#line 673 "pikchr.y"
2742
+#line 689 "pikchr.y"
26902743
{p->cur->cw = 0;}
2691
-#line 2716 "pikchr.c"
2744
+#line 2744 "pikchr.c"
26922745
break;
26932746
case 55: /* boolproperty ::= LARROW */
2694
-#line 674 "pikchr.y"
2747
+#line 690 "pikchr.y"
26952748
{p->cur->larrow=1; p->cur->rarrow=0; }
2696
-#line 2721 "pikchr.c"
2749
+#line 2749 "pikchr.c"
26972750
break;
26982751
case 56: /* boolproperty ::= RARROW */
2699
-#line 675 "pikchr.y"
2752
+#line 691 "pikchr.y"
27002753
{p->cur->larrow=0; p->cur->rarrow=1; }
2701
-#line 2726 "pikchr.c"
2754
+#line 2754 "pikchr.c"
27022755
break;
27032756
case 57: /* boolproperty ::= LRARROW */
2704
-#line 676 "pikchr.y"
2757
+#line 692 "pikchr.y"
27052758
{p->cur->larrow=1; p->cur->rarrow=1; }
2706
-#line 2731 "pikchr.c"
2759
+#line 2759 "pikchr.c"
27072760
break;
27082761
case 58: /* boolproperty ::= INVIS */
2709
-#line 677 "pikchr.y"
2762
+#line 693 "pikchr.y"
27102763
{p->cur->sw = -0.00001;}
2711
-#line 2736 "pikchr.c"
2764
+#line 2764 "pikchr.c"
27122765
break;
27132766
case 59: /* boolproperty ::= THICK */
2714
-#line 678 "pikchr.y"
2767
+#line 694 "pikchr.y"
27152768
{p->cur->sw *= 1.5;}
2716
-#line 2741 "pikchr.c"
2769
+#line 2769 "pikchr.c"
27172770
break;
27182771
case 60: /* boolproperty ::= THIN */
2719
-#line 679 "pikchr.y"
2772
+#line 695 "pikchr.y"
27202773
{p->cur->sw *= 0.67;}
2721
-#line 2746 "pikchr.c"
2774
+#line 2774 "pikchr.c"
27222775
break;
27232776
case 61: /* boolproperty ::= SOLID */
2724
-#line 680 "pikchr.y"
2777
+#line 696 "pikchr.y"
27252778
{p->cur->sw = pik_value(p,"thickness",9,0);
27262779
p->cur->dotted = p->cur->dashed = 0.0;}
2727
-#line 2752 "pikchr.c"
2780
+#line 2780 "pikchr.c"
27282781
break;
27292782
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"
27332786
break;
27342787
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;
27392792
break;
27402793
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;
27452798
break;
27462799
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;
27512804
break;
27522805
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;
27572810
break;
27582811
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;
27632816
break;
27642817
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;
27692822
break;
27702823
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"
27742827
break;
27752828
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"
27792832
break;
27802833
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;
27852838
break;
27862839
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;
27912844
break;
27922845
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;
27972850
break;
27982851
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;
28032856
break;
28042857
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;
28092862
break;
28102863
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;
28152868
break;
28162869
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;
28212874
break;
28222875
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;
28272880
break;
28282881
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;
28332886
break;
28342887
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;
28392892
break;
28402893
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;
28452898
break;
28462899
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;
28512904
break;
28522905
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;
28572910
break;
28582911
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;
28632916
break;
28642917
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;
28692922
break;
28702923
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;
28752928
break;
28762929
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;
28812934
break;
28822935
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"
28862939
break;
28872940
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;
28922945
break;
28932946
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;
28982951
break;
28992952
case 91: /* nth ::= NTH CLASSNAME */
2900
-#line 751 "pikchr.y"
2953
+#line 767 "pikchr.y"
29012954
{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"
29032956
yymsp[-1].minor.yy0 = yylhsminor.yy0;
29042957
break;
29052958
case 92: /* nth ::= NTH LAST CLASSNAME */
2906
-#line 752 "pikchr.y"
2959
+#line 768 "pikchr.y"
29072960
{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"
29092962
yymsp[-2].minor.yy0 = yylhsminor.yy0;
29102963
break;
29112964
case 93: /* nth ::= LAST CLASSNAME */
2912
-#line 753 "pikchr.y"
2965
+#line 769 "pikchr.y"
29132966
{yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.eCode = -1;}
2914
-#line 2939 "pikchr.c"
2967
+#line 2967 "pikchr.c"
29152968
break;
29162969
case 94: /* nth ::= LAST */
2917
-#line 754 "pikchr.y"
2970
+#line 770 "pikchr.y"
29182971
{yylhsminor.yy0=yymsp[0].minor.yy0; yylhsminor.yy0.eCode = -1;}
2919
-#line 2944 "pikchr.c"
2972
+#line 2972 "pikchr.c"
29202973
yymsp[0].minor.yy0 = yylhsminor.yy0;
29212974
break;
29222975
case 95: /* nth ::= NTH LB RB */
2923
-#line 755 "pikchr.y"
2976
+#line 771 "pikchr.y"
29242977
{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"
29262979
yymsp[-2].minor.yy0 = yylhsminor.yy0;
29272980
break;
29282981
case 96: /* nth ::= NTH LAST LB RB */
2929
-#line 756 "pikchr.y"
2982
+#line 772 "pikchr.y"
29302983
{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"
29322985
yymsp[-3].minor.yy0 = yylhsminor.yy0;
29332986
break;
29342987
case 97: /* nth ::= LAST LB RB */
2935
-#line 757 "pikchr.y"
2988
+#line 773 "pikchr.y"
29362989
{yymsp[-2].minor.yy0=yymsp[-1].minor.yy0; yymsp[-2].minor.yy0.eCode = -1; }
2937
-#line 2962 "pikchr.c"
2990
+#line 2990 "pikchr.c"
29382991
break;
29392992
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;
29442997
break;
29452998
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;
29503003
break;
29513004
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;
29563009
break;
29573010
case 101: /* expr ::= expr SLASH expr */
2958
-#line 762 "pikchr.y"
3011
+#line 778 "pikchr.y"
29593012
{
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; }
29623015
}
2963
-#line 2988 "pikchr.c"
2964
- yymsp[-2].minor.yy21 = yylhsminor.yy21;
3016
+#line 3016 "pikchr.c"
3017
+ yymsp[-2].minor.yy129 = yylhsminor.yy129;
29653018
break;
29663019
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"
29703023
break;
29713024
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"
29753028
break;
29763029
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"
29803033
break;
29813034
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"
29853038
break;
29863039
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;
29913044
break;
29923045
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;
29973050
break;
29983051
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;
30033056
break;
30043057
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;
30093062
break;
30103063
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"
30143067
break;
30153068
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;
30203073
break;
30213074
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;
30263079
break;
30273080
case 113: /* expr ::= object DOT_L numproperty */
30283081
case 114: /* expr ::= object DOT_L dashproperty */ yytestcase(yyruleno==114);
30293082
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;
30343087
break;
30353088
default:
30363089
/* (116) lvalue ::= ID */ yytestcase(yyruleno==116);
30373090
/* (117) lvalue ::= FILL */ yytestcase(yyruleno==117);
30383091
/* (118) lvalue ::= COLOR */ yytestcase(yyruleno==118);
@@ -3130,19 +3183,19 @@
31303183
){
31313184
pik_parserARG_FETCH
31323185
pik_parserCTX_FETCH
31333186
#define TOKEN yyminor
31343187
/************ Begin %syntax_error code ****************************************/
3135
-#line 537 "pikchr.y"
3188
+#line 551 "pikchr.y"
31363189
31373190
if( TOKEN.z && TOKEN.z[0] ){
31383191
pik_error(p, &TOKEN, "syntax error");
31393192
}else{
31403193
pik_error(p, 0, "syntax error");
31413194
}
31423195
UNUSED_PARAMETER(yymajor);
3143
-#line 3168 "pikchr.c"
3196
+#line 3196 "pikchr.c"
31443197
/************ End %syntax_error code ******************************************/
31453198
pik_parserARG_STORE /* Suppress warning about unused %extra_argument variable */
31463199
pik_parserCTX_STORE
31473200
}
31483201
@@ -3407,11 +3460,11 @@
34073460
#else
34083461
(void)iToken;
34093462
return 0;
34103463
#endif
34113464
}
3412
-#line 782 "pikchr.y"
3465
+#line 798 "pikchr.y"
34133466
34143467
34153468
34163469
/* Chart of the 148 official CSS color names with their
34173470
** corresponding RGB values thru Color Module Level 4:
@@ -3634,42 +3687,57 @@
36343687
** than true arcs. Multiple reasons: (1) the legacy-PIC parameters
36353688
** that control arcs are obscure and I could not figure out what they
36363689
** mean based on available documentation. (2) Arcs are rarely used,
36373690
** and so do not seem that important.
36383691
*/
3639
-static PPoint arcControlPoint(int cw, PPoint f, PPoint t, PNum rScale){
3692
+static PPoint arcControlPoint(int cw, PPoint f, PPoint t){
36403693
PPoint m;
36413694
PNum dx, dy;
36423695
m.x = 0.5*(f.x+t.x);
36433696
m.y = 0.5*(f.y+t.y);
36443697
dx = t.x - f.x;
36453698
dy = t.y - f.y;
36463699
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;
36493702
}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;
36523705
}
36533706
return m;
36543707
}
36553708
static void arcCheck(Pik *p, PObj *pObj){
3656
- PPoint m;
3709
+ PPoint f, m, t;
3710
+ PNum sw;
3711
+ int i;
36573712
if( p->nTPath>2 ){
36583713
pik_error(p, &pObj->errTok, "arc geometry error");
36593714
return;
36603715
}
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
+ }
36633731
}
36643732
static void arcRender(Pik *p, PObj *pObj){
36653733
PPoint f, m, t;
36663734
if( pObj->nPath<2 ) return;
36673735
if( pObj->sw<0.0 ) return;
36683736
f = pObj->aPath[0];
36693737
t = pObj->aPath[1];
3670
- m = arcControlPoint(pObj->cw,f,t,1.0);
3738
+ m = arcControlPoint(pObj->cw,f,t);
36713739
if( pObj->larrow ){
36723740
pik_draw_arrowhead(p,&m,&f,pObj);
36733741
}
36743742
if( pObj->rarrow ){
36753743
pik_draw_arrowhead(p,&m,&t,pObj);
@@ -4338,11 +4406,11 @@
43384406
static PPoint textOffset(Pik *p, PObj *pObj, int cp){
43394407
/* Automatically slim-down the width and height of text
43404408
** statements so that the bounding box tightly encloses the text,
43414409
** then get boxOffset() to do the offset computation.
43424410
*/
4343
- pik_size_to_fit(p, &pObj->errTok,3);
4411
+ pik_size_to_fit(p, pObj, &pObj->errTok,3);
43444412
return boxOffset(p, pObj, cp);
43454413
}
43464414
static void textRender(Pik *p, PObj *pObj){
43474415
pik_append_txt(p, pObj, 0);
43484416
}
@@ -6347,16 +6415,15 @@
63476415
**
63486416
** 1: Fit horizontally only
63496417
** 2: Fit vertically only
63506418
** 3: Fit both ways
63516419
*/
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){
63546421
PNum w, h;
63556422
PBox bbox;
63566423
if( p->nErr ) return;
6357
- pObj = p->cur;
6424
+ if( pObj==0 ) pObj = p->cur;
63586425
63596426
if( pObj->nTxt==0 ){
63606427
pik_error(0, pFit, "no text to fit to");
63616428
return;
63626429
}
@@ -6495,13 +6562,13 @@
64956562
unsigned int i;
64966563
mid = (first+last)/2;
64976564
zClr = aColor[mid].zName;
64986565
for(i=0; i<pId->n; i++){
64996566
c1 = zClr[i]&0x7f;
6500
- if( isupper(c1) ) c1 = tolower(c1);
6567
+ if( IsUpper(c1) ) c1 = ToLower(c1);
65016568
c2 = pId->z[i]&0x7f;
6502
- if( isupper(c2) ) c2 = tolower(c2);
6569
+ if( IsUpper(c2) ) c2 = ToLower(c2);
65036570
c = c2 - c1;
65046571
if( c ) break;
65056572
}
65066573
if( c==0 && aColor[mid].zName[pId->n] ) c = -1;
65076574
if( c==0 ) return (double)aColor[mid].val;
@@ -6896,20 +6963,20 @@
68966963
*/
68976964
if( pObj->h<=0.0 ){
68986965
if( pObj->nTxt==0 ){
68996966
pObj->h = 0.0;
69006967
}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);
69026969
}else{
6903
- pik_size_to_fit(p, &pObj->errTok, 2);
6970
+ pik_size_to_fit(p, pObj, &pObj->errTok, 2);
69046971
}
69056972
}
69066973
if( pObj->w<=0.0 ){
69076974
if( pObj->nTxt==0 ){
69086975
pObj->w = 0.0;
69096976
}else{
6910
- pik_size_to_fit(p, &pObj->errTok, 1);
6977
+ pik_size_to_fit(p, pObj, &pObj->errTok, 1);
69116978
}
69126979
}
69136980
ofst = pik_elem_offset(p, pObj, pObj->eWith);
69146981
dx = (pObj->with.x - ofst.x) - pObj->ptAt.x;
69156982
dy = (pObj->with.y - ofst.y) - pObj->ptAt.y;
@@ -7235,11 +7302,12 @@
72357302
pik_append_num(p, " width=\"", p->wSVG);
72367303
pik_append_num(p, "\" height=\"", p->hSVG);
72377304
pik_append(p, "\"", 1);
72387305
}
72397306
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);
72417309
pik_elist_render(p, pList);
72427310
pik_append(p,"</svg>\n", -1);
72437311
}else{
72447312
p->wSVG = -1;
72457313
p->hSVG = -1;
@@ -7319,10 +7387,11 @@
73197387
{ "n", 1, T_EDGEPT, 0, CP_N },
73207388
{ "ne", 2, T_EDGEPT, 0, CP_NE },
73217389
{ "north", 5, T_EDGEPT, 0, CP_N },
73227390
{ "nw", 2, T_EDGEPT, 0, CP_NW },
73237391
{ "of", 2, T_OF, 0, 0 },
7392
+ { "pikchr_date",11, T_ISODATE, 0, 0, },
73247393
{ "previous", 8, T_LAST, 0, 0, },
73257394
{ "print", 5, T_PRINT, 0, 0 },
73267395
{ "rad", 3, T_RADIUS, 0, 0 },
73277396
{ "radius", 6, T_RADIUS, 0, 0 },
73287397
{ "right", 5, T_RIGHT, DIR_RIGHT, CP_E },
@@ -7605,11 +7674,11 @@
76057674
}
76067675
default: {
76077676
c = z[0];
76087677
if( c=='.' ){
76097678
unsigned char c1 = z[1];
7610
- if( islower(c1) ){
7679
+ if( IsLower(c1) ){
76117680
const PikWord *pFound;
76127681
for(i=2; (c = z[i])>='a' && c<='z'; i++){}
76137682
pFound = pik_find_word((const char*)z+1, i-1,
76147683
pik_keywords, count(pik_keywords));
76157684
if( pFound && (pFound->eEdge>0 ||
@@ -7625,15 +7694,15 @@
76257694
}else{
76267695
/* Any other "dot" */
76277696
pToken->eType = T_DOT_L;
76287697
}
76297698
return 1;
7630
- }else if( isdigit(c1) ){
7699
+ }else if( IsDigit(c1) ){
76317700
i = 0;
76327701
/* 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++){}
76357704
pToken->eType = T_DOT_U;
76367705
return 1;
76377706
}else{
76387707
pToken->eType = T_ERROR;
76397708
return 1;
@@ -7644,11 +7713,11 @@
76447713
int isInt = 1;
76457714
if( c!='.' ){
76467715
nDigit = 1;
76477716
for(i=1; (c = z[i])>='0' && c<='9'; i++){ nDigit++; }
76487717
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++){}
76507719
pToken->eType = T_NUMBER;
76517720
return i;
76527721
}
76537722
}else{
76547723
isInt = 0;
@@ -7700,13 +7769,13 @@
77007769
){
77017770
i += 2;
77027771
}
77037772
pToken->eType = T_NUMBER;
77047773
return i;
7705
- }else if( islower(c) ){
7774
+ }else if( IsLower(c) ){
77067775
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++){}
77087777
pFound = pik_find_word((const char*)z, i,
77097778
pik_keywords, count(pik_keywords));
77107779
if( pFound ){
77117780
pToken->eType = pFound->eType;
77127781
pToken->eCode = pFound->eCode;
@@ -7719,19 +7788,19 @@
77197788
}else{
77207789
pToken->eType = T_ID;
77217790
}
77227791
return i;
77237792
}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++){}
77257794
pToken->eType = T_PLACENAME;
77267795
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]) ){
77287797
pToken->eType = T_PARAMETER;
77297798
pToken->eCode = z[1] - '1';
77307799
return 2;
77317800
}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++){}
77337802
pToken->eType = T_ID;
77347803
return i;
77357804
}else{
77367805
pToken->eType = T_ERROR;
77377806
return 1;
@@ -7814,12 +7883,12 @@
78147883
/* Remove leading and trailing whitespace from each argument.
78157884
** If what remains is one of $1, $2, ... $9 then transfer the
78167885
** corresponding argument from the outer context */
78177886
for(j=0; j<=nArg; j++){
78187887
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--; }
78217890
if( t->n==2 && t->z[0]=='$' && t->z[1]>='1' && t->z[1]<='9' ){
78227891
if( pOuter ) *t = pOuter[t->z[1]-'1'];
78237892
else t->n = 0;
78247893
}
78257894
}
@@ -7896,21 +7965,36 @@
78967965
pMac->inUse = 0;
78977966
}else{
78987967
#if 0
78997968
printf("******** Token %s (%d): \"%.*s\" **************\n",
79007969
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);
79027971
#endif
79037972
token.n = (unsigned short)(sz & 0xffff);
79047973
if( p->nToken++ > PIKCHR_TOKEN_LIMIT ){
79057974
pik_error(p, &token, "script is too complex");
79067975
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;
79077981
}
79087982
pik_parser(pParser, token.eType, token);
79097983
}
79107984
}
79117985
}
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
+}
79127996
79137997
/*
79147998
** Parse the PIKCHR script contained in zText[]. Return a rendering. Or
79157999
** if an error is encountered, return the error text. The error message
79168000
** is HTML formatted. So regardless of what happens, the return text
@@ -8130,10 +8214,14 @@
81308214
exit(1);
81318215
}
81328216
bSvgOnly = 1;
81338217
mFlags |= PIKCHR_PLAINTEXT_ERRORS;
81348218
}else
8219
+ if( strcmp(z,"version")==0 || strcmp(z,"v")==0 ){
8220
+ printf("pikchr %s\n", pikchr_version());
8221
+ return 0;
8222
+ }else
81358223
{
81368224
fprintf(stderr,"unknown option: \"%s\"\n", argv[i]);
81378225
usage(argv[0]);
81388226
}
81398227
continue;
@@ -8242,6 +8330,6 @@
82428330
82438331
82448332
#endif /* PIKCHR_TCL */
82458333
82468334
8247
-#line 8272 "pikchr.c"
8335
+#line 8335 "pikchr.c"
82488336
--- 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 @@
11
22
var initPikchrModule = (() => {
3
- var _scriptDir = typeof document !== 'undefined' && document.currentScript ? document.currentScript.src : undefined;
3
+ var _scriptName = typeof document != 'undefined' ? document.currentScript?.src : undefined;
44
55
return (
6
-function(config) {
7
- var initPikchrModule = config || {};
6
+function(moduleArg = {}) {
7
+ var moduleRtn;
88
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;
1024
25
+// Set up the promise that indicates the Module is initialized
1126
var readyPromiseResolve, readyPromiseReject;
1227
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;
1631
});
1732
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.
1846
var moduleOverrides = Object.assign({}, Module);
1947
2048
var arguments_ = [];
2149
2250
var thisProgram = "./this.program";
2351
2452
var quit_ = (status, toThrow) => {
25
- throw toThrow;
53
+ throw toThrow;
2654
};
2755
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
3257
var scriptDirectory = "";
3358
3459
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
+{}
90109
91110
var out = Module["print"] || console.log.bind(console);
92111
93
-var err = Module["printErr"] || console.warn.bind(console);
112
+var err = Module["printErr"] || console.error.bind(console);
94113
114
+// Merge back in the overrides
95115
Object.assign(Module, moduleOverrides);
96116
117
+// Free the object hierarchy contained in the overrides, this lets the GC
118
+// reclaim data used.
97119
moduleOverrides = null;
98120
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.
99125
if (Module["arguments"]) arguments_ = Module["arguments"];
100126
101127
if (Module["thisProgram"]) thisProgram = Module["thisProgram"];
102128
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
115143
var wasmMemory;
116144
145
+//========================================
146
+// Runtime essentials
147
+//========================================
148
+// whether we are quitting the application. no code should run after this.
149
+// set in exit() and abort()
117150
var ABORT = false;
118151
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.
119155
var EXITSTATUS;
120156
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
202161
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
218176
var __ATPRERUN__ = [];
219177
178
+// functions called before the runtime is initialized
220179
var __ATINIT__ = [];
221180
181
+// functions called during shutdown
222182
var __ATPOSTRUN__ = [];
223183
184
+// functions called after the main() is called
224185
var runtimeInitialized = false;
225186
226
-function keepRuntimeAlive() {
227
- return noExitRuntime;
228
-}
229
-
230187
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);
235192
}
236
- }
237
- callRuntimeCallbacks(__ATPRERUN__);
193
+ callRuntimeCallbacks(__ATPRERUN__);
238194
}
239195
240196
function initRuntime() {
241
- runtimeInitialized = true;
242
- callRuntimeCallbacks(__ATINIT__);
197
+ runtimeInitialized = true;
198
+ callRuntimeCallbacks(__ATINIT__);
243199
}
244200
245201
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);
250206
}
251
- }
252
- callRuntimeCallbacks(__ATPOSTRUN__);
207
+ callRuntimeCallbacks(__ATPOSTRUN__);
253208
}
254209
255210
function addOnPreRun(cb) {
256
- __ATPRERUN__.unshift(cb);
211
+ __ATPRERUN__.unshift(cb);
257212
}
258213
259214
function addOnInit(cb) {
260
- __ATINIT__.unshift(cb);
215
+ __ATINIT__.unshift(cb);
261216
}
262217
263218
function addOnPostRun(cb) {
264
- __ATPOSTRUN__.unshift(cb);
219
+ __ATPOSTRUN__.unshift(cb);
265220
}
266221
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.
267235
var runDependencies = 0;
268236
269237
var runDependencyWatcher = null;
270238
271239
var dependenciesFulfilled = null;
272240
273241
function addRunDependency(id) {
274
- runDependencies++;
275
- if (Module["monitorRunDependencies"]) {
276
- Module["monitorRunDependencies"](runDependencies);
277
- }
242
+ runDependencies++;
243
+ Module["monitorRunDependencies"]?.(runDependencies);
278244
}
279245
280246
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.
312294
var dataURIPrefix = "data:application/octet-stream;base64,";
313295
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;
316310
}
317311
318312
var wasmBinaryFile;
319313
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
+};
546620
547621
var _exit = exitJS;
548622
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 ===
645802
Module["stackSave"] = stackSave;
646803
647804
Module["stackRestore"] = stackRestore;
805
+
806
+Module["stackAlloc"] = stackAlloc;
807
+
808
+Module["ccall"] = ccall;
648809
649810
Module["cwrap"] = cwrap;
650811
651812
Module["setValue"] = setValue;
652813
653814
Module["getValue"] = getValue;
654815
655816
var calledRun;
817
+
818
+var calledPrerun;
656819
657820
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;
660824
};
661825
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
+ }
692860
}
693861
694862
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
+ }
699867
}
700868
701869
run();
702870
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
+
703879
704
- return initPikchrModule.ready
880
+ return moduleRtn;
705881
}
706882
);
707883
})();
708884
if (typeof exports === 'object' && typeof module === 'object')
709885
module.exports = initPikchrModule;
710886
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);
714888
--- 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
11
--- 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 @@
234234
235235
/* ctype macros that work with signed characters */
236236
#define IsSpace(X) isspace((unsigned char)X)
237237
#define IsDigit(X) isdigit((unsigned char)X)
238238
#define ToLower(X) (char)tolower((unsigned char)X)
239
+#define IsAlnum(X) isalnum((unsigned char)X)
240
+#define IsAlpha(X) isalpha((unsigned char)X)
239241
240242
#if defined(_WIN32) || defined(WIN32)
241243
#if SQLITE_OS_WINRT
242244
#include <intrin.h>
243245
#endif
@@ -849,11 +851,11 @@
849851
850852
/*
851853
** Prompt strings. Initialized in main. Settable with
852854
** .prompt main continue
853855
*/
854
-#define PROMPT_LEN_MAX 20
856
+#define PROMPT_LEN_MAX 128
855857
/* First line prompt. default: "sqlite> " */
856858
static char mainPrompt[PROMPT_LEN_MAX];
857859
/* Continuation prompt. default: " ...> " */
858860
static char continuePrompt[PROMPT_LEN_MAX];
859861
@@ -1506,13 +1508,13 @@
15061508
** Return '"' if quoting is required. Return 0 if no quoting is required.
15071509
*/
15081510
static char quoteChar(const char *zName){
15091511
int i;
15101512
if( zName==0 ) return '"';
1511
- if( !isalpha((unsigned char)zName[0]) && zName[0]!='_' ) return '"';
1513
+ if( !IsAlpha(zName[0]) && zName[0]!='_' ) return '"';
15121514
for(i=0; zName[i]; i++){
1513
- if( !isalnum((unsigned char)zName[i]) && zName[i]!='_' ) return '"';
1515
+ if( !IsAlnum(zName[i]) && zName[i]!='_' ) return '"';
15141516
}
15151517
return sqlite3_keyword_check(zName, i) ? '"' : 0;
15161518
}
15171519
15181520
/*
@@ -2425,11 +2427,11 @@
24252427
** content in cases where the content starts
24262428
** with a digit.
24272429
**
24282430
** typeof(Y)='blob' The hash is taken over prefix "Bnnn:" followed
24292431
** 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
24312433
** representation of the byte-length of the blob.
24322434
**
24332435
** According to the rules above, all of the following SELECT statements
24342436
** should return TRUE:
24352437
**
@@ -3646,11 +3648,11 @@
36463648
**
36473649
** UINT works like BINARY for text, except that embedded strings
36483650
** of digits compare in numeric order.
36493651
**
36503652
** * 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
36523654
** strings of digits. "x00123y" is equal to "x123y".
36533655
**
36543656
** * Only unsigned integers are recognized. Plus and minus
36553657
** signs are ignored. Decimal points and exponential notation
36563658
** are ignored.
@@ -3752,10 +3754,13 @@
37523754
** warnings. */
37533755
#ifndef UNUSED_PARAMETER
37543756
# define UNUSED_PARAMETER(X) (void)(X)
37553757
#endif
37563758
3759
+#ifndef IsSpace
3760
+#define IsSpace(X) isspace((unsigned char)X)
3761
+#endif
37573762
37583763
/* A decimal object */
37593764
typedef struct Decimal Decimal;
37603765
struct Decimal {
37613766
char sign; /* 0 for positive, 1 for negative */
@@ -3801,11 +3806,11 @@
38013806
p->isNull = 0;
38023807
p->nDigit = 0;
38033808
p->nFrac = 0;
38043809
p->a = sqlite3_malloc64( n+1 );
38053810
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++){}
38073812
if( zIn[i]=='-' ){
38083813
p->sign = 1;
38093814
i++;
38103815
}else if( zIn[i]=='+' ){
38113816
i++;
@@ -4456,11 +4461,11 @@
44564461
}
44574462
decimal_free(pA);
44584463
decimal_free(pB);
44594464
}
44604465
4461
-/* Aggregate funcion: decimal_sum(X)
4466
+/* Aggregate function: decimal_sum(X)
44624467
**
44634468
** Works like sum() except that it uses decimal arithmetic for unlimited
44644469
** precision.
44654470
*/
44664471
static void decimalSumStep(
@@ -4817,11 +4822,11 @@
48174822
}
48184823
48194824
/*
48204825
** Generate an error for a percentile function.
48214826
**
4822
-** The error format string must have exactly one occurrance of "%%s()"
4827
+** The error format string must have exactly one occurrence of "%%s()"
48234828
** (with two '%' characters). That substring will be replaced by the name
48244829
** of the function.
48254830
*/
48264831
static void percentError(sqlite3_context *pCtx, const char *zFormat, ...){
48274832
PercentileFunc *pFunc = (PercentileFunc*)sqlite3_user_data(pCtx);
@@ -5957,11 +5962,11 @@
59575962
** number format:
59585963
**
59595964
** WITH c(name,bin) AS (VALUES
59605965
** ('minimum positive value', x'0000000000000001'),
59615966
** ('maximum subnormal value', x'000fffffffffffff'),
5962
-** ('mininum positive nornal value', x'0010000000000000'),
5967
+** ('minimum positive normal value', x'0010000000000000'),
59635968
** ('maximum value', x'7fefffffffffffff'))
59645969
** SELECT c.name, decimal_mul(ieee754_mantissa(c.bin),pow2.v)
59655970
** FROM pow2, c WHERE pow2.x=ieee754_exponent(c.bin);
59665971
**
59675972
*/
@@ -6344,11 +6349,11 @@
63446349
* 2 for UBSAN's satisfaction (and hypothetical 1's complement ALUs.) */
63456350
smBase += (mxI64/2) * smStep;
63466351
smBase += (mxI64 - mxI64/2) * smStep;
63476352
}
63486353
/* 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. */
63506355
if( ix>=2 ){
63516356
sqlite3_int64 ix2 = (sqlite3_int64)ix/2;
63526357
smBase += ix2*smStep;
63536358
ix -= ix2;
63546359
}
@@ -6724,23 +6729,21 @@
67246729
if( pCur->ss.iBase<iMin ){
67256730
sqlite3_uint64 d = iMin - pCur->ss.iBase;
67266731
pCur->ss.iBase += ((d+szStep-1)/szStep)*szStep;
67276732
}
67286733
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;
67316735
}
67326736
}else{
67336737
sqlite3_int64 szStep = -pCur->ss.iStep;
67346738
assert( szStep>0 );
67356739
if( pCur->ss.iBase>iMax ){
67366740
sqlite3_uint64 d = pCur->ss.iBase - iMax;
67376741
pCur->ss.iBase -= ((d+szStep-1)/szStep)*szStep;
67386742
}
67396743
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;
67426745
}
67436746
}
67446747
}
67456748
67466749
/* Apply LIMIT and OFFSET constraints, if any */
@@ -9024,10 +9027,15 @@
90249027
#include <assert.h>
90259028
#include <string.h>
90269029
#include <ctype.h>
90279030
90289031
#ifndef SQLITE_OMIT_VIRTUALTABLE
9032
+
9033
+#ifndef IsAlnum
9034
+#define IsAlnum(X) isalnum((unsigned char)X)
9035
+#endif
9036
+
90299037
90309038
/* completion_vtab is a subclass of sqlite3_vtab which will
90319039
** serve as the underlying representation of a completion virtual table
90329040
*/
90339041
typedef struct completion_vtab completion_vtab;
@@ -9361,11 +9369,11 @@
93619369
if( pCur->zLine==0 ) return SQLITE_NOMEM;
93629370
}
93639371
}
93649372
if( pCur->zLine!=0 && pCur->zPrefix==0 ){
93659373
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]=='_') ){
93679375
i--;
93689376
}
93699377
pCur->nPrefix = pCur->nLine - i;
93709378
if( pCur->nPrefix>0 ){
93719379
pCur->zPrefix = sqlite3_mprintf("%.*s", pCur->nPrefix, pCur->zLine + i);
@@ -14768,11 +14776,11 @@
1476814776
/* Register the auth callback with dbv */
1476914777
if( rc==SQLITE_OK ){
1477014778
sqlite3_set_authorizer(pNew->dbv, idxAuthCallback, (void*)pNew);
1477114779
}
1477214780
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,
1477414782
** return the new sqlite3expert handle. */
1477514783
if( rc!=SQLITE_OK ){
1477614784
sqlite3_expert_destroy(pNew);
1477714785
pNew = 0;
1477814786
}
@@ -16290,11 +16298,11 @@
1629016298
**
1629116299
** PRAGMA vfstrace('+all');
1629216300
**
1629316301
** Individual APIs can be enabled or disabled by name, with or without
1629416302
** the initial "x" character. For example, to set up for tracing lock
16295
-** primatives only:
16303
+** primitives only:
1629616304
**
1629716305
** PRAGMA vfstrace('-all, +Lock,Unlock,ShmLock');
1629816306
**
1629916307
** The argument to the vfstrace pragma ignores capitalization and any
1630016308
** characters other than alphabetics, '+', and '-'.
@@ -18700,10 +18708,20 @@
1870018708
1870118709
/* typedef unsigned int u32; */
1870218710
/* typedef unsigned char u8; */
1870318711
/* typedef sqlite3_int64 i64; */
1870418712
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
+
1870518723
typedef struct RecoverTable RecoverTable;
1870618724
typedef struct RecoverColumn RecoverColumn;
1870718725
1870818726
/*
1870918727
** When recovering rows of data that can be associated with table
@@ -18807,12 +18825,15 @@
1880718825
** (aElem[iKey/32] & (1 << (iKey%32))) ? 1 : 0
1880818826
*/
1880918827
typedef struct RecoverBitmap RecoverBitmap;
1881018828
struct RecoverBitmap {
1881118829
i64 nPg; /* Size of bitmap */
18812
- u32 aElem[1]; /* Array of 32-bit bitmasks */
18830
+ u32 aElem[FLEXARRAY]; /* Array of 32-bit bitmasks */
1881318831
};
18832
+
18833
+/* Size in bytes of a RecoverBitmap object sufficient to cover 32 pages */
18834
+#define SZ_RECOVERBITMAP_32 (16)
1881418835
1881518836
/*
1881618837
** State variables (part of the sqlite3_recover structure) used while
1881718838
** recovering data for tables identified in the recovered schema (state
1881818839
** RECOVER_STATE_WRITING).
@@ -19049,11 +19070,11 @@
1904919070
** large enough to store a bit for all page numbers between 1 and nPg,
1905019071
** inclusive. The bitmap is initially zeroed.
1905119072
*/
1905219073
static RecoverBitmap *recoverBitmapAlloc(sqlite3_recover *p, i64 nPg){
1905319074
int nElem = (nPg+1+31) / 32;
19054
- int nByte = sizeof(RecoverBitmap) + nElem*sizeof(u32);
19075
+ int nByte = SZ_RECOVERBITMAP_32 + nElem*sizeof(u32);
1905519076
RecoverBitmap *pRet = (RecoverBitmap*)recoverMalloc(p, nByte);
1905619077
1905719078
if( pRet ){
1905819079
pRet->nPg = nPg;
1905919080
}
@@ -21241,41 +21262,57 @@
2124121262
** function is called.
2124221263
*/
2124321264
static void recoverStep(sqlite3_recover *p){
2124421265
assert( p && p->errCode==SQLITE_OK );
2124521266
switch( p->eState ){
21246
- case RECOVER_STATE_INIT:
21267
+ case RECOVER_STATE_INIT: {
21268
+ int bUseWrapper = 1;
2124721269
/* This is the very first call to sqlite3_recover_step() on this object.
2124821270
*/
2124921271
recoverSqlCallback(p, "BEGIN");
2125021272
recoverSqlCallback(p, "PRAGMA writable_schema = on");
21273
+ recoverSqlCallback(p, "PRAGMA foreign_keys = off");
2125121274
2125221275
recoverEnterMutex();
21253
- recoverInstallWrapper(p);
2125421276
2125521277
/* Open the output database. And register required virtual tables and
2125621278
** user functions with the new handle. */
2125721279
recoverOpenOutput(p);
2125821280
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
+
2127021308
recoverLeaveMutex();
21271
-
2127221309
recoverExec(p, p->dbOut, "BEGIN");
21273
-
2127421310
recoverWriteSchema1(p);
2127521311
p->eState = RECOVER_STATE_WRITING;
2127621312
break;
21313
+ }
2127721314
2127821315
case RECOVER_STATE_WRITING: {
2127921316
if( p->w1.pTbls==0 ){
2128021317
recoverWriteDataInit(p);
2128121318
}
@@ -21610,10 +21647,11 @@
2161021647
u8 eTraceType; /* SHELL_TRACE_* value for type of trace */
2161121648
u8 bSafeMode; /* True to prohibit unsafe operations */
2161221649
u8 bSafeModePersist; /* The long-term value of bSafeMode */
2161321650
u8 eRestoreState; /* See comments above doAutoDetectRestore() */
2161421651
u8 crlfMode; /* Do NL-to-CRLF translations when enabled (maybe) */
21652
+ u8 eEscMode; /* Escape mode for text output */
2161521653
ColModeOpts cmOpts; /* Option values affecting columnar mode output */
2161621654
unsigned statsOn; /* True to display memory stats before each finalize */
2161721655
unsigned mEqpLines; /* Mask of vertical lines in the EQP output graph */
2161821656
int inputNesting; /* Track nesting level of .read and other redirects */
2161921657
int outCount; /* Revert to stdout when reaching zero */
@@ -21710,10 +21748,19 @@
2171021748
#define SHELL_PROGRESS_RESET 0x02 /* Reset the count when the progress
2171121749
** callback limit is reached, and for each
2171221750
** top-level SQL statement */
2171321751
#define SHELL_PROGRESS_ONCE 0x04 /* Cancel the --limit after firing once */
2171421752
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
+
2171521762
/*
2171621763
** These are the allowed shellFlgs values
2171721764
*/
2171821765
#define SHFLG_Pagecache 0x00000001 /* The --pagecache option is used */
2171921766
#define SHFLG_Lookaside 0x00000002 /* Lookaside memory is used */
@@ -22047,63 +22094,79 @@
2204722094
sqlite3_fprintf(out, "X'%s'", zStr);
2204822095
sqlite3_free(zStr);
2204922096
}
2205022097
2205122098
/*
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){
2207822113
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;
2208022118
FILE *out = p->out;
2208122119
sqlite3_fsetmode(out, _O_BINARY);
2208222120
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
+ ){
2208522132
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);
2208622137
}else{
22087
- sqlite3_fputs("'", out);
22138
+ if( needUnistr ){
22139
+ sqlite3_fputs("unistr('", out);
22140
+ }else{
22141
+ sqlite3_fputs("'", out);
22142
+ }
2208822143
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
+ }
2209122151
if( i ){
2209222152
sqlite3_fprintf(out, "%.*s", i, z);
2209322153
z += i;
2209422154
}
22155
+ if( c==0 ) break;
2209522156
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);
2210122160
}
2210222161
z++;
2210322162
}
22104
- sqlite3_fputs("'", out);
22163
+ if( needUnistr ){
22164
+ sqlite3_fputs("')", out);
22165
+ }else{
22166
+ sqlite3_fputs("'", out);
22167
+ }
2210522168
}
2210622169
setCrlfMode(p);
2210722170
}
2210822171
2210922172
/*
@@ -22114,65 +22177,19 @@
2211422177
**
2211522178
** This is like output_quoted_string() but with the addition of the \r\n
2211622179
** escape mechanism.
2211722180
*/
2211822181
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);
2212622186
}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);
2217422191
setCrlfMode(p);
2217522192
}
2217622193
2217722194
/*
2217822195
** Find earliest of chars within s specified in zAny.
@@ -22317,10 +22334,97 @@
2231722334
sqlite3_fputs(ace+1, out);
2231822335
}
2231922336
}
2232022337
sqlite3_fputs(zq, out);
2232122338
}
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
+}
2232222426
2232322427
/*
2232422428
** Output the given string with characters that are special to
2232522429
** HTML escaped.
2232622430
*/
@@ -22761,12 +22865,16 @@
2276122865
int len = strlen30(azCol[i] ? azCol[i] : "");
2276222866
if( len>w ) w = len;
2276322867
}
2276422868
if( p->cnt++>0 ) sqlite3_fputs(p->rowSeparator, p->out);
2276522869
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);
2276622873
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);
2276822876
}
2276922877
break;
2277022878
}
2277122879
case MODE_ScanExp:
2277222880
case MODE_Explain: {
@@ -22832,18 +22940,22 @@
2283222940
int j;
2283322941
int nParen = 0;
2283422942
char cEnd = 0;
2283522943
char c;
2283622944
int nLine = 0;
22945
+ int isIndex;
22946
+ int isWhere = 0;
2283722947
assert( nArg==1 );
2283822948
if( azArg[0]==0 ) break;
2283922949
if( sqlite3_strlike("CREATE VIEW%", azArg[0], 0)==0
2284022950
|| sqlite3_strlike("CREATE TRIG%", azArg[0], 0)==0
2284122951
){
2284222952
sqlite3_fprintf(p->out, "%s;\n", azArg[0]);
2284322953
break;
2284422954
}
22955
+ isIndex = sqlite3_strlike("CREATE INDEX%", azArg[0], 0)==0
22956
+ || sqlite3_strlike("CREATE UNIQUE INDEX%", azArg[0], 0)==0;
2284522957
z = sqlite3_mprintf("%s", azArg[0]);
2284622958
shell_check_oom(z);
2284722959
j = 0;
2284822960
for(i=0; IsSpace(z[i]); i++){}
2284922961
for(; (c = z[i])!=0; i++){
@@ -22869,18 +22981,30 @@
2286922981
cEnd = '\n';
2287022982
}else if( c=='(' ){
2287122983
nParen++;
2287222984
}else if( c==')' ){
2287322985
nParen--;
22874
- if( nLine>0 && nParen==0 && j>0 ){
22986
+ if( nLine>0 && nParen==0 && j>0 && !isWhere ){
2287522987
printSchemaLineN(p->out, z, j, "\n");
2287622988
j = 0;
2287722989
}
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;
2287823001
}
2287923002
z[j++] = c;
2288023003
if( nParen==1 && cEnd==0
2288123004
&& (c=='(' || c=='\n' || (c==',' && !wsToEol(z+i+1)))
23005
+ && !isWhere
2288223006
){
2288323007
if( c=='\n' ) j--;
2288423008
printSchemaLineN(p->out, z, j, "\n ");
2288523009
j = 0;
2288623010
nLine++;
@@ -22894,19 +23018,27 @@
2289423018
break;
2289523019
}
2289623020
case MODE_List: {
2289723021
if( p->cnt++==0 && p->showHeader ){
2289823022
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,
2290023027
i==nArg-1 ? p->rowSeparator : p->colSeparator);
23028
+ if( pFree ) sqlite3_free(pFree);
2290123029
}
2290223030
}
2290323031
if( azArg==0 ) break;
2290423032
for(i=0; i<nArg; i++){
2290523033
char *z = azArg[i];
23034
+ char *pFree;
23035
+ const char *zOut;
2290623036
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);
2290823040
sqlite3_fputs((i<nArg-1)? p->colSeparator : p->rowSeparator, p->out);
2290923041
}
2291023042
break;
2291123043
}
2291223044
case MODE_Www:
@@ -24021,10 +24153,11 @@
2402124153
** from malloc()) of that first line, which caller should free sometime.
2402224154
** Write anything to display on the next line into *pzTail. If this is
2402324155
** the last line, write a NULL into *pzTail. (*pzTail is not allocated.)
2402424156
*/
2402524157
static char *translateForDisplayAndDup(
24158
+ ShellState *p, /* To access current settings */
2402624159
const unsigned char *z, /* Input text to be transformed */
2402724160
const unsigned char **pzTail, /* OUT: Tail of the input for next line */
2402824161
int mxWidth, /* Max width. 0 means no limit */
2402924162
u8 bWordWrap /* If true, avoid breaking mid-word */
2403024163
){
@@ -24055,28 +24188,31 @@
2405524188
n++;
2405624189
i++;
2405724190
j++;
2405824191
continue;
2405924192
}
24193
+ if( c==0 || c=='\n' || (c=='\r' && z[i+1]=='\n') ) break;
2406024194
if( c=='\t' ){
2406124195
do{
2406224196
n++;
2406324197
j++;
2406424198
}while( (n&7)!=0 && n<mxWidth );
2406524199
i++;
2406624200
continue;
2406724201
}
24068
- break;
24202
+ n++;
24203
+ j += 3;
24204
+ i++;
2406924205
}
2407024206
if( n>=mxWidth && bWordWrap ){
2407124207
/* Perhaps try to back up to a better place to break the line */
2407224208
for(k=i; k>i/2; k--){
24073
- if( isspace(z[k-1]) ) break;
24209
+ if( IsSpace(z[k-1]) ) break;
2407424210
}
2407524211
if( k<=i/2 ){
2407624212
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;
2407824214
}
2407924215
}
2408024216
if( k<=i/2 ){
2408124217
k = i;
2408224218
}else{
@@ -24110,23 +24246,48 @@
2411024246
if( c>=' ' ){
2411124247
n++;
2411224248
zOut[j++] = z[i++];
2411324249
continue;
2411424250
}
24251
+ if( c==0 ) break;
2411524252
if( z[i]=='\t' ){
2411624253
do{
2411724254
n++;
2411824255
zOut[j++] = ' ';
2411924256
}while( (n&7)!=0 && n<mxWidth );
2412024257
i++;
2412124258
continue;
2412224259
}
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++;
2412424275
}
2412524276
zOut[j] = 0;
2412624277
return (char*)zOut;
2412724278
}
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
+}
2412824289
2412924290
/* Extract the value of the i-th current column for pStmt as an SQL literal
2413024291
** value. Memory is obtained from sqlite3_malloc64() and must be freed by
2413124292
** the caller.
2413224293
*/
@@ -24138,11 +24299,12 @@
2413824299
case SQLITE_INTEGER:
2413924300
case SQLITE_FLOAT: {
2414024301
return sqlite3_mprintf("%s",sqlite3_column_text(pStmt,i));
2414124302
}
2414224303
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);
2414424306
}
2414524307
case SQLITE_BLOB: {
2414624308
int j;
2414724309
sqlite3_str *pStr = sqlite3_str_new(0);
2414824310
const unsigned char *a = sqlite3_column_blob(pStmt,i);
@@ -24230,11 +24392,11 @@
2423024392
wx = p->cmOpts.iWrap;
2423124393
}
2423224394
if( wx<0 ) wx = -wx;
2423324395
uz = (const unsigned char*)sqlite3_column_name(pStmt,i);
2423424396
if( uz==0 ) uz = (u8*)"";
24235
- azData[i] = translateForDisplayAndDup(uz, &zNotUsed, wx, bw);
24397
+ azData[i] = translateForDisplayAndDup(p, uz, &zNotUsed, wx, bw);
2423624398
}
2423724399
do{
2423824400
int useNextLine = bNextLine;
2423924401
bNextLine = 0;
2424024402
if( (nRow+2)*nColumn >= nAlloc ){
@@ -24254,19 +24416,20 @@
2425424416
if( wx<0 ) wx = -wx;
2425524417
if( useNextLine ){
2425624418
uz = azNextLine[i];
2425724419
if( uz==0 ) uz = (u8*)zEmpty;
2425824420
}else if( p->cmOpts.bQuote ){
24421
+ assert( azQuoted!=0 );
2425924422
sqlite3_free(azQuoted[i]);
2426024423
azQuoted[i] = quoted_column(pStmt,i);
2426124424
uz = (const unsigned char*)azQuoted[i];
2426224425
}else{
2426324426
uz = (const unsigned char*)sqlite3_column_text(pStmt,i);
2426424427
if( uz==0 ) uz = (u8*)zShowNull;
2426524428
}
2426624429
azData[nRow*nColumn + i]
24267
- = translateForDisplayAndDup(uz, &azNextLine[i], wx, bw);
24430
+ = translateForDisplayAndDup(p, uz, &azNextLine[i], wx, bw);
2426824431
if( azNextLine[i] ){
2426924432
bNextLine = 1;
2427024433
abRowDiv[nRow-1] = 0;
2427124434
bMultiLineRowExists = 1;
2427224435
}
@@ -25185,11 +25348,11 @@
2518525348
#if !defined(SQLITE_SHELL_FIDDLE)
2518625349
".log FILE|on|off Turn logging on or off. FILE can be stderr/stdout",
2518725350
#else
2518825351
".log on|off Turn logging on or off.",
2518925352
#endif
25190
- ".mode MODE ?OPTIONS? Set output mode",
25353
+ ".mode ?MODE? ?OPTIONS? Set output mode",
2519125354
" MODE is one of:",
2519225355
" ascii Columns/rows delimited by 0x1F and 0x1E",
2519325356
" box Tables using unicode box-drawing characters",
2519425357
" csv Comma-separated values",
2519525358
" column Output in columns. (See .width)",
@@ -25203,10 +25366,11 @@
2520325366
" quote Escape answers as for SQL",
2520425367
" table ASCII-art table",
2520525368
" tabs Tab-separated values",
2520625369
" tcl TCL list elements",
2520725370
" OPTIONS: (for columnar modes or insert mode):",
25371
+ " --escape T ctrl-char escape; T is one of: symbol, ascii, off",
2520825372
" --wrap N Wrap output lines to no longer than N characters",
2520925373
" --wordwrap B Wrap or not at word boundaries per B (on/off)",
2521025374
" --ww Shorthand for \"--wordwrap 1\"",
2521125375
" --quote Quote output text as SQL literals",
2521225376
" --noquote Do not quote output text",
@@ -25978,11 +26142,11 @@
2597826142
#if HAVE_LINENOISE==2
2597926143
UNUSED_PARAMETER(pUserData);
2598026144
#endif
2598126145
if( nLine>(i64)sizeof(zBuf)-30 ) return;
2598226146
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--){}
2598426148
if( i==nLine-1 ) return;
2598526149
iStart = i+1;
2598626150
memcpy(zBuf, zLine, iStart);
2598726151
zSql = sqlite3_mprintf("SELECT DISTINCT candidate COLLATE nocase"
2598826152
" FROM completion(%Q,%Q) ORDER BY 1",
@@ -28218,10 +28382,11 @@
2821828382
sqlite3_recover_config(p, 789, (void*)zRecoveryDb); /* Debug use only */
2821928383
sqlite3_recover_config(p, SQLITE_RECOVER_LOST_AND_FOUND, (void*)zLAF);
2822028384
sqlite3_recover_config(p, SQLITE_RECOVER_ROWIDS, (void*)&bRowids);
2822128385
sqlite3_recover_config(p, SQLITE_RECOVER_FREELIST_CORRUPT,(void*)&bFreelist);
2822228386
28387
+ sqlite3_fprintf(pState->out, ".dbconfig defensive off\n");
2822328388
sqlite3_recover_run(p);
2822428389
if( sqlite3_recover_errcode(p)!=SQLITE_OK ){
2822528390
const char *zErr = sqlite3_recover_errmsg(p);
2822628391
int errCode = sqlite3_recover_errcode(p);
2822728392
sqlite3_fprintf(stderr,"sql error: %s (%d)\n", zErr, errCode);
@@ -29943,28 +30108,56 @@
2994330108
2994430109
if( c=='m' && cli_strncmp(azArg[0], "mode", n)==0 ){
2994530110
const char *zMode = 0;
2994630111
const char *zTabname = 0;
2994730112
int i, n2;
30113
+ int chng = 0; /* 0x01: change to cmopts. 0x02: Any other change */
2994830114
ColModeOpts cmOpts = ColModeOpts_default;
2994930115
for(i=1; i<nArg; i++){
2995030116
const char *z = azArg[i];
2995130117
if( optionMatch(z,"wrap") && i+1<nArg ){
2995230118
cmOpts.iWrap = integerValue(azArg[++i]);
30119
+ chng |= 1;
2995330120
}else if( optionMatch(z,"ww") ){
2995430121
cmOpts.bWordWrap = 1;
30122
+ chng |= 1;
2995530123
}else if( optionMatch(z,"wordwrap") && i+1<nArg ){
2995630124
cmOpts.bWordWrap = (u8)booleanValue(azArg[++i]);
30125
+ chng |= 1;
2995730126
}else if( optionMatch(z,"quote") ){
2995830127
cmOpts.bQuote = 1;
30128
+ chng |= 1;
2995930129
}else if( optionMatch(z,"noquote") ){
2996030130
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
+ }
2996130153
}else if( zMode==0 ){
2996230154
zMode = z;
2996330155
/* Apply defaults for qbox pseudo-mode. If that
2996430156
* overwrites already-set values, user was informed of this.
2996530157
*/
30158
+ chng |= 1;
2996630159
if( cli_strcmp(z, "qbox")==0 ){
2996730160
ColModeOpts cmo = ColModeOpts_default_qbox;
2996830161
zMode = "box";
2996930162
cmOpts = cmo;
2997030163
}
@@ -29971,10 +30164,11 @@
2997130164
}else if( zTabname==0 ){
2997230165
zTabname = z;
2997330166
}else if( z[0]=='-' ){
2997430167
sqlite3_fprintf(stderr,"unknown option: %s\n", z);
2997530168
eputz("options:\n"
30169
+ " --escape MODE\n"
2997630170
" --noquote\n"
2997730171
" --quote\n"
2997830172
" --wordwrap on/off\n"
2997930173
" --wrap N\n"
2998030174
" --ww\n");
@@ -29984,24 +30178,33 @@
2998430178
sqlite3_fprintf(stderr,"extra argument: \"%s\"\n", z);
2998530179
rc = 1;
2998630180
goto meta_command_exit;
2998730181
}
2998830182
}
29989
- if( zMode==0 ){
30183
+ if( !chng ){
2999030184
if( p->mode==MODE_Column
2999130185
|| (p->mode>=MODE_Markdown && p->mode<=MODE_Box)
2999230186
){
2999330187
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",
2999530190
modeDescr[p->mode], p->cmOpts.iWrap,
2999630191
p->cmOpts.bWordWrap ? "on" : "off",
29997
- p->cmOpts.bQuote ? "" : "no");
30192
+ p->cmOpts.bQuote ? "" : "no",
30193
+ shell_EscModeNames[p->eEscMode]
30194
+ );
2999830195
}else{
2999930196
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
+ );
3000130201
}
30202
+ }
30203
+ if( zMode==0 ){
3000230204
zMode = modeDescr[p->mode];
30205
+ if( (chng&1)==0 ) cmOpts = p->cmOpts;
3000330206
}
3000430207
n2 = strlen30(zMode);
3000530208
if( cli_strncmp(zMode,"lines",n2)==0 ){
3000630209
p->mode = MODE_Line;
3000730210
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row);
@@ -30030,10 +30233,15 @@
3003030233
p->mode = MODE_List;
3003130234
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Tab);
3003230235
}else if( cli_strncmp(zMode,"insert",n2)==0 ){
3003330236
p->mode = MODE_Insert;
3003430237
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
+ }
3003530243
}else if( cli_strncmp(zMode,"quote",n2)==0 ){
3003630244
p->mode = MODE_Quote;
3003730245
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma);
3003830246
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_Row);
3003930247
}else if( cli_strncmp(zMode,"ascii",n2)==0 ){
@@ -32266,11 +32474,11 @@
3226632474
/*
3226732475
** The CLI needs a working sqlite3_complete() to work properly. So error
3226832476
** out of the build if compiling with SQLITE_OMIT_COMPLETE.
3226932477
*/
3227032478
#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.
3227232480
#endif
3227332481
3227432482
/*
3227532483
** Return true if zSql is a complete SQL statement. Return false if it
3227632484
** ends in the middle of a string literal or C-style comment.
@@ -32445,11 +32653,11 @@
3244532653
UNUSED_PARAMETER(in);
3244632654
UNUSED_PARAMETER(isContinuation);
3244732655
if(!z || !*z){
3244832656
return 0;
3244932657
}
32450
- while(*z && isspace(*z)) ++z;
32658
+ while(*z && IsSpace(*z)) ++z;
3245132659
zBegin = z;
3245232660
for(; *z && '\n'!=*z; ++nZ, ++z){}
3245332661
if(nZ>0 && '\r'==zBegin[nZ-1]){
3245432662
--nZ;
3245532663
}
@@ -32750,10 +32958,11 @@
3275032958
" -csv set output mode to 'csv'\n"
3275132959
#if !defined(SQLITE_OMIT_DESERIALIZE)
3275232960
" -deserialize open the database using sqlite3_deserialize()\n"
3275332961
#endif
3275432962
" -echo print inputs before execution\n"
32963
+ " -escape T ctrl-char escape; T is one of: symbol, ascii, off\n"
3275532964
" -init FILENAME read/process named file\n"
3275632965
" -[no]header turn headers on or off\n"
3275732966
#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
3275832967
" -heap SIZE Size of heap for memsys3 or memsys5\n"
3275932968
#endif
@@ -32797,11 +33006,11 @@
3279733006
#ifdef SQLITE_HAVE_ZLIB
3279833007
" -zip open the file as a ZIP Archive\n"
3279933008
#endif
3280033009
;
3280133010
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"
3280333012
"FILENAME is the name of an SQLite database. A new database is created\n"
3280433013
"if the file does not previously exist. Defaults to :memory:.\n", Argv0);
3280533014
if( showDetail ){
3280633015
sqlite3_fprintf(stderr,"OPTIONS include:\n%s", zOptions);
3280733016
}else{
@@ -33183,10 +33392,13 @@
3318333392
data.zNonce = strdup(cmdline_option_value(argc, argv, ++i));
3318433393
}else if( cli_strcmp(z,"-unsafe-testing")==0 ){
3318533394
ShellSetFlag(&data,SHFLG_TestingMode);
3318633395
}else if( cli_strcmp(z,"-safe")==0 ){
3318733396
/* 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++;
3318833400
}
3318933401
}
3319033402
#ifndef SQLITE_SHELL_FIDDLE
3319133403
if( !bEnableVfstrace ) verify_uninitialized();
3319233404
#endif
@@ -33282,10 +33494,29 @@
3328233494
}else if( cli_strcmp(z,"-box")==0 ){
3328333495
data.mode = MODE_Box;
3328433496
}else if( cli_strcmp(z,"-csv")==0 ){
3328533497
data.mode = MODE_Csv;
3328633498
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
+ }
3328733518
#ifdef SQLITE_HAVE_ZLIB
3328833519
}else if( cli_strcmp(z,"-zip")==0 ){
3328933520
data.openMode = SHELL_OPEN_ZIPFILE;
3329033521
#endif
3329133522
}else if( cli_strcmp(z,"-append")==0 ){
@@ -33443,19 +33674,19 @@
3344333674
/* Run all arguments that do not begin with '-' as if they were separate
3344433675
** command-line inputs, except for the argToSkip argument which contains
3344533676
** the database filename.
3344633677
*/
3344733678
for(i=0; i<nCmd; i++){
33679
+ echo_group_input(&data, azCmd[i]);
3344833680
if( azCmd[i][0]=='.' ){
3344933681
rc = do_meta_command(azCmd[i], &data);
3345033682
if( rc ){
3345133683
if( rc==2 ) rc = 0;
3345233684
goto shell_main_exit;
3345333685
}
3345433686
}else{
3345533687
open_db(&data, 0);
33456
- echo_group_input(&data, azCmd[i]);
3345733688
rc = shell_exec(&data, azCmd[i], &zErrMsg);
3345833689
if( zErrMsg || rc ){
3345933690
if( zErrMsg!=0 ){
3346033691
shellEmitError(zErrMsg);
3346133692
}else{
3346233693
--- 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 @@
1616
** if you want a wrapper to interface SQLite with your choice of programming
1717
** language. The code for the "sqlite3" command-line shell is also in a
1818
** separate file. This file contains only code for the core SQLite library.
1919
**
2020
** The content in this amalgamation comes from Fossil check-in
21
-** 57caa3136d1bfca06e4f2285734a4977b8d3 with changes in files:
21
+** 18bda13e197e4b4ec7464b3e70012f71edc0 with changes in files:
2222
**
2323
**
2424
*/
2525
#ifndef SQLITE_AMALGAMATION
2626
#define SQLITE_CORE 1
@@ -465,11 +465,11 @@
465465
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
466466
** [sqlite_version()] and [sqlite_source_id()].
467467
*/
468468
#define SQLITE_VERSION "3.50.0"
469469
#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"
471471
472472
/*
473473
** CAPI3REF: Run-Time Library Version Numbers
474474
** KEYWORDS: sqlite3_version sqlite3_sourceid
475475
**
@@ -1479,10 +1479,16 @@
14791479
** to block for up to M milliseconds before failing when attempting to
14801480
** obtain a file lock using the xLock or xShmLock methods of the VFS.
14811481
** The parameter is a pointer to a 32-bit signed integer that contains
14821482
** the value that M is to be set to. Before returning, the 32-bit signed
14831483
** 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.
14841490
**
14851491
** <li>[[SQLITE_FCNTL_DATA_VERSION]]
14861492
** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to
14871493
** a database file. The argument is a pointer to a 32-bit unsigned integer.
14881494
** The "data version" for the pager is written into the pointer. The
@@ -1576,10 +1582,11 @@
15761582
#define SQLITE_FCNTL_CKPT_START 39
15771583
#define SQLITE_FCNTL_EXTERNAL_READER 40
15781584
#define SQLITE_FCNTL_CKSM_FILE 41
15791585
#define SQLITE_FCNTL_RESET_CACHE 42
15801586
#define SQLITE_FCNTL_NULL_IO 43
1587
+#define SQLITE_FCNTL_BLOCK_ON_CONNECT 44
15811588
15821589
/* deprecated names */
15831590
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
15841591
#define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE
15851592
#define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO
@@ -3332,10 +3339,48 @@
33323339
**
33333340
** See also: [PRAGMA busy_timeout]
33343341
*/
33353342
SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms);
33363343
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
+
33373382
/*
33383383
** CAPI3REF: Convenience Routines For Running Queries
33393384
** METHOD: sqlite3
33403385
**
33413386
** This is a legacy interface that is preserved for backwards compatibility.
@@ -5447,11 +5492,11 @@
54475492
** For all versions of SQLite up to and including 3.6.23.1, a call to
54485493
** [sqlite3_reset()] was required after sqlite3_step() returned anything
54495494
** other than [SQLITE_ROW] before any subsequent invocation of
54505495
** sqlite3_step(). Failure to reset the prepared statement using
54515496
** [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]),
54535498
** sqlite3_step() began
54545499
** calling [sqlite3_reset()] automatically in this circumstance rather
54555500
** than returning [SQLITE_MISUSE]. This is not considered a compatibility
54565501
** break because any application that ever receives an SQLITE_MISUSE error
54575502
** is broken by definition. The [SQLITE_OMIT_AUTORESET] compile-time option
@@ -7343,10 +7388,12 @@
73437388
** ^Any callback set by a previous call to this function
73447389
** for the same database connection is overridden.
73457390
**
73467391
** ^The second argument is a pointer to the function to invoke when a
73477392
** 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.
73487395
** ^The first argument to the callback is a copy of the third argument
73497396
** to sqlite3_update_hook().
73507397
** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE],
73517398
** or [SQLITE_UPDATE], depending on the operation that caused the callback
73527399
** to be invoked.
@@ -14097,18 +14144,26 @@
1409714144
** * Terms in the SET clause of an UPDATE statement
1409814145
** * Terms in the result set of a SELECT statement
1409914146
** * Terms in the GROUP BY or ORDER BY clauses of a SELECT statement.
1410014147
** * Terms in the VALUES clause of an INSERT statement
1410114148
**
14102
-** The hard upper limit here is 32676. Most database people will
14149
+** The hard upper limit here is 32767. Most database people will
1410314150
** tell you that in a well-normalized database, you usually should
1410414151
** not have more than a dozen or so columns in any table. And if
1410514152
** that is the case, there is no point in having more than a few
1410614153
** 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.
1410714160
*/
14108
-#ifndef SQLITE_MAX_COLUMN
14161
+#if !defined(SQLITE_MAX_COLUMN)
1410914162
# define SQLITE_MAX_COLUMN 2000
14163
+#elif SQLITE_MAX_COLUMN>32767
14164
+# error SQLITE_MAX_COLUMN may not exceed 32767
1411014165
#endif
1411114166
1411214167
/*
1411314168
** The maximum length of a single SQL statement in bytes.
1411414169
**
@@ -15117,11 +15172,21 @@
1511715172
/*
1511815173
** GCC does not define the offsetof() macro so we'll have to do it
1511915174
** ourselves.
1512015175
*/
1512115176
#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
1512315188
#endif
1512415189
1512515190
/*
1512615191
** Macros to compute minimum and maximum of two numbers.
1512715192
*/
@@ -17352,12 +17417,12 @@
1735217417
SQLITE_PRIVATE int sqlite3NotPureFunc(sqlite3_context*);
1735317418
#ifdef SQLITE_ENABLE_BYTECODE_VTAB
1735417419
SQLITE_PRIVATE int sqlite3VdbeBytecodeVtabInit(sqlite3*);
1735517420
#endif
1735617421
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.
1735917424
**
1736017425
** Use the SQLITE_ENABLE_MODULE_COMMENTS macro to see some extra no-op
1736117426
** comments in VDBE programs that show key decision points in the code
1736217427
** generator.
1736317428
*/
@@ -18076,10 +18141,14 @@
1807618141
BusyHandler busyHandler; /* Busy callback */
1807718142
Db aDbStatic[2]; /* Static space for the 2 default backends */
1807818143
Savepoint *pSavepoint; /* List of active savepoints */
1807918144
int nAnalysisLimit; /* Number of index rows to ANALYZE */
1808018145
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
1808118150
int nSavepoint; /* Number of non-transaction savepoints */
1808218151
int nStatement; /* Number of nested statement-transactions */
1808318152
i64 nDeferredCons; /* Net deferred constraints this transaction. */
1808418153
i64 nDeferredImmCons; /* Net deferred immediate constraints */
1808518154
int *pnBytesFreed; /* If not NULL, increment this in DbFree() */
@@ -18888,12 +18957,16 @@
1888818957
u8 aAction[2]; /* ON DELETE and ON UPDATE actions, respectively */
1888918958
Trigger *apTrigger[2];/* Triggers for aAction[] actions */
1889018959
struct sColMap { /* Mapping of columns in pFrom to columns in zTo */
1889118960
int iFrom; /* Index of column in pFrom */
1889218961
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 */
1889418963
};
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))
1889518968
1889618969
/*
1889718970
** SQLite supports many different ways to resolve a constraint
1889818971
** error. ROLLBACK processing means that a constraint violation
1889918972
** causes the operation in process to fail and for the current transaction
@@ -18952,13 +19025,16 @@
1895219025
u8 enc; /* Text encoding - one of the SQLITE_UTF* values */
1895319026
u16 nKeyField; /* Number of key columns in the index */
1895419027
u16 nAllField; /* Total columns, including key plus others */
1895519028
sqlite3 *db; /* The database connection */
1895619029
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 */
1895819031
};
1895919032
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
+
1896019036
/*
1896119037
** Allowed bit values for entries in the KeyInfo.aSortFlags[] array.
1896219038
*/
1896319039
#define KEYINFO_ORDER_DESC 0x01 /* DESC sort order */
1896419040
#define KEYINFO_ORDER_BIGNULL 0x02 /* NULL is larger than any other value */
@@ -19074,11 +19150,11 @@
1907419150
Expr *pPartIdxWhere; /* WHERE clause for partial indices */
1907519151
ExprList *aColExpr; /* Column expressions */
1907619152
Pgno tnum; /* DB Page containing root of this index */
1907719153
LogEst szIdxRow; /* Estimated average row size in bytes */
1907819154
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 */
1908019156
u8 onError; /* OE_Abort, OE_Ignore, OE_Replace, or OE_None */
1908119157
unsigned idxType:2; /* 0:Normal 1:UNIQUE, 2:PRIMARY KEY, 3:IPK */
1908219158
unsigned bUnordered:1; /* Use this index for == or IN queries only */
1908319159
unsigned uniqNotNull:1; /* True if UNIQUE and NOT NULL for all columns */
1908419160
unsigned isResized:1; /* True if resizeIndexObject() has been called */
@@ -19412,14 +19488,14 @@
1941219488
#define EP_Propagate (EP_Collate|EP_Subquery|EP_HasFunc)
1941319489
1941419490
/* Macros can be used to test, set, or clear bits in the
1941519491
** Expr.flags field.
1941619492
*/
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)
1942119497
#define ExprAlwaysTrue(E) (((E)->flags&(EP_OuterON|EP_IsTrue))==EP_IsTrue)
1942219498
#define ExprAlwaysFalse(E) (((E)->flags&(EP_OuterON|EP_IsFalse))==EP_IsFalse)
1942319499
#define ExprIsFullSize(E) (((E)->flags&(EP_Reduced|EP_TokenOnly))==0)
1942419500
1942519501
/* Macros used to ensure that the correct members of unions are accessed
@@ -19527,12 +19603,17 @@
1952719603
u16 iAlias; /* Index into Parse.aAlias[] for zName */
1952819604
} x;
1952919605
int iConstExprReg; /* Register in which Expr value is cached. Used only
1953019606
** by Parse.pConstExpr */
1953119607
} u;
19532
- } a[1]; /* One slot for each expression in the list */
19608
+ } a[FLEXARRAY]; /* One slot for each expression in the list */
1953319609
};
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))
1953419615
1953519616
/*
1953619617
** Allowed values for Expr.a.eEName
1953719618
*/
1953819619
#define ENAME_NAME 0 /* The AS clause of a result set */
@@ -19557,13 +19638,16 @@
1955719638
*/
1955819639
struct IdList {
1955919640
int nId; /* Number of identifiers on the list */
1956019641
struct IdList_item {
1956119642
char *zName; /* Name of the identifier */
19562
- } a[1];
19643
+ } a[FLEXARRAY];
1956319644
};
1956419645
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
+
1956519649
/*
1956619650
** Allowed values for IdList.eType, which determines which value of the a.u4
1956719651
** is valid.
1956819652
*/
1956919653
#define EU4_NONE 0 /* Does not use IdList.a.u4 */
@@ -19679,14 +19763,22 @@
1967919763
** is used to hold the FROM clause of a SELECT statement. SrcList also
1968019764
** represents the target tables for DELETE, INSERT, and UPDATE statements.
1968119765
**
1968219766
*/
1968319767
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 */
1968719771
};
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))
1968819780
1968919781
/*
1969019782
** Permitted values of the SrcList.a.jointype field
1969119783
*/
1969219784
#define JT_INNER 0x01 /* Any kind of inner or cross join */
@@ -20747,12 +20839,16 @@
2074720839
*/
2074820840
struct With {
2074920841
int nCte; /* Number of CTEs in the WITH clause */
2075020842
int bView; /* Belongs to the outermost Select of a view */
2075120843
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.... */
2075320845
};
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))
2075420850
2075520851
/*
2075620852
** The Cte object is not guaranteed to persist for the entire duration
2075720853
** of code generation. (The query flattener or other parser tree
2075820854
** edits might delete it.) The following object records information
@@ -20778,12 +20874,16 @@
2077820874
*/
2077920875
struct DbClientData {
2078020876
DbClientData *pNext; /* Next in a linked list */
2078120877
void *pData; /* The data */
2078220878
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 */
2078420880
};
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))
2078520885
2078620886
#ifdef SQLITE_DEBUG
2078720887
/*
2078820888
** An instance of the TreeView object is used for printing the content of
2078920889
** data structures on sqlite3DebugPrintf() using a tree-like view.
@@ -21223,11 +21323,11 @@
2122321323
SQLITE_PRIVATE int sqlite3ColumnsFromExprList(Parse*,ExprList*,i16*,Column**);
2122421324
SQLITE_PRIVATE void sqlite3SubqueryColumnTypes(Parse*,Table*,Select*,char);
2122521325
SQLITE_PRIVATE Table *sqlite3ResultSetOfSelect(Parse*,Select*,char);
2122621326
SQLITE_PRIVATE void sqlite3OpenSchemaTable(Parse *, int);
2122721327
SQLITE_PRIVATE Index *sqlite3PrimaryKeyIndex(Table*);
21228
-SQLITE_PRIVATE i16 sqlite3TableColumnToIndex(Index*, i16);
21328
+SQLITE_PRIVATE int sqlite3TableColumnToIndex(Index*, int);
2122921329
#ifdef SQLITE_OMIT_GENERATED_COLUMNS
2123021330
# define sqlite3TableColumnToStorage(T,X) (X) /* No-op pass-through */
2123121331
# define sqlite3StorageColumnToTable(T,X) (X) /* No-op pass-through */
2123221332
#else
2123321333
SQLITE_PRIVATE i16 sqlite3TableColumnToStorage(Table*, i16);
@@ -21321,11 +21421,11 @@
2132121421
SQLITE_PRIVATE void sqlite3SrcListShiftJoinType(Parse*,SrcList*);
2132221422
SQLITE_PRIVATE void sqlite3SrcListAssignCursors(Parse*, SrcList*);
2132321423
SQLITE_PRIVATE void sqlite3IdListDelete(sqlite3*, IdList*);
2132421424
SQLITE_PRIVATE void sqlite3ClearOnOrUsing(sqlite3*, OnOrUsing*);
2132521425
SQLITE_PRIVATE void sqlite3SrcListDelete(sqlite3*, SrcList*);
21326
-SQLITE_PRIVATE Index *sqlite3AllocateIndexObject(sqlite3*,i16,int,char**);
21426
+SQLITE_PRIVATE Index *sqlite3AllocateIndexObject(sqlite3*,int,int,char**);
2132721427
SQLITE_PRIVATE void sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*,
2132821428
Expr*, int, int, u8);
2132921429
SQLITE_PRIVATE void sqlite3DropIndex(Parse*, SrcList*, int);
2133021430
SQLITE_PRIVATE int sqlite3Select(Parse*, Select*, SelectDest*);
2133121431
SQLITE_PRIVATE Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*,
@@ -21457,11 +21557,12 @@
2145721557
SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3*,const IdList*);
2145821558
SQLITE_PRIVATE Select *sqlite3SelectDup(sqlite3*,const Select*,int);
2145921559
SQLITE_PRIVATE FuncDef *sqlite3FunctionSearch(int,const char*);
2146021560
SQLITE_PRIVATE void sqlite3InsertBuiltinFuncs(FuncDef*,int);
2146121561
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);
2146321564
SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void);
2146421565
SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void);
2146521566
SQLITE_PRIVATE void sqlite3RegisterJsonFunctions(void);
2146621567
SQLITE_PRIVATE void sqlite3RegisterPerConnectionBuiltinFunctions(sqlite3*);
2146721568
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON)
@@ -22557,10 +22658,13 @@
2255722658
#ifdef SQLITE_ENABLE_RTREE
2255822659
"ENABLE_RTREE",
2255922660
#endif
2256022661
#ifdef SQLITE_ENABLE_SESSION
2256122662
"ENABLE_SESSION",
22663
+#endif
22664
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
22665
+ "ENABLE_SETLK_TIMEOUT",
2256222666
#endif
2256322667
#ifdef SQLITE_ENABLE_SNAPSHOT
2256422668
"ENABLE_SNAPSHOT",
2256522669
#endif
2256622670
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
@@ -22612,10 +22716,13 @@
2261222716
"EXTRA_IFNULLROW",
2261322717
#endif
2261422718
#ifdef SQLITE_EXTRA_INIT
2261522719
"EXTRA_INIT=" CTIMEOPT_VAL(SQLITE_EXTRA_INIT),
2261622720
#endif
22721
+#ifdef SQLITE_EXTRA_INIT_MUTEXED
22722
+ "EXTRA_INIT_MUTEXED=" CTIMEOPT_VAL(SQLITE_EXTRA_INIT_MUTEXED),
22723
+#endif
2261722724
#ifdef SQLITE_EXTRA_SHUTDOWN
2261822725
"EXTRA_SHUTDOWN=" CTIMEOPT_VAL(SQLITE_EXTRA_SHUTDOWN),
2261922726
#endif
2262022727
#ifdef SQLITE_FTS3_MAX_EXPR_DEPTH
2262122728
"FTS3_MAX_EXPR_DEPTH=" CTIMEOPT_VAL(SQLITE_FTS3_MAX_EXPR_DEPTH),
@@ -23596,16 +23703,23 @@
2359623703
#ifdef SQLITE_ENABLE_COLUMN_USED_MASK
2359723704
u64 maskUsed; /* Mask of columns used by this cursor */
2359823705
#endif
2359923706
VdbeTxtBlbCache *pCache; /* Cache of large TEXT or BLOB values */
2360023707
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 */
2360523711
};
2360623712
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
+
2360723721
/* Return true if P is a null-only cursor
2360823722
*/
2360923723
#define IsNullCursor(P) \
2361023724
((P)->eCurType==CURTYPE_PSEUDO && (P)->nullRow && (P)->seekResult==0)
2361123725
@@ -23858,13 +23972,20 @@
2385823972
int iOp; /* Instruction number of OP_Function */
2385923973
int isError; /* Error code returned by the function. */
2386023974
u8 enc; /* Encoding to use for results */
2386123975
u8 skipFlag; /* Skip accumulator loading if true */
2386223976
u16 argc; /* Number of arguments */
23863
- sqlite3_value *argv[1]; /* Argument set */
23977
+ sqlite3_value *argv[FLEXARRAY]; /* Argument set */
2386423978
};
2386523979
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
+
2386623987
2386723988
/* The ScanStatus object holds a single value for the
2386823989
** sqlite3_stmt_scanstatus() interface.
2386923990
**
2387023991
** aAddrRange[]:
@@ -23994,11 +24115,11 @@
2399424115
struct PreUpdate {
2399524116
Vdbe *v;
2399624117
VdbeCursor *pCsr; /* Cursor to read old values from */
2399724118
int op; /* One of SQLITE_INSERT, UPDATE, DELETE */
2399824119
u8 *aRecord; /* old.* database record */
23999
- KeyInfo keyinfo;
24120
+ KeyInfo *pKeyinfo; /* Key information */
2400024121
UnpackedRecord *pUnpacked; /* Unpacked version of aRecord[] */
2400124122
UnpackedRecord *pNewUnpacked; /* Unpacked version of new.* record */
2400224123
int iNewReg; /* Register for new.* values */
2400324124
int iBlobWrite; /* Value returned by preupdate_blobwrite() */
2400424125
i64 iKey1; /* First key value passed to hook */
@@ -24006,10 +24127,11 @@
2400624127
Mem oldipk; /* Memory cell holding "old" IPK value */
2400724128
Mem *aNew; /* Array of new.* values */
2400824129
Table *pTab; /* Schema object being updated */
2400924130
Index *pPk; /* PK index if pTab is WITHOUT ROWID */
2401024131
sqlite3_value **apDflt; /* Array of default values, if required */
24132
+ u8 keyinfoSpace[SZ_KEYINFO(0)]; /* Space to hold pKeyinfo[0] content */
2401124133
};
2401224134
2401324135
/*
2401424136
** An instance of this object is used to pass an vector of values into
2401524137
** OP_VFilter, the xFilter method of a virtual table. The vector is the
@@ -24372,12 +24494,13 @@
2437224494
u32 nFree = countLookasideSlots(db->lookaside.pFree);
2437324495
#ifndef SQLITE_OMIT_TWOSIZE_LOOKASIDE
2437424496
nInit += countLookasideSlots(db->lookaside.pSmallInit);
2437524497
nFree += countLookasideSlots(db->lookaside.pSmallFree);
2437624498
#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));
2437924502
}
2438024503
2438124504
/*
2438224505
** Query status information for a single database connection
2438324506
*/
@@ -24426,11 +24549,11 @@
2442624549
testcase( op==SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE );
2442724550
testcase( op==SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL );
2442824551
assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)>=0 );
2442924552
assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)<3 );
2443024553
*pCurrent = 0;
24431
- *pHighwater = db->lookaside.anStat[op - SQLITE_DBSTATUS_LOOKASIDE_HIT];
24554
+ *pHighwater = (int)db->lookaside.anStat[op-SQLITE_DBSTATUS_LOOKASIDE_HIT];
2443224555
if( resetFlag ){
2443324556
db->lookaside.anStat[op - SQLITE_DBSTATUS_LOOKASIDE_HIT] = 0;
2443424557
}
2443524558
break;
2443624559
}
@@ -25938,11 +26061,11 @@
2593826061
** Return the number of days after the most recent Sunday.
2593926062
**
2594026063
** In other words, return the day of the week according
2594126064
** to this code:
2594226065
**
25943
-** 0=Sunday, 1=Monday, 2=Tues, ..., 6=Saturday
26066
+** 0=Sunday, 1=Monday, 2=Tuesday, ..., 6=Saturday
2594426067
*/
2594526068
static int daysAfterSunday(DateTime *pDate){
2594626069
assert( pDate->validJD );
2594726070
return (int)((pDate->iJD+129600000)/86400000) % 7;
2594826071
}
@@ -31541,21 +31664,21 @@
3154131664
#define etSTRING 5 /* Strings. %s */
3154231665
#define etDYNSTRING 6 /* Dynamically allocated strings. %z */
3154331666
#define etPERCENT 7 /* Percent symbol. %% */
3154431667
#define etCHARX 8 /* Characters. %c */
3154531668
/* 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 */
3155731680
3155831681
3155931682
/*
3156031683
** An "etByte" is an 8-bit unsigned value.
3156131684
*/
@@ -31590,13 +31713,13 @@
3159031713
static const et_info fmtinfo[] = {
3159131714
{ 'd', 10, 1, etDECIMAL, 0, 0 },
3159231715
{ 's', 0, 4, etSTRING, 0, 0 },
3159331716
{ 'g', 0, 1, etGENERIC, 30, 0 },
3159431717
{ '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 },
3159831721
{ 'c', 0, 0, etCHARX, 0, 0 },
3159931722
{ 'o', 8, 0, etRADIX, 0, 2 },
3160031723
{ 'u', 10, 0, etDECIMAL, 0, 0 },
3160131724
{ 'x', 16, 0, etRADIX, 16, 1 },
3160231725
{ 'X', 16, 0, etRADIX, 0, 4 },
@@ -32189,29 +32312,11 @@
3218932312
}else{
3219032313
buf[0] = 0;
3219132314
}
3219232315
}else{
3219332316
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);
3221332318
}
3221432319
if( precision>1 ){
3221532320
i64 nPrior = 1;
3221632321
width -= precision-1;
3221732322
if( width>1 && !flag_leftjustify ){
@@ -32287,26 +32392,35 @@
3228732392
/* Adjust width to account for extra bytes in UTF-8 characters */
3228832393
int ii = length - 1;
3228932394
while( ii>=0 ) if( (bufpt[ii--] & 0xc0)==0x80 ) width++;
3229032395
}
3229132396
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 */
3229532400
i64 i, j, k, n;
32296
- int needQuote, isnull;
32401
+ int needQuote = 0;
3229732402
char ch;
32298
- char q = ((xtype==etSQLESCAPE3)?'"':'\''); /* Quote character */
3229932403
char *escarg;
32404
+ char q;
3230032405
3230132406
if( bArgList ){
3230232407
escarg = getTextArg(pArgList);
3230332408
}else{
3230432409
escarg = va_arg(ap,char*);
3230532410
}
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
+ }
3230832422
/* For %q, %Q, and %w, the precision is the number of bytes (or
3230932423
** characters if the ! flags is present) to use from the input.
3231032424
** Because of the extra quoting characters inserted, the number
3231132425
** of output characters may be larger than the precision.
3231232426
*/
@@ -32315,26 +32429,77 @@
3231532429
if( ch==q ) n++;
3231632430
if( flag_altform2 && (ch&0xc0)==0xc0 ){
3231732431
while( (escarg[i+1]&0xc0)==0x80 ){ i++; }
3231832432
}
3231932433
}
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
+ }
3232132458
n += i + 3;
3232232459
if( n>etBUFSIZE ){
3232332460
bufpt = zExtra = printfTempBuf(pAccum, n);
3232432461
if( bufpt==0 ) return;
3232532462
}else{
3232632463
bufpt = buf;
3232732464
}
3232832465
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
+ }
3233032474
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
+ }
3233432496
}
32335
- if( needQuote ) bufpt[j++] = q;
32497
+ if( needQuote ){
32498
+ bufpt[j++] = '\'';
32499
+ if( needQuote==2 ) bufpt[j++] = ')';
32500
+ }
3233632501
bufpt[j] = 0;
3233732502
length = j;
3233832503
goto adjust_width_for_utf8;
3233932504
}
3234032505
case etTOKEN: {
@@ -34828,10 +34993,39 @@
3482834993
*zOut++ = (u8)(((c>>10)&0x003F) + (((c-0x10000)>>10)&0x00C0)); \
3482934994
*zOut++ = (u8)(0x00DC + ((c>>8)&0x03)); \
3483034995
*zOut++ = (u8)(c&0x00FF); \
3483134996
} \
3483234997
}
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
+}
3483335027
3483435028
/*
3483535029
** Translate a single UTF-8 character. Return the unicode value.
3483635030
**
3483735031
** During translation, assume that the byte that zTerm points
@@ -36934,11 +37128,11 @@
3693437128
return 0;
3693537129
#endif
3693637130
}
3693737131
3693837132
/*
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
3694037134
** if the integer has a value of -2147483648, return +2147483647
3694137135
*/
3694237136
SQLITE_PRIVATE int sqlite3AbsInt32(int x){
3694337137
if( x>=0 ) return x;
3694437138
if( x==(int)0x80000000 ) return 0x7fffffff;
@@ -38911,10 +39105,11 @@
3891139105
#if SQLITE_ENABLE_LOCKING_STYLE || defined(__APPLE__)
3891239106
unsigned fsFlags; /* cached details from statfs() */
3891339107
#endif
3891439108
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
3891539109
unsigned iBusyTimeout; /* Wait this many millisec on locks */
39110
+ int bBlockOnConnect; /* True to block for SHARED locks */
3891639111
#endif
3891739112
#if OS_VXWORKS
3891839113
struct vxworksFileId *pId; /* Unique file ID */
3891939114
#endif
3892039115
#ifdef SQLITE_DEBUG
@@ -40304,10 +40499,17 @@
4030440499
pInode->nLock++;
4030540500
}else{
4030640501
rc = 0;
4030740502
}
4030840503
}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
4030940511
rc = osSetPosixAdvisoryLock(pFile->h, pLock, pFile);
4031040512
}
4031140513
return rc;
4031240514
}
4031340515
@@ -42665,21 +42867,27 @@
4266542867
return SQLITE_OK;
4266642868
}
4266742869
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
4266842870
case SQLITE_FCNTL_LOCK_TIMEOUT: {
4266942871
int iOld = pFile->iBusyTimeout;
42872
+ int iNew = *(int*)pArg;
4267042873
#if SQLITE_ENABLE_SETLK_TIMEOUT==1
42671
- pFile->iBusyTimeout = *(int*)pArg;
42874
+ pFile->iBusyTimeout = iNew<0 ? 0x7FFFFFFF : (unsigned)iNew;
4267242875
#elif SQLITE_ENABLE_SETLK_TIMEOUT==2
4267342876
pFile->iBusyTimeout = !!(*(int*)pArg);
4267442877
#else
4267542878
# error "SQLITE_ENABLE_SETLK_TIMEOUT must be set to 1 or 2"
4267642879
#endif
4267742880
*(int*)pArg = iOld;
4267842881
return SQLITE_OK;
4267942882
}
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 */
4268142889
#if SQLITE_MAX_MMAP_SIZE>0
4268242890
case SQLITE_FCNTL_MMAP_SIZE: {
4268342891
i64 newLimit = *(i64*)pArg;
4268442892
int rc = SQLITE_OK;
4268542893
if( newLimit>sqlite3GlobalConfig.mxMmap ){
@@ -43658,11 +43866,11 @@
4365843866
** occur later in the above list than the lock being obtained may be
4365943867
** held.
4366043868
**
4366143869
** It is not permitted to block on the RECOVER lock.
4366243870
*/
43663
-#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
43871
+#if defined(SQLITE_ENABLE_SETLK_TIMEOUT) && defined(SQLITE_DEBUG)
4366443872
{
4366543873
u16 lockMask = (p->exclMask|p->sharedMask);
4366643874
assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || (
4366743875
(ofst!=2) /* not RECOVER */
4366843876
&& (ofst!=1 || lockMask==0 || lockMask==2)
@@ -45467,11 +45675,11 @@
4546745675
sp.tv_sec = microseconds / 1000000;
4546845676
sp.tv_nsec = (microseconds % 1000000) * 1000;
4546945677
4547045678
/* Almost all modern unix systems support nanosleep(). But if you are
4547145679
** 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
4547345681
** usleep() is available) in order to bypass the use of nanosleep() */
4547445682
nanosleep(&sp, NULL);
4547545683
4547645684
UNUSED_PARAMETER(NotUsed);
4547745685
return microseconds;
@@ -47188,11 +47396,21 @@
4718847396
HANDLE hMap; /* Handle for accessing memory mapping */
4718947397
void *pMapRegion; /* Area memory mapped */
4719047398
sqlite3_int64 mmapSize; /* Size of mapped region */
4719147399
sqlite3_int64 mmapSizeMax; /* Configured FCNTL_MMAP_SIZE value */
4719247400
#endif
47401
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
47402
+ DWORD iBusyTimeout; /* Wait this many millisec on locks */
47403
+ int bBlockOnConnect;
47404
+#endif
4719347405
};
47406
+
47407
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
47408
+# define winFileBusyTimeout(pDbFd) pDbFd->iBusyTimeout
47409
+#else
47410
+# define winFileBusyTimeout(pDbFd) 0
47411
+#endif
4719447412
4719547413
/*
4719647414
** The winVfsAppData structure is used for the pAppData member for all of the
4719747415
** Win32 VFS variants.
4719847416
*/
@@ -47623,10 +47841,16 @@
4762347841
#endif
4762447842
4762547843
#define osGetFullPathNameW ((DWORD(WINAPI*)(LPCWSTR,DWORD,LPWSTR, \
4762647844
LPWSTR*))aSyscall[25].pCurrent)
4762747845
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
+*/
4762847852
{ "GetLastError", (SYSCALL)GetLastError, 0 },
4762947853
4763047854
#define osGetLastError ((DWORD(WINAPI*)(VOID))aSyscall[26].pCurrent)
4763147855
4763247856
#if !defined(SQLITE_OMIT_LOAD_EXTENSION)
@@ -47905,15 +48129,17 @@
4790548129
#endif
4790648130
4790748131
#define osCreateEventExW ((HANDLE(WINAPI*)(LPSECURITY_ATTRIBUTES,LPCWSTR, \
4790848132
DWORD,DWORD))aSyscall[62].pCurrent)
4790948133
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
+*/
4791148140
{ "WaitForSingleObject", (SYSCALL)WaitForSingleObject, 0 },
47912
-#else
47913
- { "WaitForSingleObject", (SYSCALL)0, 0 },
47914
-#endif
4791548141
4791648142
#define osWaitForSingleObject ((DWORD(WINAPI*)(HANDLE, \
4791748143
DWORD))aSyscall[63].pCurrent)
4791848144
4791948145
#if !SQLITE_OS_WINCE
@@ -48056,10 +48282,44 @@
4805648282
#endif
4805748283
4805848284
#define osFlushViewOfFile \
4805948285
((BOOL(WINAPI*)(LPCVOID,SIZE_T))aSyscall[79].pCurrent)
4806048286
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
+
4806148321
}; /* End of the overrideable system calls */
4806248322
4806348323
/*
4806448324
** This is the xSetSystemCall() method of sqlite3_vfs for all of the
4806548325
** "win32" VFSes. Return SQLITE_OK upon successfully updating the
@@ -48354,11 +48614,13 @@
4835448614
(sInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) ? 2 : 1, 0);
4835548615
#endif
4835648616
}
4835748617
return osInterlockedCompareExchange(&sqlite3_os_type, 2, 2)==2;
4835848618
#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
+ ;
4836048622
#else
4836148623
/*
4836248624
** NOTE: All sub-platforms where the GetVersionEx[AW] functions are
4836348625
** deprecated are always assumed to be based on the NT kernel.
4836448626
*/
@@ -49440,10 +49702,89 @@
4944049702
return osLockFile(*phFile, offsetLow, offsetHigh, numBytesLow,
4944149703
numBytesHigh);
4944249704
}
4944349705
#endif
4944449706
}
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
+}
4944549786
4944649787
/*
4944749788
** Unlock a file region.
4944849789
*/
4944949790
static BOOL winUnlockFile(
@@ -49471,10 +49812,18 @@
4947149812
return osUnlockFile(*phFile, offsetLow, offsetHigh, numBytesLow,
4947249813
numBytesHigh);
4947349814
}
4947449815
#endif
4947549816
}
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
+}
4947649825
4947749826
/*****************************************************************************
4947849827
** The next group of routines implement the I/O methods specified
4947949828
** by the sqlite3_io_methods object.
4948049829
******************************************************************************/
@@ -49485,69 +49834,73 @@
4948549834
#ifndef INVALID_SET_FILE_POINTER
4948649835
# define INVALID_SET_FILE_POINTER ((DWORD)-1)
4948749836
#endif
4948849837
4948949838
/*
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.
4949349843
*/
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
+
4949549847
#if !SQLITE_OS_WINRT
4949649848
LONG upperBits; /* Most sig. 32 bits of new offset */
4949749849
LONG lowerBits; /* Least sig. 32 bits of new offset */
4949849850
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));
4950249851
4950349852
upperBits = (LONG)((iOffset>>32) & 0x7fffffff);
4950449853
lowerBits = (LONG)(iOffset & 0xffffffff);
49854
+
49855
+ dwRet = osSetFilePointer(h, lowerBits, &upperBits, FILE_BEGIN);
4950549856
4950649857
/* API oddity: If successful, SetFilePointer() returns a dword
4950749858
** containing the lower 32-bits of the new file-offset. Or, if it fails,
4950849859
** it returns INVALID_SET_FILE_POINTER. However according to MSDN,
4950949860
** INVALID_SET_FILE_POINTER may also be a valid new offset. So to determine
4951049861
** 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. */
4953149871
LARGE_INTEGER x; /* The new offset */
4953249872
BOOL bRet; /* Value returned by SetFilePointerEx() */
4953349873
4953449874
x.QuadPart = iOffset;
49535
- bRet = osSetFilePointerEx(pFile->h, x, 0, FILE_BEGIN);
49875
+ bRet = osSetFilePointerEx(h, x, 0, FILE_BEGIN);
4953649876
4953749877
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 ){
4953849896
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
+
4954949902
4955049903
#if SQLITE_MAX_MMAP_SIZE>0
4955149904
/* Forward references to VFS helper methods used for memory mapped files */
4955249905
static int winMapfile(winFile*, sqlite3_int64);
4955349906
static int winUnmapfile(winFile*);
@@ -49803,10 +50156,64 @@
4980350156
}
4980450157
OSTRACE(("WRITE pid=%lu, pFile=%p, file=%p, rc=SQLITE_OK\n",
4980550158
osGetCurrentProcessId(), pFile, pFile->h));
4980650159
return SQLITE_OK;
4980750160
}
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
+}
4980850215
4980950216
/*
4981050217
** Truncate an open file to a specified size
4981150218
*/
4981250219
static int winTruncate(sqlite3_file *id, sqlite3_int64 nByte){
@@ -50059,31 +50466,32 @@
5005950466
/*
5006050467
** Acquire a reader lock.
5006150468
** Different API routines are called depending on whether or not this
5006250469
** is Win9x or WinNT.
5006350470
*/
50064
-static int winGetReadLock(winFile *pFile){
50471
+static int winGetReadLock(winFile *pFile, int bBlock){
5006550472
int res;
50473
+ DWORD mask = ~(bBlock ? LOCKFILE_FAIL_IMMEDIATELY : 0);
5006650474
OSTRACE(("READ-LOCK file=%p, lock=%d\n", pFile->h, pFile->locktype));
5006750475
if( osIsNT() ){
5006850476
#if SQLITE_OS_WINCE
5006950477
/*
5007050478
** NOTE: Windows CE is handled differently here due its lack of the Win32
5007150479
** API LockFileEx.
5007250480
*/
5007350481
res = winceLockFile(&pFile->h, SHARED_FIRST, 0, 1, 0);
5007450482
#else
50075
- res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS, SHARED_FIRST, 0,
50483
+ res = winLockFile(&pFile->h, SQLITE_LOCKFILEEX_FLAGS&mask, SHARED_FIRST, 0,
5007650484
SHARED_SIZE, 0);
5007750485
#endif
5007850486
}
5007950487
#ifdef SQLITE_WIN32_HAS_ANSI
5008050488
else{
5008150489
int lk;
5008250490
sqlite3_randomness(sizeof(lk), &lk);
5008350491
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,
5008550493
SHARED_FIRST+pFile->sharedLockByte, 0, 1, 0);
5008650494
}
5008750495
#endif
5008850496
if( res == 0 ){
5008950497
pFile->lastErrno = osGetLastError();
@@ -50174,50 +50582,66 @@
5017450582
*/
5017550583
assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK );
5017650584
assert( locktype!=PENDING_LOCK );
5017750585
assert( locktype!=RESERVED_LOCK || pFile->locktype==SHARED_LOCK );
5017850586
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
5018050588
** a SHARED lock. If we are acquiring a SHARED lock, the acquisition of
5018150589
** the PENDING_LOCK byte is temporary.
5018250590
*/
5018350591
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)
5018650594
){
5018750595
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 ){
5019050605
/* Try 3 times to get the pending lock. This is needed to work
5019150606
** around problems caused by indexing and/or anti-virus software on
5019250607
** Windows systems.
50608
+ **
5019350609
** 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
+
5019650614
lastErrno = osGetLastError();
5019750615
OSTRACE(("LOCK-PENDING-FAIL file=%p, count=%d, result=%d\n",
50198
- pFile->h, cnt, res));
50616
+ pFile->h, cnt, res
50617
+ ));
50618
+
5019950619
if( lastErrno==ERROR_INVALID_HANDLE ){
5020050620
pFile->lastErrno = lastErrno;
5020150621
rc = SQLITE_IOERR_LOCK;
5020250622
OSTRACE(("LOCK-FAIL file=%p, count=%d, rc=%s\n",
50203
- pFile->h, cnt, sqlite3ErrName(rc)));
50623
+ pFile->h, cnt, sqlite3ErrName(rc)
50624
+ ));
5020450625
return rc;
5020550626
}
50206
- if( cnt ) sqlite3_win32_sleep(1);
50627
+
50628
+ cnt--;
50629
+ if( cnt>0 ) sqlite3_win32_sleep(1);
5020750630
}
5020850631
gotPendingLock = res;
50209
- if( !res ){
50210
- lastErrno = osGetLastError();
50211
- }
5021250632
}
5021350633
5021450634
/* Acquire a shared lock
5021550635
*/
5021650636
if( locktype==SHARED_LOCK && res ){
5021750637
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
5021950643
if( res ){
5022050644
newLocktype = SHARED_LOCK;
5022150645
}else{
5022250646
lastErrno = osGetLastError();
5022350647
}
@@ -50251,11 +50675,11 @@
5025150675
SHARED_SIZE, 0);
5025250676
if( res ){
5025350677
newLocktype = EXCLUSIVE_LOCK;
5025450678
}else{
5025550679
lastErrno = osGetLastError();
50256
- winGetReadLock(pFile);
50680
+ winGetReadLock(pFile, 0);
5025750681
}
5025850682
}
5025950683
5026050684
/* If we are holding a PENDING lock that ought to be released, then
5026150685
** release it now.
@@ -50331,11 +50755,11 @@
5033150755
OSTRACE(("UNLOCK file=%p, oldLock=%d(%d), newLock=%d\n",
5033250756
pFile->h, pFile->locktype, pFile->sharedLockByte, locktype));
5033350757
type = pFile->locktype;
5033450758
if( type>=EXCLUSIVE_LOCK ){
5033550759
winUnlockFile(&pFile->h, SHARED_FIRST, 0, SHARED_SIZE, 0);
50336
- if( locktype==SHARED_LOCK && !winGetReadLock(pFile) ){
50760
+ if( locktype==SHARED_LOCK && !winGetReadLock(pFile, 0) ){
5033750761
/* This should never happen. We should always be able to
5033850762
** reacquire the read lock */
5033950763
rc = winLogError(SQLITE_IOERR_UNLOCK, osGetLastError(),
5034050764
"winUnlock", pFile->zPath);
5034150765
}
@@ -50541,10 +50965,32 @@
5054150965
}
5054250966
OSTRACE(("FCNTL file=%p, rc=%s\n", pFile->h, sqlite3ErrName(rc)));
5054350967
return rc;
5054450968
}
5054550969
#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
+
5054650992
}
5054750993
OSTRACE(("FCNTL file=%p, rc=SQLITE_NOTFOUND\n", pFile->h));
5054850994
return SQLITE_NOTFOUND;
5054950995
}
5055050996
@@ -50621,36 +51067,39 @@
5062151067
** nRef
5062251068
** pNext
5062351069
**
5062451070
** The following fields are read-only after the object is created:
5062551071
**
50626
-** fid
5062751072
** zFilename
5062851073
**
5062951074
** Either winShmNode.mutex must be held or winShmNode.nRef==0 and
5063051075
** winShmMutexHeld() is true when reading or writing any other field
5063151076
** in this structure.
5063251077
**
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.
5063351083
*/
5063451084
struct winShmNode {
5063551085
sqlite3_mutex *mutex; /* Mutex to access this object */
5063651086
char *zFilename; /* Name of the file */
50637
- winFile hFile; /* File handle from winOpen */
51087
+ HANDLE hSharedShm; /* File handle open on zFilename */
5063851088
51089
+ int isUnlocked; /* DMS lock has not yet been obtained */
51090
+ int isReadonly; /* True if read-only */
5063951091
int szRegion; /* Size of shared-memory regions */
5064051092
int nRegion; /* Size of array apRegion */
50641
- u8 isReadonly; /* True if read-only */
50642
- u8 isUnlocked; /* True if no DMS lock held */
5064351093
5064451094
struct ShmRegion {
5064551095
HANDLE hMap; /* File handle from CreateFileMapping */
5064651096
void *pMap;
5064751097
} *aRegion;
5064851098
DWORD lastErrno; /* The Windows errno from the last I/O error */
5064951099
5065051100
int nRef; /* Number of winShm objects pointing to this */
50651
- winShm *pFirst; /* All winShm objects pointing to this */
5065251101
winShmNode *pNext; /* Next in list of all winShmNode objects */
5065351102
#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
5065451103
u8 nextShmId; /* Next available winShm.id value */
5065551104
#endif
5065651105
};
@@ -50662,27 +51111,19 @@
5066251111
*/
5066351112
static winShmNode *winShmNodeList = 0;
5066451113
5066551114
/*
5066651115
** 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.
5067751118
*/
5067851119
struct winShm {
5067951120
winShmNode *pShmNode; /* The underlying winShmNode object */
50680
- winShm *pNext; /* Next winShm with the same winShmNode */
50681
- u8 hasMutex; /* True if holding the winShmNode mutex */
5068251121
u16 sharedMask; /* Mask of shared locks held */
5068351122
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 */
5068451125
#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
5068551126
u8 id; /* Id of this connection with its winShmNode */
5068651127
#endif
5068751128
};
5068851129
@@ -50690,54 +51131,10 @@
5069051131
** Constants used for locking
5069151132
*/
5069251133
#define WIN_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */
5069351134
#define WIN_SHM_DMS (WIN_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */
5069451135
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
-
5073951136
/* Forward references to VFS methods */
5074051137
static int winOpen(sqlite3_vfs*,const char*,sqlite3_file*,int,int*);
5074151138
static int winDelete(sqlite3_vfs *,const char*,int);
5074251139
5074351140
/*
@@ -50765,15 +51162,11 @@
5076551162
bRc = osCloseHandle(p->aRegion[i].hMap);
5076651163
OSTRACE(("SHM-PURGE-CLOSE pid=%lu, region=%d, rc=%s\n",
5076751164
osGetCurrentProcessId(), i, bRc ? "ok" : "failed"));
5076851165
UNUSED_VARIABLE_VALUE(bRc);
5076951166
}
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);
5077551168
if( deleteFlag ){
5077651169
SimulateIOErrorBenign(1);
5077751170
sqlite3BeginBenignMalloc();
5077851171
winDelete(pVfs, p->zFilename, 0);
5077951172
sqlite3EndBenignMalloc();
@@ -50787,46 +51180,166 @@
5078751180
}
5078851181
}
5078951182
}
5079051183
5079151184
/*
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.
5079951188
*/
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;
5080251192
51193
+ assert( sqlite3_mutex_held(pShmNode->mutex) );
51194
+ rc = winHandleLockTimeout(h, WIN_SHM_DMS, 1, 1, 0);
5080351195
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. */
5080451199
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.
5082851341
*/
5082951342
static int winOpenSharedMemory(winFile *pDbFd){
5083051343
struct winShm *p; /* The connection to be opened */
5083151344
winShmNode *pShmNode = 0; /* The underlying mmapped file */
5083251345
int rc = SQLITE_OK; /* Result code */
@@ -50834,102 +51347,87 @@
5083451347
int nName; /* Size of zName in bytes */
5083551348
5083651349
assert( pDbFd->pShm==0 ); /* Not previously opened */
5083751350
5083851351
/* 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. */
5084151353
p = sqlite3MallocZero( sizeof(*p) );
5084251354
if( p==0 ) return SQLITE_IOERR_NOMEM_BKPT;
5084351355
nName = sqlite3Strlen30(pDbFd->zPath);
5084451356
pNew = sqlite3MallocZero( sizeof(*pShmNode) + (i64)nName + 17 );
5084551357
if( pNew==0 ){
5084651358
sqlite3_free(p);
5084751359
return SQLITE_IOERR_NOMEM_BKPT;
5084851360
}
5084951361
pNew->zFilename = (char*)&pNew[1];
51362
+ pNew->hSharedShm = INVALID_HANDLE_VALUE;
51363
+ pNew->isUnlocked = 1;
5085051364
sqlite3_snprintf(nName+15, pNew->zFilename, "%s-shm", pDbFd->zPath);
5085151365
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);
5085251372
5085351373
/* 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. */
5085651375
winShmEnterMutex();
5085751376
for(pShmNode = winShmNodeList; pShmNode; pShmNode=pShmNode->pNext){
5085851377
/* 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. */
5086151379
if( sqlite3StrICmp(pShmNode->zFilename, pNew->zFilename)==0 ) break;
5086251380
}
50863
- if( pShmNode ){
50864
- sqlite3_free(pNew);
50865
- }else{
50866
- int inFlags = SQLITE_OPEN_WAL;
50867
- int outFlags = 0;
50868
-
51381
+ if( pShmNode==0 ){
5086951382
pShmNode = pNew;
50870
- pNew = 0;
50871
- ((winFile*)(&pShmNode->hFile))->h = INVALID_HANDLE_VALUE;
50872
- pShmNode->pNext = winShmNodeList;
50873
- winShmNodeList = pShmNode;
5087451383
51384
+ /* Allocate a mutex for this winShmNode object, if one is required. */
5087551385
if( sqlite3GlobalConfig.bCoreMutex ){
5087651386
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++;
5090451417
#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);
5093151429
return rc;
5093251430
}
5093351431
5093451432
/*
5093551433
** Close a connection to shared-memory. Delete the underlying
@@ -50940,38 +51438,33 @@
5094051438
int deleteFlag /* Delete after closing if true */
5094151439
){
5094251440
winFile *pDbFd; /* Database holding shared-memory */
5094351441
winShm *p; /* The connection to be closed */
5094451442
winShmNode *pShmNode; /* The underlying shared-memory file */
50945
- winShm **pp; /* For looping over sibling connections */
5094651443
5094751444
pDbFd = (winFile*)fd;
5094851445
p = pDbFd->pShm;
5094951446
if( p==0 ) return SQLITE_OK;
51447
+ if( p->hShm!=INVALID_HANDLE_VALUE ){
51448
+ osCloseHandle(p->hShm);
51449
+ }
51450
+
5095051451
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();
5096251453
5096351454
/* If pShmNode->nRef has reached 0, then close the underlying
50964
- ** shared-memory file, too */
50965
- winShmEnterMutex();
51455
+ ** shared-memory file, too. */
5096651456
assert( pShmNode->nRef>0 );
5096751457
pShmNode->nRef--;
5096851458
if( pShmNode->nRef==0 ){
5096951459
winShmPurge(pDbFd->pVfs, deleteFlag);
5097051460
}
5097151461
winShmLeaveMutex();
5097251462
51463
+ /* Free the connection p */
51464
+ sqlite3_free(p);
51465
+ pDbFd->pShm = 0;
5097351466
return SQLITE_OK;
5097451467
}
5097551468
5097651469
/*
5097751470
** Change the lock state for a shared-memory segment.
@@ -50982,14 +51475,13 @@
5098251475
int n, /* Number of locks to acquire or release */
5098351476
int flags /* What to do with the lock */
5098451477
){
5098551478
winFile *pDbFd = (winFile*)fd; /* Connection holding shared memory */
5098651479
winShm *p = pDbFd->pShm; /* The shared memory being locked */
50987
- winShm *pX; /* For looping over all siblings */
5098851480
winShmNode *pShmNode;
5098951481
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 */
5099151483
5099251484
if( p==0 ) return SQLITE_IOERR_SHMLOCK;
5099351485
pShmNode = p->pShmNode;
5099451486
if( NEVER(pShmNode==0) ) return SQLITE_IOERR_SHMLOCK;
5099551487
@@ -50999,89 +51491,86 @@
5099951491
|| flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE)
5100051492
|| flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED)
5100151493
|| flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) );
5100251494
assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 );
5100351495
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
+ );
5108351572
return rc;
5108451573
}
5108551574
5108651575
/*
5108751576
** Implement a memory barrier or memory fence on shared memory.
@@ -51139,17 +51628,19 @@
5113951628
}
5114051629
pShmNode = pShm->pShmNode;
5114151630
5114251631
sqlite3_mutex_enter(pShmNode->mutex);
5114351632
if( pShmNode->isUnlocked ){
51144
- rc = winLockSharedMemory(pShmNode);
51633
+ /* Take the DMS lock. */
51634
+ assert( pShmNode->nRegion==0 );
51635
+ rc = winLockSharedMemory(pShmNode, winFileBusyTimeout(pDbFd));
5114551636
if( rc!=SQLITE_OK ) goto shmpage_out;
51146
- pShmNode->isUnlocked = 0;
5114751637
}
51638
+
5114851639
assert( szRegion==pShmNode->szRegion || pShmNode->nRegion==0 );
51149
-
5115051640
if( pShmNode->nRegion<=iRegion ){
51641
+ HANDLE hShared = pShmNode->hSharedShm;
5115151642
struct ShmRegion *apNew; /* New aRegion[] array */
5115251643
int nByte = (iRegion+1)*szRegion; /* Minimum required file size */
5115351644
sqlite3_int64 sz; /* Current size of wal-index file */
5115451645
5115551646
pShmNode->szRegion = szRegion;
@@ -51156,35 +51647,32 @@
5115651647
5115751648
/* The requested region is not mapped into this processes address space.
5115851649
** Check to see if it has been allocated (i.e. if the wal-index file is
5115951650
** large enough to contain the requested region).
5116051651
*/
51161
- rc = winFileSize((sqlite3_file *)&pShmNode->hFile, &sz);
51652
+ rc = winHandleSize(hShared, &sz);
5116251653
if( rc!=SQLITE_OK ){
51163
- rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(),
51164
- "winShmMap1", pDbFd->zPath);
51654
+ rc = winLogError(rc, osGetLastError(), "winShmMap1", pDbFd->zPath);
5116551655
goto shmpage_out;
5116651656
}
5116751657
5116851658
if( sz<nByte ){
5116951659
/* The requested memory region does not exist. If isWrite is set to
5117051660
** zero, exit early. *pp will be set to NULL and SQLITE_OK returned.
5117151661
**
5117251662
** Alternatively, if isWrite is non-zero, use ftruncate() to allocate
51173
- ** the requested memory region.
51174
- */
51663
+ ** the requested memory region. */
5117551664
if( !isWrite ) goto shmpage_out;
51176
- rc = winTruncate((sqlite3_file *)&pShmNode->hFile, nByte);
51665
+ rc = winHandleTruncate(hShared, nByte);
5117751666
if( rc!=SQLITE_OK ){
51178
- rc = winLogError(SQLITE_IOERR_SHMSIZE, osGetLastError(),
51179
- "winShmMap2", pDbFd->zPath);
51667
+ rc = winLogError(rc, osGetLastError(), "winShmMap2", pDbFd->zPath);
5118051668
goto shmpage_out;
5118151669
}
5118251670
}
5118351671
5118451672
/* Map the requested memory region into this processes address space. */
51185
- apNew = (struct ShmRegion *)sqlite3_realloc64(
51673
+ apNew = (struct ShmRegion*)sqlite3_realloc64(
5118651674
pShmNode->aRegion, (iRegion+1)*sizeof(apNew[0])
5118751675
);
5118851676
if( !apNew ){
5118951677
rc = SQLITE_IOERR_NOMEM_BKPT;
5119051678
goto shmpage_out;
@@ -51199,22 +51687,17 @@
5119951687
while( pShmNode->nRegion<=iRegion ){
5120051688
HANDLE hMap = NULL; /* file-mapping handle */
5120151689
void *pMap = 0; /* Mapped memory region */
5120251690
5120351691
#if SQLITE_OS_WINRT
51204
- hMap = osCreateFileMappingFromApp(pShmNode->hFile.h,
51205
- NULL, protect, nByte, NULL
51206
- );
51692
+ hMap = osCreateFileMappingFromApp(hShared, NULL, protect, nByte, NULL);
5120751693
#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);
5121151695
#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);
5121551697
#endif
51698
+
5121651699
OSTRACE(("SHM-MAP-CREATE pid=%lu, region=%d, size=%d, rc=%s\n",
5121751700
osGetCurrentProcessId(), pShmNode->nRegion, nByte,
5121851701
hMap ? "ok" : "failed"));
5121951702
if( hMap ){
5122051703
int iOffset = pShmNode->nRegion*szRegion;
@@ -51253,11 +51736,13 @@
5125351736
char *p = (char *)pShmNode->aRegion[iRegion].pMap;
5125451737
*pp = (void *)&p[iOffsetShift];
5125551738
}else{
5125651739
*pp = 0;
5125751740
}
51258
- if( pShmNode->isReadonly && rc==SQLITE_OK ) rc = SQLITE_READONLY;
51741
+ if( pShmNode->isReadonly && rc==SQLITE_OK ){
51742
+ rc = SQLITE_READONLY;
51743
+ }
5125951744
sqlite3_mutex_leave(pShmNode->mutex);
5126051745
return rc;
5126151746
}
5126251747
5126351748
#else
@@ -51594,30 +52079,10 @@
5159452079
/* caller will handle out of memory */
5159552080
return zConverted;
5159652081
}
5159752082
#endif
5159852083
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
-
5161952084
/*
5162052085
** This function returns non-zero if the specified UTF-8 string buffer
5162152086
** ends with a directory separator character or one was successfully
5162252087
** added to it.
5162352088
*/
@@ -53067,11 +53532,11 @@
5306753532
};
5306853533
#endif
5306953534
5307053535
/* Double-check that the aSyscall[] array has been constructed
5307153536
** correctly. See ticket [bb3a86e890c8e96ab] */
53072
- assert( ArraySize(aSyscall)==80 );
53537
+ assert( ArraySize(aSyscall)==82 );
5307353538
5307453539
/* get memory map allocation granularity */
5307553540
memset(&winSysInfo, 0, sizeof(SYSTEM_INFO));
5307653541
#if SQLITE_OS_WINRT
5307753542
osGetNativeSystemInfo(&winSysInfo);
@@ -54125,11 +54590,11 @@
5412554590
** Empirical testing showed that the *37 multiplier
5412654591
** (an arbitrary prime)in the hash function provided
5412754592
** no fewer collisions than the no-op *1. */
5412854593
#define BITVEC_HASH(X) (((X)*1)%BITVEC_NINT)
5412954594
54130
-#define BITVEC_NPTR (BITVEC_USIZE/sizeof(Bitvec *))
54595
+#define BITVEC_NPTR ((u32)(BITVEC_USIZE/sizeof(Bitvec *)))
5413154596
5413254597
5413354598
/*
5413454599
** A bitmap is an instance of the following structure.
5413554600
**
@@ -54308,11 +54773,11 @@
5430854773
if (!p) {
5430954774
return;
5431054775
}
5431154776
}
5431254777
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)));
5431454779
}else{
5431554780
unsigned int j;
5431654781
u32 *aiValues = pBuf;
5431754782
memcpy(aiValues, p->u.aHash, sizeof(p->u.aHash));
5431854783
memset(p->u.aHash, 0, sizeof(p->u.aHash));
@@ -54359,11 +54824,11 @@
5435954824
** up to N bits. Let I be an integer between 0 and N. 0<=I<N.
5436054825
** Then the following macros can be used to set, clear, or test
5436154826
** individual bits within V.
5436254827
*/
5436354828
#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))
5436554830
#define TESTBIT(V,I) (V[I>>3]&(1<<(I&7)))!=0
5436654831
5436754832
/*
5436854833
** This routine runs an extensive test of the Bitvec code.
5436954834
**
@@ -55643,14 +56108,10 @@
5564356108
void *pStart, *pEnd; /* Bounds of global page cache memory */
5564456109
/* Above requires no mutex. Use mutex below for variable that follow. */
5564556110
sqlite3_mutex *mutex; /* Mutex for accessing the following: */
5564656111
PgFreeslot *pFree; /* Free page blocks */
5564756112
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. */
5565256113
int bUnderPressure; /* True if low on PAGECACHE memory */
5565356114
} pcache1_g;
5565456115
5565556116
/*
5565656117
** All code in this file should access the global structure above via the
@@ -55694,11 +56155,11 @@
5569456155
pcache1.szSlot = sz;
5569556156
pcache1.nSlot = pcache1.nFreeSlot = n;
5569656157
pcache1.nReserve = n>90 ? 10 : (n/10 + 1);
5569756158
pcache1.pStart = pBuf;
5569856159
pcache1.pFree = 0;
55699
- pcache1.bUnderPressure = 0;
56160
+ AtomicStore(&pcache1.bUnderPressure,0);
5570056161
while( n-- ){
5570156162
p = (PgFreeslot*)pBuf;
5570256163
p->pNext = pcache1.pFree;
5570356164
pcache1.pFree = p;
5570456165
pBuf = (void*)&((char*)pBuf)[sz];
@@ -55762,11 +56223,11 @@
5576256223
sqlite3_mutex_enter(pcache1.mutex);
5576356224
p = (PgHdr1 *)pcache1.pFree;
5576456225
if( p ){
5576556226
pcache1.pFree = pcache1.pFree->pNext;
5576656227
pcache1.nFreeSlot--;
55767
- pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve;
56228
+ AtomicStore(&pcache1.bUnderPressure,pcache1.nFreeSlot<pcache1.nReserve);
5576856229
assert( pcache1.nFreeSlot>=0 );
5576956230
sqlite3StatusHighwater(SQLITE_STATUS_PAGECACHE_SIZE, nByte);
5577056231
sqlite3StatusUp(SQLITE_STATUS_PAGECACHE_USED, 1);
5577156232
}
5577256233
sqlite3_mutex_leave(pcache1.mutex);
@@ -55801,11 +56262,11 @@
5580156262
sqlite3StatusDown(SQLITE_STATUS_PAGECACHE_USED, 1);
5580256263
pSlot = (PgFreeslot*)p;
5580356264
pSlot->pNext = pcache1.pFree;
5580456265
pcache1.pFree = pSlot;
5580556266
pcache1.nFreeSlot++;
55806
- pcache1.bUnderPressure = pcache1.nFreeSlot<pcache1.nReserve;
56267
+ AtomicStore(&pcache1.bUnderPressure,pcache1.nFreeSlot<pcache1.nReserve);
5580756268
assert( pcache1.nFreeSlot<=pcache1.nSlot );
5580856269
sqlite3_mutex_leave(pcache1.mutex);
5580956270
}else{
5581056271
assert( sqlite3MemdebugHasType(p, MEMTYPE_PCACHE) );
5581156272
sqlite3MemdebugSetType(p, MEMTYPE_HEAP);
@@ -55932,11 +56393,11 @@
5593256393
** allocating a new page cache entry in order to avoid stressing
5593356394
** the heap even further.
5593456395
*/
5593556396
static int pcache1UnderMemoryPressure(PCache1 *pCache){
5593656397
if( pcache1.nSlot && (pCache->szPage+pCache->szExtra)<=pcache1.szSlot ){
55937
- return pcache1.bUnderPressure;
56398
+ return AtomicLoad(&pcache1.bUnderPressure);
5593856399
}else{
5593956400
return sqlite3HeapNearlyFull();
5594056401
}
5594156402
}
5594256403
@@ -59211,10 +59672,19 @@
5921159672
pPager->pInJournal = 0;
5921259673
releaseAllSavepoints(pPager);
5921359674
5921459675
if( pagerUseWal(pPager) ){
5921559676
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
+ }
5921659686
sqlite3WalEndReadTransaction(pPager->pWal);
5921759687
pPager->eState = PAGER_OPEN;
5921859688
}else if( !pPager->exclusiveMode ){
5921959689
int rc; /* Error code returned by pagerUnlockDb() */
5922059690
int iDc = isOpen(pPager->fd)?sqlite3OsDeviceCharacteristics(pPager->fd):0;
@@ -65669,10 +66139,15 @@
6566966139
)
6567066140
6567166141
/*
6567266142
** An open write-ahead log file is represented by an instance of the
6567366143
** 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.
6567466149
*/
6567566150
struct Wal {
6567666151
sqlite3_vfs *pVfs; /* The VFS used to create pDbFd */
6567766152
sqlite3_file *pDbFd; /* File handle for the database file */
6567866153
sqlite3_file *pWalFd; /* File handle for WAL file */
@@ -65759,12 +66234,16 @@
6575966234
int iNext; /* Next slot in aIndex[] not yet returned */
6576066235
ht_slot *aIndex; /* i0, i1, i2... such that aPgno[iN] ascend */
6576166236
u32 *aPgno; /* Array of page numbers. */
6576266237
int nEntry; /* Nr. of entries in aPgno[] and aIndex[] */
6576366238
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 */
6576566240
};
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))
6576666245
6576766246
/*
6576866247
** Define the parameters of the hash tables in the wal-index file. There
6576966248
** is a hash-table following every HASHTABLE_NPAGE page numbers in the
6577066249
** wal-index.
@@ -67122,12 +67601,11 @@
6712267601
assert( pWal->ckptLock && pWal->hdr.mxFrame>0 );
6712367602
iLast = pWal->hdr.mxFrame;
6712467603
6712567604
/* Allocate space for the WalIterator object. */
6712667605
nSegment = walFramePage(iLast) + 1;
67127
- nByte = sizeof(WalIterator)
67128
- + (nSegment-1)*sizeof(struct WalSegment)
67606
+ nByte = SZ_WALITERATOR(nSegment)
6712967607
+ iLast*sizeof(ht_slot);
6713067608
p = (WalIterator *)sqlite3_malloc64(nByte
6713167609
+ sizeof(ht_slot) * (iLast>HASHTABLE_NPAGE?HASHTABLE_NPAGE:iLast)
6713267610
);
6713367611
if( !p ){
@@ -67194,11 +67672,11 @@
6719467672
** or 0 otherwise.
6719567673
*/
6719667674
static int walEnableBlocking(Wal *pWal){
6719767675
int res = 0;
6719867676
if( pWal->db ){
67199
- int tmout = pWal->db->busyTimeout;
67677
+ int tmout = pWal->db->setlkTimeout;
6720067678
if( tmout ){
6720167679
res = walEnableBlockingMs(pWal, tmout);
6720267680
}
6720367681
}
6720467682
return res;
@@ -67580,11 +68058,13 @@
6758068058
static int walHandleException(Wal *pWal){
6758168059
if( pWal->exclusiveMode==0 ){
6758268060
static const int S = 1;
6758368061
static const int E = (1<<SQLITE_SHM_NLOCK);
6758468062
int ii;
67585
- u32 mUnlock = pWal->lockMask & ~(
68063
+ u32 mUnlock;
68064
+ if( pWal->writeLock==2 ) pWal->writeLock = 0;
68065
+ mUnlock = pWal->lockMask & ~(
6758668066
(pWal->readLock<0 ? 0 : (S << WAL_READ_LOCK(pWal->readLock)))
6758768067
| (pWal->writeLock ? (E << WAL_WRITE_LOCK) : 0)
6758868068
| (pWal->ckptLock ? (E << WAL_CKPT_LOCK) : 0)
6758968069
);
6759068070
for(ii=0; ii<SQLITE_SHM_NLOCK; ii++){
@@ -67852,11 +68332,16 @@
6785268332
}else{
6785368333
int bWriteLock = pWal->writeLock;
6785468334
if( bWriteLock
6785568335
|| SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1))
6785668336
){
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;
6785868343
if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){
6785968344
badHdr = walIndexTryHdr(pWal, pChanged);
6786068345
if( badHdr ){
6786168346
/* If the wal-index header is still malformed even while holding
6786268347
** a WRITE lock, it can only mean that the header is corrupted and
@@ -68637,12 +69122,15 @@
6863769122
/*
6863869123
** Finish with a read transaction. All this does is release the
6863969124
** read-lock.
6864069125
*/
6864169126
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
6864369130
if( pWal->readLock>=0 ){
69131
+ sqlite3WalEndWriteTransaction(pWal);
6864469132
walUnlockShared(pWal, WAL_READ_LOCK(pWal->readLock));
6864569133
pWal->readLock = -1;
6864669134
}
6864769135
}
6864869136
@@ -68831,11 +69319,11 @@
6883169319
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
6883269320
/* If the write-lock is already held, then it was obtained before the
6883369321
** read-transaction was even opened, making this call a no-op.
6883469322
** Return early. */
6883569323
if( pWal->writeLock ){
68836
- assert( !memcmp(&pWal->hdr,(void *)walIndexHdr(pWal),sizeof(WalIndexHdr)) );
69324
+ assert( !memcmp(&pWal->hdr,(void*)pWal->apWiData[0],sizeof(WalIndexHdr)) );
6883769325
return SQLITE_OK;
6883869326
}
6883969327
#endif
6884069328
6884169329
/* Cannot start a write transaction without first holding a read
@@ -70280,10 +70768,16 @@
7028070768
** If a tree that appears to be taller than this is encountered, it is
7028170769
** assumed that the database is corrupt.
7028270770
*/
7028370771
#define BTCURSOR_MAX_DEPTH 20
7028470772
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
+
7028570779
/*
7028670780
** A cursor is a pointer to a particular entry within a particular
7028770781
** b-tree within a database file.
7028870782
**
7028970783
** The entry is identified by its MemPage and the index in
@@ -70688,11 +71182,11 @@
7068871182
** two or more btrees in common both try to lock all their btrees
7068971183
** at the same instant.
7069071184
*/
7069171185
static void SQLITE_NOINLINE btreeEnterAll(sqlite3 *db){
7069271186
int i;
70693
- int skipOk = 1;
71187
+ u8 skipOk = 1;
7069471188
Btree *p;
7069571189
assert( sqlite3_mutex_held(db->mutex) );
7069671190
for(i=0; i<db->nDb; i++){
7069771191
p = db->aDb[i].pBt;
7069871192
if( p && p->sharable ){
@@ -71834,11 +72328,11 @@
7183472328
/*
7183572329
** Provide flag hints to the cursor.
7183672330
*/
7183772331
SQLITE_PRIVATE void sqlite3BtreeCursorHintFlags(BtCursor *pCur, unsigned x){
7183872332
assert( x==BTREE_SEEK_EQ || x==BTREE_BULKLOAD || x==0 );
71839
- pCur->hints = x;
72333
+ pCur->hints = (u8)x;
7184072334
}
7184172335
7184272336
7184372337
#ifndef SQLITE_OMIT_AUTOVACUUM
7184472338
/*
@@ -72028,18 +72522,19 @@
7202872522
** page pPage, return the number of bytes of payload stored locally.
7202972523
*/
7203072524
static int btreePayloadToLocal(MemPage *pPage, i64 nPayload){
7203172525
int maxLocal; /* Maximum amount of payload held locally */
7203272526
maxLocal = pPage->maxLocal;
72527
+ assert( nPayload>=0 );
7203372528
if( nPayload<=maxLocal ){
72034
- return nPayload;
72529
+ return (int)nPayload;
7203572530
}else{
7203672531
int minLocal; /* Minimum amount of payload held locally */
7203772532
int surplus; /* Overflow payload available for local storage */
7203872533
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;
7204172536
}
7204272537
}
7204372538
7204472539
/*
7204572540
** The following routines are implementations of the MemPage.xParseCell()
@@ -72145,15 +72640,17 @@
7214572640
pInfo->nKey = *(i64*)&iKey;
7214672641
pInfo->nPayload = nPayload;
7214772642
pInfo->pPayload = pIter;
7214872643
testcase( nPayload==pPage->maxLocal );
7214972644
testcase( nPayload==(u32)pPage->maxLocal+1 );
72645
+ assert( nPayload>=0 );
72646
+ assert( pPage->maxLocal <= BT_MAX_LOCAL );
7215072647
if( nPayload<=pPage->maxLocal ){
7215172648
/* This is the (easy) common case where the entire payload fits
7215272649
** on the local page. No overflow is required.
7215372650
*/
72154
- pInfo->nSize = nPayload + (u16)(pIter - pCell);
72651
+ pInfo->nSize = (u16)nPayload + (u16)(pIter - pCell);
7215572652
if( pInfo->nSize<4 ) pInfo->nSize = 4;
7215672653
pInfo->nLocal = (u16)nPayload;
7215772654
}else{
7215872655
btreeParseCellAdjustSizeForOverflow(pPage, pCell, pInfo);
7215972656
}
@@ -72182,15 +72679,17 @@
7218272679
pInfo->nKey = nPayload;
7218372680
pInfo->nPayload = nPayload;
7218472681
pInfo->pPayload = pIter;
7218572682
testcase( nPayload==pPage->maxLocal );
7218672683
testcase( nPayload==(u32)pPage->maxLocal+1 );
72684
+ assert( nPayload>=0 );
72685
+ assert( pPage->maxLocal <= BT_MAX_LOCAL );
7218772686
if( nPayload<=pPage->maxLocal ){
7218872687
/* This is the (easy) common case where the entire payload fits
7218972688
** on the local page. No overflow is required.
7219072689
*/
72191
- pInfo->nSize = nPayload + (u16)(pIter - pCell);
72690
+ pInfo->nSize = (u16)nPayload + (u16)(pIter - pCell);
7219272691
if( pInfo->nSize<4 ) pInfo->nSize = 4;
7219372692
pInfo->nLocal = (u16)nPayload;
7219472693
}else{
7219572694
btreeParseCellAdjustSizeForOverflow(pPage, pCell, pInfo);
7219672695
}
@@ -72725,18 +73224,18 @@
7272573224
** that routine will not detect overlap between cells or freeblocks. Nor
7272673225
** does it detect cells or freeblocks that encroach into the reserved bytes
7272773226
** at the end of the page. So do additional corruption checks inside this
7272873227
** routine and return SQLITE_CORRUPT if any problems are found.
7272973228
*/
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 */
7273373232
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 */
7273873237
unsigned char *data = pPage->aData; /* Page content */
7273973238
u8 *pTmp; /* Temporary ptr into data[] */
7274073239
7274173240
assert( pPage->pBt!=0 );
7274273241
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
@@ -72759,11 +73258,11 @@
7275973258
if( iFreeBlk==0 ) break; /* TH3: corrupt082.100 */
7276073259
return SQLITE_CORRUPT_PAGE(pPage);
7276173260
}
7276273261
iPtr = iFreeBlk;
7276373262
}
72764
- if( iFreeBlk>pPage->pBt->usableSize-4 ){ /* TH3: corrupt081.100 */
73263
+ if( iFreeBlk>(int)pPage->pBt->usableSize-4 ){ /* TH3: corrupt081.100 */
7276573264
return SQLITE_CORRUPT_PAGE(pPage);
7276673265
}
7276773266
assert( iFreeBlk>iPtr || iFreeBlk==0 || CORRUPT_DB );
7276873267
7276973268
/* At this point:
@@ -72774,11 +73273,11 @@
7277473273
*/
7277573274
if( iFreeBlk && iEnd+3>=iFreeBlk ){
7277673275
nFrag = iFreeBlk - iEnd;
7277773276
if( iEnd>iFreeBlk ) return SQLITE_CORRUPT_PAGE(pPage);
7277873277
iEnd = iFreeBlk + get2byte(&data[iFreeBlk+2]);
72779
- if( iEnd > pPage->pBt->usableSize ){
73278
+ if( iEnd > (int)pPage->pBt->usableSize ){
7278073279
return SQLITE_CORRUPT_PAGE(pPage);
7278173280
}
7278273281
iSize = iEnd - iStart;
7278373282
iFreeBlk = get2byte(&data[iFreeBlk]);
7278473283
}
@@ -72795,11 +73294,11 @@
7279573294
iSize = iEnd - iPtr;
7279673295
iStart = iPtr;
7279773296
}
7279873297
}
7279973298
if( nFrag>data[hdr+7] ) return SQLITE_CORRUPT_PAGE(pPage);
72800
- data[hdr+7] -= nFrag;
73299
+ data[hdr+7] -= (u8)nFrag;
7280173300
}
7280273301
pTmp = &data[hdr+5];
7280373302
x = get2byte(pTmp);
7280473303
if( pPage->pBt->btsFlags & BTS_FAST_SECURE ){
7280573304
/* Overwrite deleted information with zeros when the secure_delete
@@ -72816,11 +73315,12 @@
7281673315
put2byte(&data[hdr+5], iEnd);
7281773316
}else{
7281873317
/* Insert the new freeblock into the freelist */
7281973318
put2byte(&data[iPtr], iStart);
7282073319
put2byte(&data[iStart], iFreeBlk);
72821
- put2byte(&data[iStart+2], iSize);
73320
+ assert( iSize>=0 && iSize<=0xffff );
73321
+ put2byte(&data[iStart+2], (u16)iSize);
7282273322
}
7282373323
pPage->nFree += iOrigSize;
7282473324
return SQLITE_OK;
7282573325
}
7282673326
@@ -73042,11 +73542,11 @@
7304273542
return SQLITE_CORRUPT_PAGE(pPage);
7304373543
}
7304473544
assert( pBt->pageSize>=512 && pBt->pageSize<=65536 );
7304573545
pPage->maskPage = (u16)(pBt->pageSize - 1);
7304673546
pPage->nOverflow = 0;
73047
- pPage->cellOffset = pPage->hdrOffset + 8 + pPage->childPtrSize;
73547
+ pPage->cellOffset = (u16)(pPage->hdrOffset + 8 + pPage->childPtrSize);
7304873548
pPage->aCellIdx = data + pPage->childPtrSize + 8;
7304973549
pPage->aDataEnd = pPage->aData + pBt->pageSize;
7305073550
pPage->aDataOfst = pPage->aData + pPage->childPtrSize;
7305173551
/* EVIDENCE-OF: R-37002-32774 The two-byte integer at offset 3 gives the
7305273552
** number of cells on the page. */
@@ -73076,12 +73576,12 @@
7307673576
** no entries.
7307773577
*/
7307873578
static void zeroPage(MemPage *pPage, int flags){
7307973579
unsigned char *data = pPage->aData;
7308073580
BtShared *pBt = pPage->pBt;
73081
- u8 hdr = pPage->hdrOffset;
73082
- u16 first;
73581
+ int hdr = pPage->hdrOffset;
73582
+ int first;
7308373583
7308473584
assert( sqlite3PagerPagenumber(pPage->pDbPage)==pPage->pgno || CORRUPT_DB );
7308573585
assert( sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage );
7308673586
assert( sqlite3PagerGetData(pPage->pDbPage) == data );
7308773587
assert( sqlite3PagerIswriteable(pPage->pDbPage) );
@@ -73094,11 +73594,11 @@
7309473594
memset(&data[hdr+1], 0, 4);
7309573595
data[hdr+7] = 0;
7309673596
put2byte(&data[hdr+5], pBt->usableSize);
7309773597
pPage->nFree = (u16)(pBt->usableSize - first);
7309873598
decodeFlags(pPage, flags);
73099
- pPage->cellOffset = first;
73599
+ pPage->cellOffset = (u16)first;
7310073600
pPage->aDataEnd = &data[pBt->pageSize];
7310173601
pPage->aCellIdx = &data[first];
7310273602
pPage->aDataOfst = &data[pPage->childPtrSize];
7310373603
pPage->nOverflow = 0;
7310473604
assert( pBt->pageSize>=512 && pBt->pageSize<=65536 );
@@ -73880,11 +74380,11 @@
7388074380
int rc = SQLITE_OK;
7388174381
int x;
7388274382
BtShared *pBt = p->pBt;
7388374383
assert( nReserve>=0 && nReserve<=255 );
7388474384
sqlite3BtreeEnter(p);
73885
- pBt->nReserveWanted = nReserve;
74385
+ pBt->nReserveWanted = (u8)nReserve;
7388674386
x = pBt->pageSize - pBt->usableSize;
7388774387
if( nReserve<x ) nReserve = x;
7388874388
if( pBt->btsFlags & BTS_PAGESIZE_FIXED ){
7388974389
sqlite3BtreeLeave(p);
7389074390
return SQLITE_READONLY;
@@ -73986,11 +74486,11 @@
7398674486
sqlite3BtreeEnter(p);
7398774487
assert( BTS_OVERWRITE==BTS_SECURE_DELETE*2 );
7398874488
assert( BTS_FAST_SECURE==(BTS_OVERWRITE|BTS_SECURE_DELETE) );
7398974489
if( newFlag>=0 ){
7399074490
p->pBt->btsFlags &= ~BTS_FAST_SECURE;
73991
- p->pBt->btsFlags |= BTS_SECURE_DELETE*newFlag;
74491
+ p->pBt->btsFlags |= (u16)(BTS_SECURE_DELETE*newFlag);
7399274492
}
7399374493
b = (p->pBt->btsFlags & BTS_FAST_SECURE)/BTS_SECURE_DELETE;
7399474494
sqlite3BtreeLeave(p);
7399574495
return b;
7399674496
}
@@ -78434,11 +78934,12 @@
7843478934
pSrcEnd = pCArray->apEnd[k];
7843578935
}
7843678936
}
7843778937
7843878938
/* 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;
7844078941
pPg->nOverflow = 0;
7844178942
7844278943
put2byte(&aData[hdr+1], 0);
7844378944
put2byte(&aData[hdr+3], pPg->nCell);
7844478945
put2byte(&aData[hdr+5], pData - aData);
@@ -78681,13 +79182,17 @@
7868179182
assert( nCell>=0 );
7868279183
pCellptr = &pPg->aCellIdx[nCell*2];
7868379184
if( pageInsertArray(
7868479185
pPg, pBegin, &pData, pCellptr,
7868579186
iNew+nCell, nNew-nCell, pCArray
78686
- ) ) goto editpage_fail;
79187
+ )
79188
+ ){
79189
+ goto editpage_fail;
79190
+ }
7868779191
78688
- pPg->nCell = nNew;
79192
+ assert( nNew < 10922 );
79193
+ pPg->nCell = (u16)nNew;
7868979194
pPg->nOverflow = 0;
7869079195
7869179196
put2byte(&aData[hdr+3], pPg->nCell);
7869279197
put2byte(&aData[hdr+5], pData - aData);
7869379198
@@ -78992,11 +79497,11 @@
7899279497
int leafData; /* True if pPage is a leaf of a LEAFDATA tree */
7899379498
int usableSpace; /* Bytes in pPage beyond the header */
7899479499
int pageFlags; /* Value of pPage->aData[0] */
7899579500
int iSpace1 = 0; /* First unused byte of aSpace1[] */
7899679501
int iOvflSpace = 0; /* First unused byte of aOvflSpace[] */
78997
- int szScratch; /* Size of scratch memory requested */
79502
+ u64 szScratch; /* Size of scratch memory requested */
7899879503
MemPage *apOld[NB]; /* pPage and up to two siblings */
7899979504
MemPage *apNew[NB+2]; /* pPage and up to NB siblings after balancing */
7900079505
u8 *pRight; /* Location in parent of right-sibling pointer */
7900179506
u8 *apDiv[NB-1]; /* Divider cells in pParent */
7900279507
int cntNew[NB+2]; /* Index in b.paCell[] of cell after i-th page */
@@ -80277,11 +80782,11 @@
8027780782
if( loc==0 ){
8027880783
getCellInfo(pCur);
8027980784
if( pCur->info.nKey==pX->nKey ){
8028080785
BtreePayload x2;
8028180786
x2.pData = pX->pKey;
80282
- x2.nData = pX->nKey;
80787
+ x2.nData = (int)pX->nKey; assert( pX->nKey<=0x7fffffff );
8028380788
x2.nZero = 0;
8028480789
return btreeOverwriteCell(pCur, &x2);
8028580790
}
8028680791
}
8028780792
}
@@ -80458,11 +80963,11 @@
8045880963
u32 nIn; /* Size of input buffer aIn[] */
8045980964
u32 nRem; /* Bytes of data still to copy */
8046080965
8046180966
getCellInfo(pSrc);
8046280967
if( pSrc->info.nPayload<0x80 ){
80463
- *(aOut++) = pSrc->info.nPayload;
80968
+ *(aOut++) = (u8)pSrc->info.nPayload;
8046480969
}else{
8046580970
aOut += sqlite3PutVarint(aOut, pSrc->info.nPayload);
8046680971
}
8046780972
if( pDest->pKeyInfo==0 ) aOut += putVarint(aOut, iKey);
8046880973
nIn = pSrc->info.nLocal;
@@ -80471,11 +80976,11 @@
8047180976
return SQLITE_CORRUPT_PAGE(pSrc->pPage);
8047280977
}
8047380978
nRem = pSrc->info.nPayload;
8047480979
if( nIn==nRem && nIn<pDest->pPage->maxLocal ){
8047580980
memcpy(aOut, aIn, nIn);
80476
- pBt->nPreformatSize = nIn + (aOut - pBt->pTmpSpace);
80981
+ pBt->nPreformatSize = nIn + (int)(aOut - pBt->pTmpSpace);
8047780982
return SQLITE_OK;
8047880983
}else{
8047980984
int rc = SQLITE_OK;
8048080985
Pager *pSrcPager = pSrc->pBt->pPager;
8048180986
u8 *pPgnoOut = 0;
@@ -80483,11 +80988,11 @@
8048380988
DbPage *pPageIn = 0;
8048480989
MemPage *pPageOut = 0;
8048580990
u32 nOut; /* Size of output buffer aOut[] */
8048680991
8048780992
nOut = btreePayloadToLocal(pDest->pPage, pSrc->info.nPayload);
80488
- pBt->nPreformatSize = nOut + (aOut - pBt->pTmpSpace);
80993
+ pBt->nPreformatSize = (int)nOut + (int)(aOut - pBt->pTmpSpace);
8048980994
if( nOut<pSrc->info.nPayload ){
8049080995
pPgnoOut = &aOut[nOut];
8049180996
pBt->nPreformatSize += 4;
8049280997
}
8049380998
@@ -85584,16 +86089,14 @@
8558486089
int nArg, /* Number of argument */
8558586090
const FuncDef *pFunc, /* The function to be invoked */
8558686091
int eCallCtx /* Calling context */
8558786092
){
8558886093
Vdbe *v = pParse->pVdbe;
85589
- int nByte;
8559086094
int addr;
8559186095
sqlite3_context *pCtx;
8559286096
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));
8559586098
if( pCtx==0 ){
8559686099
assert( pParse->db->mallocFailed );
8559786100
freeEphemeralFunction(pParse->db, (FuncDef*)pFunc);
8559886101
return 0;
8559986102
}
@@ -90665,25 +91168,26 @@
9066591168
9066691169
preupdate.v = v;
9066791170
preupdate.pCsr = pCsr;
9066891171
preupdate.op = op;
9066991172
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;
9067491178
preupdate.iKey1 = iKey1;
9067591179
preupdate.iKey2 = iKey2;
9067691180
preupdate.pTab = pTab;
9067791181
preupdate.iBlobWrite = iBlobWrite;
9067891182
9067991183
db->pPreUpdate = &preupdate;
9068091184
db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2);
9068191185
db->pPreUpdate = 0;
9068291186
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);
9068591189
sqlite3VdbeMemRelease(&preupdate.oldipk);
9068691190
if( preupdate.aNew ){
9068791191
int i;
9068891192
for(i=0; i<pCsr->nField; i++){
9068991193
sqlite3VdbeMemRelease(&preupdate.aNew[i]);
@@ -92918,11 +93422,11 @@
9291893422
nRec = sqlite3BtreePayloadSize(p->pCsr->uc.pCursor);
9291993423
aRec = sqlite3DbMallocRaw(db, nRec);
9292093424
if( !aRec ) goto preupdate_old_out;
9292193425
rc = sqlite3BtreePayload(p->pCsr->uc.pCursor, 0, nRec, aRec);
9292293426
if( rc==SQLITE_OK ){
92923
- p->pUnpacked = vdbeUnpackRecord(&p->keyinfo, nRec, aRec);
93427
+ p->pUnpacked = vdbeUnpackRecord(p->pKeyinfo, nRec, aRec);
9292493428
if( !p->pUnpacked ) rc = SQLITE_NOMEM;
9292593429
}
9292693430
if( rc!=SQLITE_OK ){
9292793431
sqlite3DbFree(db, aRec);
9292893432
goto preupdate_old_out;
@@ -92983,11 +93487,11 @@
9298393487
#ifdef SQLITE_ENABLE_API_ARMOR
9298493488
p = db!=0 ? db->pPreUpdate : 0;
9298593489
#else
9298693490
p = db->pPreUpdate;
9298793491
#endif
92988
- return (p ? p->keyinfo.nKeyField : 0);
93492
+ return (p ? p->pKeyinfo->nKeyField : 0);
9298993493
}
9299093494
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
9299193495
9299293496
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
9299393497
/*
@@ -93066,11 +93570,11 @@
9306693570
UnpackedRecord *pUnpack = p->pNewUnpacked;
9306793571
if( !pUnpack ){
9306893572
Mem *pData = &p->v->aMem[p->iNewReg];
9306993573
rc = ExpandBlob(pData);
9307093574
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);
9307293576
if( !pUnpack ){
9307393577
rc = SQLITE_NOMEM;
9307493578
goto preupdate_new_out;
9307593579
}
9307693580
p->pNewUnpacked = pUnpack;
@@ -93860,13 +94364,13 @@
9386094364
*/
9386194365
Mem *pMem = iCur>0 ? &p->aMem[p->nMem-iCur] : p->aMem;
9386294366
9386394367
i64 nByte;
9386494368
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();
9386894372
9386994373
assert( iCur>=0 && iCur<p->nCursor );
9387094374
if( p->apCsr[iCur] ){ /*OPTIMIZATION-IF-FALSE*/
9387194375
sqlite3VdbeFreeCursorNN(p, p->apCsr[iCur]);
9387294376
p->apCsr[iCur] = 0;
@@ -93895,12 +94399,12 @@
9389594399
memset(pCx, 0, offsetof(VdbeCursor,pAltCursor));
9389694400
pCx->eCurType = eCurType;
9389794401
pCx->nField = nField;
9389894402
pCx->aOffset = &pCx->aType[nField];
9389994403
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)];
9390294406
sqlite3BtreeCursorZero(pCx->uc.pCursor);
9390394407
}
9390494408
return pCx;
9390594409
}
9390694410
@@ -99638,11 +100142,11 @@
99638100142
pCrsr = pC->uc.pCursor;
99639100143
99640100144
/* The OP_RowData opcodes always follow OP_NotExists or
99641100145
** OP_SeekRowid or OP_Rewind/Op_Next with no intervening instructions
99642100146
** 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
99644100148
** would fail. Should this ever change (because of changes in the code
99645100149
** generator) then the fix would be to insert a call to
99646100150
** sqlite3VdbeCursorMoveto().
99647100151
*/
99648100152
assert( pC->deferredMoveto==0 );
@@ -101287,11 +101791,11 @@
101287101791
** cell in which to store the accumulation. Be careful that the memory
101288101792
** cell is 8-byte aligned, even on platforms where a pointer is 32-bits.
101289101793
**
101290101794
** Note: We could avoid this by using a regular memory cell from aMem[] for
101291101795
** the accumulator, instead of allocating one here. */
101292
- nAlloc = ROUND8P( sizeof(pCtx[0]) + (n-1)*sizeof(sqlite3_value*) );
101796
+ nAlloc = ROUND8P( SZ_CONTEXT(n) );
101293101797
pCtx = sqlite3DbMallocRawNN(db, nAlloc + sizeof(Mem));
101294101798
if( pCtx==0 ) goto no_mem;
101295101799
pCtx->pOut = (Mem*)((u8*)pCtx + nAlloc);
101296101800
assert( EIGHT_BYTE_ALIGNMENT(pCtx->pOut) );
101297101801
@@ -102945,10 +103449,11 @@
102945103449
int iCol; /* Index of zColumn in row-record */
102946103450
int rc = SQLITE_OK;
102947103451
char *zErr = 0;
102948103452
Table *pTab;
102949103453
Incrblob *pBlob = 0;
103454
+ int iDb;
102950103455
Parse sParse;
102951103456
102952103457
#ifdef SQLITE_ENABLE_API_ARMOR
102953103458
if( ppBlob==0 ){
102954103459
return SQLITE_MISUSE_BKPT;
@@ -102990,11 +103495,14 @@
102990103495
if( pTab && IsView(pTab) ){
102991103496
pTab = 0;
102992103497
sqlite3ErrorMsg(&sParse, "cannot open view: %s", zTable);
102993103498
}
102994103499
#endif
102995
- if( !pTab ){
103500
+ if( pTab==0
103501
+ || ((iDb = sqlite3SchemaToIndex(db, pTab->pSchema))==1 &&
103502
+ sqlite3OpenTempDatabase(&sParse))
103503
+ ){
102996103504
if( sParse.zErrMsg ){
102997103505
sqlite3DbFree(db, zErr);
102998103506
zErr = sParse.zErrMsg;
102999103507
sParse.zErrMsg = 0;
103000103508
}
@@ -103001,11 +103509,11 @@
103001103509
rc = SQLITE_ERROR;
103002103510
sqlite3BtreeLeaveAll(db);
103003103511
goto blob_open_out;
103004103512
}
103005103513
pBlob->pTab = pTab;
103006
- pBlob->zDb = db->aDb[sqlite3SchemaToIndex(db, pTab->pSchema)].zDbSName;
103514
+ pBlob->zDb = db->aDb[iDb].zDbSName;
103007103515
103008103516
/* Now search pTab for the exact column. */
103009103517
iCol = sqlite3ColumnIndex(pTab, zColumn);
103010103518
if( iCol<0 ){
103011103519
sqlite3DbFree(db, zErr);
@@ -103085,11 +103593,10 @@
103085103593
{OP_Column, 0, 0, 1}, /* 3 */
103086103594
{OP_ResultRow, 1, 0, 0}, /* 4 */
103087103595
{OP_Halt, 0, 0, 0}, /* 5 */
103088103596
};
103089103597
Vdbe *v = (Vdbe *)pBlob->pStmt;
103090
- int iDb = sqlite3SchemaToIndex(db, pTab->pSchema);
103091103598
VdbeOp *aOp;
103092103599
103093103600
sqlite3VdbeAddOp4Int(v, OP_Transaction, iDb, wrFlag,
103094103601
pTab->pSchema->schema_cookie,
103095103602
pTab->pSchema->iGeneration);
@@ -103663,12 +104170,15 @@
103663104170
u8 bUsePMA; /* True if one or more PMAs created */
103664104171
u8 bUseThreads; /* True to use background threads */
103665104172
u8 iPrev; /* Previous thread used to flush PMA */
103666104173
u8 nTask; /* Size of aTask[] array */
103667104174
u8 typeMask;
103668
- SortSubtask aTask[1]; /* One or more subtasks */
104175
+ SortSubtask aTask[FLEXARRAY]; /* One or more subtasks */
103669104176
};
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))
103670104180
103671104181
#define SORTER_TYPE_INTEGER 0x01
103672104182
#define SORTER_TYPE_TEXT 0x02
103673104183
103674104184
/*
@@ -104297,12 +104807,12 @@
104297104807
assert( pCsr->pKeyInfo );
104298104808
assert( !pCsr->isEphemeral );
104299104809
assert( pCsr->eCurType==CURTYPE_SORTER );
104300104810
assert( sizeof(KeyInfo) + UMXV(pCsr->pKeyInfo->nKeyField)*sizeof(CollSeq*)
104301104811
< 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);
104304104814
104305104815
pSorter = (VdbeSorter*)sqlite3DbMallocZero(db, sz + szKeyInfo);
104306104816
pCsr->uc.pSorter = pSorter;
104307104817
if( pSorter==0 ){
104308104818
rc = SQLITE_NOMEM_BKPT;
@@ -104762,10 +105272,14 @@
104762105272
}
104763105273
104764105274
p->u.pNext = 0;
104765105275
for(i=0; aSlot[i]; i++){
104766105276
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) );
104767105281
aSlot[i] = 0;
104768105282
}
104769105283
aSlot[i] = p;
104770105284
p = pNext;
104771105285
}
@@ -109536,32 +110050,34 @@
109536110050
Table *pTab, /* The table being referenced, or NULL */
109537110051
int type, /* NC_IsCheck, NC_PartIdx, NC_IdxExpr, NC_GenCol, or 0 */
109538110052
Expr *pExpr, /* Expression to resolve. May be NULL. */
109539110053
ExprList *pList /* Expression list to resolve. May be NULL. */
109540110054
){
109541
- SrcList sSrc; /* Fake SrcList for pParse->pNewTable */
110055
+ SrcList *pSrc; /* Fake SrcList for pParse->pNewTable */
109542110056
NameContext sNC; /* Name context for pParse->pNewTable */
109543110057
int rc;
110058
+ u8 srcSpace[SZ_SRCLIST_1]; /* Memory space for the fake SrcList */
109544110059
109545110060
assert( type==0 || pTab!=0 );
109546110061
assert( type==NC_IsCheck || type==NC_PartIdx || type==NC_IdxExpr
109547110062
|| type==NC_GenCol || pTab==0 );
109548110063
memset(&sNC, 0, sizeof(sNC));
109549
- memset(&sSrc, 0, sizeof(sSrc));
110064
+ pSrc = (SrcList*)srcSpace;
110065
+ memset(pSrc, 0, SZ_SRCLIST_1);
109550110066
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;
109555110071
if( pTab->pSchema!=pParse->db->aDb[1].pSchema ){
109556110072
/* Cause EP_FromDDL to be set on TK_FUNCTION nodes of non-TEMP
109557110073
** schema elements */
109558110074
type |= NC_FromDDL;
109559110075
}
109560110076
}
109561110077
sNC.pParse = pParse;
109562
- sNC.pSrcList = &sSrc;
110078
+ sNC.pSrcList = pSrc;
109563110079
sNC.ncFlags = type | NC_IsDDL;
109564110080
if( (rc = sqlite3ResolveExprNames(&sNC, pExpr))!=SQLITE_OK ) return rc;
109565110081
if( pList ) rc = sqlite3ResolveExprListNames(&sNC, pList);
109566110082
return rc;
109567110083
}
@@ -111306,11 +111822,11 @@
111306111822
*/
111307111823
#ifndef SQLITE_OMIT_CTE
111308111824
SQLITE_PRIVATE With *sqlite3WithDup(sqlite3 *db, With *p){
111309111825
With *pRet = 0;
111310111826
if( p ){
111311
- sqlite3_int64 nByte = sizeof(*p) + sizeof(p->a[0]) * (p->nCte-1);
111827
+ sqlite3_int64 nByte = SZ_WITH(p->nCte);
111312111828
pRet = sqlite3DbMallocZero(db, nByte);
111313111829
if( pRet ){
111314111830
int i;
111315111831
pRet->nCte = p->nCte;
111316111832
for(i=0; i<p->nCte; i++){
@@ -111433,15 +111949,13 @@
111433111949
#if !defined(SQLITE_OMIT_VIEW) || !defined(SQLITE_OMIT_TRIGGER) \
111434111950
|| !defined(SQLITE_OMIT_SUBQUERY)
111435111951
SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3 *db, const SrcList *p, int flags){
111436111952
SrcList *pNew;
111437111953
int i;
111438
- int nByte;
111439111954
assert( db!=0 );
111440111955
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) );
111443111957
if( pNew==0 ) return 0;
111444111958
pNew->nSrc = pNew->nAlloc = p->nSrc;
111445111959
for(i=0; i<p->nSrc; i++){
111446111960
SrcItem *pNewItem = &pNew->a[i];
111447111961
const SrcItem *pOldItem = &p->a[i];
@@ -111499,11 +112013,11 @@
111499112013
SQLITE_PRIVATE IdList *sqlite3IdListDup(sqlite3 *db, const IdList *p){
111500112014
IdList *pNew;
111501112015
int i;
111502112016
assert( db!=0 );
111503112017
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));
111505112019
if( pNew==0 ) return 0;
111506112020
pNew->nId = p->nId;
111507112021
for(i=0; i<p->nId; i++){
111508112022
struct IdList_item *pNewItem = &pNew->a[i];
111509112023
const struct IdList_item *pOldItem = &p->a[i];
@@ -111531,11 +112045,11 @@
111531112045
pNew->pNext = pNext;
111532112046
pNew->pPrior = 0;
111533112047
pNew->pLimit = sqlite3ExprDup(db, p->pLimit, flags);
111534112048
pNew->iLimit = 0;
111535112049
pNew->iOffset = 0;
111536
- pNew->selFlags = p->selFlags & ~SF_UsesEphemeral;
112050
+ pNew->selFlags = p->selFlags & ~(u32)SF_UsesEphemeral;
111537112051
pNew->addrOpenEphm[0] = -1;
111538112052
pNew->addrOpenEphm[1] = -1;
111539112053
pNew->nSelectRow = p->nSelectRow;
111540112054
pNew->pWith = sqlite3WithDup(db, p->pWith);
111541112055
#ifndef SQLITE_OMIT_WINDOWFUNC
@@ -111583,11 +112097,11 @@
111583112097
Expr *pExpr /* Expression to be appended. Might be NULL */
111584112098
){
111585112099
struct ExprList_item *pItem;
111586112100
ExprList *pList;
111587112101
111588
- pList = sqlite3DbMallocRawNN(db, sizeof(ExprList)+sizeof(pList->a[0])*4 );
112102
+ pList = sqlite3DbMallocRawNN(db, SZ_EXPRLIST(4));
111589112103
if( pList==0 ){
111590112104
sqlite3ExprDelete(db, pExpr);
111591112105
return 0;
111592112106
}
111593112107
pList->nAlloc = 4;
@@ -111603,12 +112117,11 @@
111603112117
Expr *pExpr /* Expression to be appended. Might be NULL */
111604112118
){
111605112119
struct ExprList_item *pItem;
111606112120
ExprList *pNew;
111607112121
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));
111610112123
if( pNew==0 ){
111611112124
sqlite3ExprListDelete(db, pList);
111612112125
sqlite3ExprDelete(db, pExpr);
111613112126
return 0;
111614112127
}else{
@@ -114240,11 +114753,11 @@
114240114753
return -1; /* Not found */
114241114754
}
114242114755
114243114756
114244114757
/*
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
114246114759
** function checks the Parse.pIdxPartExpr list to see if this column
114247114760
** can be replaced with a constant value. If so, it generates code to
114248114761
** put the constant value in a register (ideally, but not necessarily,
114249114762
** register iTarget) and returns the register number.
114250114763
**
@@ -117481,17 +117994,17 @@
117481117994
pNew->nTabRef = 1;
117482117995
pNew->nCol = pTab->nCol;
117483117996
assert( pNew->nCol>0 );
117484117997
nAlloc = (((pNew->nCol-1)/8)*8)+8;
117485117998
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);
117487118000
pNew->zName = sqlite3MPrintf(db, "sqlite_altertab_%s", pTab->zName);
117488118001
if( !pNew->aCol || !pNew->zName ){
117489118002
assert( db->mallocFailed );
117490118003
goto exit_begin_add_column;
117491118004
}
117492
- memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*pNew->nCol);
118005
+ memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*(size_t)pNew->nCol);
117493118006
for(i=0; i<pNew->nCol; i++){
117494118007
Column *pCol = &pNew->aCol[i];
117495118008
pCol->zCnName = sqlite3DbStrDup(db, pCol->zCnName);
117496118009
pCol->hName = sqlite3StrIHash(pCol->zCnName);
117497118010
}
@@ -118086,23 +118599,34 @@
118086118599
sqlite3 *db, /* Database handle */
118087118600
const char *zSql, /* SQL to parse */
118088118601
int bTemp /* True if SQL is from temp schema */
118089118602
){
118090118603
int rc;
118604
+ u64 flags;
118091118605
118092118606
sqlite3ParseObjectInit(p, db);
118093118607
if( zSql==0 ){
118094118608
return SQLITE_NOMEM;
118095118609
}
118096118610
if( sqlite3StrNICmp(zSql,"CREATE ",7)!=0 ){
118097118611
return SQLITE_CORRUPT_BKPT;
118098118612
}
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
+ }
118100118620
p->eParseMode = PARSE_MODE_RENAME;
118101118621
p->db = db;
118102118622
p->nQueryLoop = 1;
118623
+ flags = db->flags;
118624
+ testcase( (db->flags & SQLITE_Comments)==0 && strstr(zSql," /* ")!=0 );
118625
+ db->flags |= SQLITE_Comments;
118103118626
rc = sqlite3RunParser(p, zSql);
118627
+ db->flags = flags;
118104118628
if( db->mallocFailed ) rc = SQLITE_NOMEM;
118105118629
if( rc==SQLITE_OK
118106118630
&& NEVER(p->pNewTable==0 && p->pNewIndex==0 && p->pNewTrigger==0)
118107118631
){
118108118632
rc = SQLITE_CORRUPT_BKPT;
@@ -118161,14 +118685,15 @@
118161118685
return SQLITE_NOMEM;
118162118686
}else{
118163118687
nQuot = sqlite3Strlen30(zQuot)-1;
118164118688
}
118165118689
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));
118168118692
}else{
118169
- zOut = (char*)sqlite3DbMallocZero(db, (nSql*2+1) * 3);
118693
+ assert( nSql>0 );
118694
+ zOut = (char*)sqlite3DbMallocZero(db, (u64)(nSql*2+1) * 3);
118170118695
if( zOut ){
118171118696
zBuf1 = &zOut[nSql*2+1];
118172118697
zBuf2 = &zOut[nSql*4+2];
118173118698
}
118174118699
}
@@ -118176,20 +118701,21 @@
118176118701
/* At this point pRename->pList contains a list of RenameToken objects
118177118702
** corresponding to all tokens in the input SQL that must be replaced
118178118703
** with the new column name, or with single-quoted versions of themselves.
118179118704
** All that remains is to construct and return the edited SQL string. */
118180118705
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);
118183118709
while( pRename->pList ){
118184118710
int iOff; /* Offset of token to replace in zOut */
118185
- u32 nReplace;
118711
+ i64 nReplace;
118186118712
const char *zReplace;
118187118713
RenameToken *pBest = renameColumnTokenNext(pRename);
118188118714
118189118715
if( zNew ){
118190
- if( bQuote==0 && sqlite3IsIdChar(*pBest->t.z) ){
118716
+ if( bQuote==0 && sqlite3IsIdChar(*(u8*)pBest->t.z) ){
118191118717
nReplace = nNew;
118192118718
zReplace = zNew;
118193118719
}else{
118194118720
nReplace = nQuot;
118195118721
zReplace = zQuot;
@@ -118203,18 +118729,19 @@
118203118729
** token. This is so that (SELECT "string"'alias') maps to
118204118730
** (SELECT 'string' 'alias'), and not (SELECT 'string''alias'). */
118205118731
memcpy(zBuf1, pBest->t.z, pBest->t.n);
118206118732
zBuf1[pBest->t.n] = 0;
118207118733
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,
118209118736
pBest->t.z[pBest->t.n]=='\'' ? " " : ""
118210118737
);
118211118738
zReplace = zBuf2;
118212118739
nReplace = sqlite3Strlen30(zReplace);
118213118740
}
118214118741
118215
- iOff = pBest->t.z - zSql;
118742
+ iOff = (int)(pBest->t.z - zSql);
118216118743
if( pBest->t.n!=nReplace ){
118217118744
memmove(&zOut[iOff + nReplace], &zOut[iOff + pBest->t.n],
118218118745
nOut - (iOff + pBest->t.n)
118219118746
);
118220118747
nOut += nReplace - pBest->t.n;
@@ -118236,15 +118763,16 @@
118236118763
118237118764
/*
118238118765
** Set all pEList->a[].fg.eEName fields in the expression-list to val.
118239118766
*/
118240118767
static void renameSetENames(ExprList *pEList, int val){
118768
+ assert( val==ENAME_NAME || val==ENAME_TAB || val==ENAME_SPAN );
118241118769
if( pEList ){
118242118770
int i;
118243118771
for(i=0; i<pEList->nExpr; i++){
118244118772
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;
118246118774
}
118247118775
}
118248118776
}
118249118777
118250118778
/*
@@ -118497,11 +119025,11 @@
118497119025
sCtx.pTab = pTab;
118498119026
if( rc!=SQLITE_OK ) goto renameColumnFunc_done;
118499119027
if( sParse.pNewTable ){
118500119028
if( IsView(sParse.pNewTable) ){
118501119029
Select *pSelect = sParse.pNewTable->u.view.pSelect;
118502
- pSelect->selFlags &= ~SF_View;
119030
+ pSelect->selFlags &= ~(u32)SF_View;
118503119031
sParse.rc = SQLITE_OK;
118504119032
sqlite3SelectPrep(&sParse, pSelect, 0);
118505119033
rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc);
118506119034
if( rc==SQLITE_OK ){
118507119035
sqlite3WalkSelect(&sWalker, pSelect);
@@ -118715,11 +119243,11 @@
118715119243
NameContext sNC;
118716119244
memset(&sNC, 0, sizeof(sNC));
118717119245
sNC.pParse = &sParse;
118718119246
118719119247
assert( pSelect->selFlags & SF_View );
118720
- pSelect->selFlags &= ~SF_View;
119248
+ pSelect->selFlags &= ~(u32)SF_View;
118721119249
sqlite3SelectPrep(&sParse, pTab->u.view.pSelect, &sNC);
118722119250
if( sParse.nErr ){
118723119251
rc = sParse.rc;
118724119252
}else{
118725119253
sqlite3WalkSelect(&sWalker, pTab->u.view.pSelect);
@@ -118888,11 +119416,11 @@
118888119416
sWalker.u.pRename = &sCtx;
118889119417
118890119418
if( sParse.pNewTable ){
118891119419
if( IsView(sParse.pNewTable) ){
118892119420
Select *pSelect = sParse.pNewTable->u.view.pSelect;
118893
- pSelect->selFlags &= ~SF_View;
119421
+ pSelect->selFlags &= ~(u32)SF_View;
118894119422
sParse.rc = SQLITE_OK;
118895119423
sqlite3SelectPrep(&sParse, pSelect, 0);
118896119424
rc = (db->mallocFailed ? SQLITE_NOMEM : sParse.rc);
118897119425
if( rc==SQLITE_OK ){
118898119426
sqlite3WalkSelect(&sWalker, pSelect);
@@ -118987,14 +119515,14 @@
118987119515
UNUSED_PARAMETER(NotUsed);
118988119516
118989119517
if( zDb && zInput ){
118990119518
int rc;
118991119519
Parse sParse;
118992
- int flags = db->flags;
119520
+ u64 flags = db->flags;
118993119521
if( bNoDQS ) db->flags &= ~(SQLITE_DqsDML|SQLITE_DqsDDL);
118994119522
rc = renameParseSql(&sParse, zDb, db, zInput, bTemp);
118995
- db->flags |= (flags & (SQLITE_DqsDML|SQLITE_DqsDDL));
119523
+ db->flags = flags;
118996119524
if( rc==SQLITE_OK ){
118997119525
if( isLegacy==0 && sParse.pNewTable && IsView(sParse.pNewTable) ){
118998119526
NameContext sNC;
118999119527
memset(&sNC, 0, sizeof(sNC));
119000119528
sNC.pParse = &sParse;
@@ -119710,11 +120238,11 @@
119710120238
}
119711120239
119712120240
p->db = db;
119713120241
p->nEst = sqlite3_value_int64(argv[2]);
119714120242
p->nRow = 0;
119715
- p->nLimit = sqlite3_value_int64(argv[3]);
120243
+ p->nLimit = sqlite3_value_int(argv[3]);
119716120244
p->nCol = nCol;
119717120245
p->nKeyCol = nKeyCol;
119718120246
p->nSkipAhead = 0;
119719120247
p->current.anDLt = (tRowcnt*)&p[1];
119720120248
@@ -121519,10 +122047,17 @@
121519122047
*/
121520122048
if( rc==SQLITE_OK ){
121521122049
sqlite3BtreeEnterAll(db);
121522122050
db->init.iDb = 0;
121523122051
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
121524122059
if( !REOPEN_AS_MEMDB(db) ){
121525122060
rc = sqlite3Init(db, &zErrDyn);
121526122061
}
121527122062
sqlite3BtreeLeaveAll(db);
121528122063
assert( zErrDyn==0 || rc!=SQLITE_OK );
@@ -123240,14 +123775,20 @@
123240123775
** Convert an table column number into a index column number. That is,
123241123776
** for the column iCol in the table (as defined by the CREATE TABLE statement)
123242123777
** find the (first) offset of that column in index pIdx. Or return -1
123243123778
** if column iCol is not used in index pIdx.
123244123779
*/
123245
-SQLITE_PRIVATE i16 sqlite3TableColumnToIndex(Index *pIdx, i16 iCol){
123780
+SQLITE_PRIVATE int sqlite3TableColumnToIndex(Index *pIdx, int iCol){
123246123781
int i;
123782
+ i16 iCol16;
123783
+ assert( iCol>=(-1) && iCol<=SQLITE_MAX_COLUMN );
123784
+ assert( pIdx->nColumn<=SQLITE_MAX_COLUMN );
123785
+ iCol16 = iCol;
123247123786
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
+ }
123249123790
}
123250123791
return -1;
123251123792
}
123252123793
123253123794
#ifndef SQLITE_OMIT_GENERATED_COLUMNS
@@ -124340,16 +124881,21 @@
124340124881
124341124882
/*
124342124883
** Resize an Index object to hold N columns total. Return SQLITE_OK
124343124884
** on success and SQLITE_NOMEM on an OOM error.
124344124885
*/
124345
-static int resizeIndexObject(sqlite3 *db, Index *pIdx, int N){
124886
+static int resizeIndexObject(Parse *pParse, Index *pIdx, int N){
124346124887
char *zExtra;
124347
- int nByte;
124888
+ u64 nByte;
124889
+ sqlite3 *db;
124348124890
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] );
124349124895
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;
124351124897
zExtra = sqlite3DbMallocZero(db, nByte);
124352124898
if( zExtra==0 ) return SQLITE_NOMEM_BKPT;
124353124899
memcpy(zExtra, pIdx->azColl, sizeof(char*)*pIdx->nColumn);
124354124900
pIdx->azColl = (const char**)zExtra;
124355124901
zExtra += sizeof(char*)*N;
@@ -124359,11 +124905,11 @@
124359124905
memcpy(zExtra, pIdx->aiColumn, sizeof(i16)*pIdx->nColumn);
124360124906
pIdx->aiColumn = (i16*)zExtra;
124361124907
zExtra += sizeof(i16)*N;
124362124908
memcpy(zExtra, pIdx->aSortOrder, pIdx->nColumn);
124363124909
pIdx->aSortOrder = (u8*)zExtra;
124364
- pIdx->nColumn = N;
124910
+ pIdx->nColumn = (u16)N; /* See tag-20250221-1 above for proof of safety */
124365124911
pIdx->isResized = 1;
124366124912
return SQLITE_OK;
124367124913
}
124368124914
124369124915
/*
@@ -124613,11 +125159,11 @@
124613125159
if( n==0 ){
124614125160
/* This index is a superset of the primary key */
124615125161
pIdx->nColumn = pIdx->nKeyCol;
124616125162
continue;
124617125163
}
124618
- if( resizeIndexObject(db, pIdx, pIdx->nKeyCol+n) ) return;
125164
+ if( resizeIndexObject(pParse, pIdx, pIdx->nKeyCol+n) ) return;
124619125165
for(i=0, j=pIdx->nKeyCol; i<nPk; i++){
124620125166
if( !isDupColumn(pIdx, pIdx->nKeyCol, pPk, i) ){
124621125167
testcase( hasColumn(pIdx->aiColumn, pIdx->nKeyCol, pPk->aiColumn[i]) );
124622125168
pIdx->aiColumn[j] = pPk->aiColumn[i];
124623125169
pIdx->azColl[j] = pPk->azColl[i];
@@ -124637,11 +125183,11 @@
124637125183
nExtra = 0;
124638125184
for(i=0; i<pTab->nCol; i++){
124639125185
if( !hasColumn(pPk->aiColumn, nPk, i)
124640125186
&& (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0 ) nExtra++;
124641125187
}
124642
- if( resizeIndexObject(db, pPk, nPk+nExtra) ) return;
125188
+ if( resizeIndexObject(pParse, pPk, nPk+nExtra) ) return;
124643125189
for(i=0, j=nPk; i<pTab->nCol; i++){
124644125190
if( !hasColumn(pPk->aiColumn, j, i)
124645125191
&& (pTab->aCol[i].colFlags & COLFLAG_VIRTUAL)==0
124646125192
){
124647125193
assert( j<pPk->nColumn );
@@ -125795,11 +126341,11 @@
125795126341
"columns in the referenced table");
125796126342
goto fk_end;
125797126343
}else{
125798126344
nCol = pFromCol->nExpr;
125799126345
}
125800
- nByte = sizeof(*pFKey) + (nCol-1)*sizeof(pFKey->aCol[0]) + pTo->n + 1;
126346
+ nByte = SZ_FKEY(nCol) + pTo->n + 1;
125801126347
if( pToCol ){
125802126348
for(i=0; i<pToCol->nExpr; i++){
125803126349
nByte += sqlite3Strlen30(pToCol->a[i].zEName) + 1;
125804126350
}
125805126351
}
@@ -126021,17 +126567,18 @@
126021126567
** of 8-byte aligned space after the Index object and return a
126022126568
** pointer to this extra space in *ppExtra.
126023126569
*/
126024126570
SQLITE_PRIVATE Index *sqlite3AllocateIndexObject(
126025126571
sqlite3 *db, /* Database connection */
126026
- i16 nCol, /* Total number of columns in the index */
126572
+ int nCol, /* Total number of columns in the index */
126027126573
int nExtra, /* Number of bytes of extra space to alloc */
126028126574
char **ppExtra /* Pointer to the "extra" space */
126029126575
){
126030126576
Index *p; /* Allocated index object */
126031126577
i64 nByte; /* Bytes of space for Index object + arrays */
126032126578
126579
+ assert( nCol <= 2*db->aLimit[SQLITE_LIMIT_COLUMN] );
126033126580
nByte = ROUND8(sizeof(Index)) + /* Index structure */
126034126581
ROUND8(sizeof(char*)*nCol) + /* Index.azColl */
126035126582
ROUND8(sizeof(LogEst)*(nCol+1) + /* Index.aiRowLogEst */
126036126583
sizeof(i16)*nCol + /* Index.aiColumn */
126037126584
sizeof(u8)*nCol); /* Index.aSortOrder */
@@ -126040,12 +126587,13 @@
126040126587
char *pExtra = ((char*)p)+ROUND8(sizeof(Index));
126041126588
p->azColl = (const char**)pExtra; pExtra += ROUND8(sizeof(char*)*nCol);
126042126589
p->aiRowLogEst = (LogEst*)pExtra; pExtra += sizeof(LogEst)*(nCol+1);
126043126590
p->aiColumn = (i16*)pExtra; pExtra += sizeof(i16)*nCol;
126044126591
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);
126047126595
*ppExtra = ((char*)p) + nByte;
126048126596
}
126049126597
return p;
126050126598
}
126051126599
@@ -126852,16 +127400,15 @@
126852127400
*/
126853127401
SQLITE_PRIVATE IdList *sqlite3IdListAppend(Parse *pParse, IdList *pList, Token *pToken){
126854127402
sqlite3 *db = pParse->db;
126855127403
int i;
126856127404
if( pList==0 ){
126857
- pList = sqlite3DbMallocZero(db, sizeof(IdList) );
127405
+ pList = sqlite3DbMallocZero(db, SZ_IDLIST(1));
126858127406
if( pList==0 ) return 0;
126859127407
}else{
126860127408
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));
126863127410
if( pNew==0 ){
126864127411
sqlite3IdListDelete(db, pList);
126865127412
return 0;
126866127413
}
126867127414
pList = pNew;
@@ -126956,12 +127503,11 @@
126956127503
sqlite3ErrorMsg(pParse, "too many FROM clause terms, max: %d",
126957127504
SQLITE_MAX_SRCLIST);
126958127505
return 0;
126959127506
}
126960127507
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));
126963127509
if( pNew==0 ){
126964127510
assert( db->mallocFailed );
126965127511
return 0;
126966127512
}
126967127513
pSrc = pNew;
@@ -127032,11 +127578,11 @@
127032127578
assert( pDatabase==0 || pTable!=0 ); /* Cannot have C without B */
127033127579
assert( pParse!=0 );
127034127580
assert( pParse->db!=0 );
127035127581
db = pParse->db;
127036127582
if( pList==0 ){
127037
- pList = sqlite3DbMallocRawNN(pParse->db, sizeof(SrcList) );
127583
+ pList = sqlite3DbMallocRawNN(pParse->db, SZ_SRCLIST(1));
127038127584
if( pList==0 ) return 0;
127039127585
pList->nAlloc = 1;
127040127586
pList->nSrc = 1;
127041127587
memset(&pList->a[0], 0, sizeof(pList->a[0]));
127042127588
pList->a[0].iCursor = -1;
@@ -127918,14 +128464,13 @@
127918128464
}
127919128465
}
127920128466
}
127921128467
127922128468
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));
127925128470
}else{
127926
- pNew = sqlite3DbMallocZero(db, sizeof(*pWith));
128471
+ pNew = sqlite3DbMallocZero(db, SZ_WITH(1));
127927128472
}
127928128473
assert( (pNew!=0 && zName!=0) || db->mallocFailed );
127929128474
127930128475
if( db->mallocFailed ){
127931128476
sqlite3CteDelete(db, pCte);
@@ -130629,11 +131174,11 @@
130629131174
130630131175
/*
130631131176
** Append to pStr text that is the SQL literal representation of the
130632131177
** value contained in pValue.
130633131178
*/
130634
-SQLITE_PRIVATE void sqlite3QuoteValue(StrAccum *pStr, sqlite3_value *pValue){
131179
+SQLITE_PRIVATE void sqlite3QuoteValue(StrAccum *pStr, sqlite3_value *pValue, int bEscape){
130635131180
/* As currently implemented, the string must be initially empty.
130636131181
** we might relax this requirement in the future, but that will
130637131182
** require enhancements to the implementation. */
130638131183
assert( pStr!=0 && pStr->nChar==0 );
130639131184
@@ -130677,20 +131222,119 @@
130677131222
}
130678131223
break;
130679131224
}
130680131225
case SQLITE_TEXT: {
130681131226
const unsigned char *zArg = sqlite3_value_text(pValue);
130682
- sqlite3_str_appendf(pStr, "%Q", zArg);
131227
+ sqlite3_str_appendf(pStr, bEscape ? "%#Q" : "%Q", zArg);
130683131228
break;
130684131229
}
130685131230
default: {
130686131231
assert( sqlite3_value_type(pValue)==SQLITE_NULL );
130687131232
sqlite3_str_append(pStr, "NULL", 4);
130688131233
break;
130689131234
}
130690131235
}
130691131236
}
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
+
130692131336
130693131337
/*
130694131338
** Implementation of the QUOTE() function.
130695131339
**
130696131340
** The quote(X) function returns the text of an SQL literal which is the
@@ -130697,18 +131341,22 @@
130697131341
** value of its argument suitable for inclusion into an SQL statement.
130698131342
** Strings are surrounded by single-quotes with escapes on interior quotes
130699131343
** as needed. BLOBs are encoded as hexadecimal literals. Strings with
130700131344
** embedded NUL characters cannot be represented as string literals in SQL
130701131345
** 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.
130702131350
*/
130703131351
static void quoteFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
130704131352
sqlite3_str str;
130705131353
sqlite3 *db = sqlite3_context_db_handle(context);
130706131354
assert( argc==1 );
130707131355
UNUSED_PARAMETER(argc);
130708131356
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)));
130710131358
sqlite3_result_text(context, sqlite3StrAccumFinish(&str), str.nChar,
130711131359
SQLITE_DYNAMIC);
130712131360
if( str.accError!=SQLITE_OK ){
130713131361
sqlite3_result_null(context);
130714131362
sqlite3_result_error_code(context, str.accError);
@@ -131355,11 +132003,11 @@
131355132003
**
131356132004
** The SUM() function follows the (broken) SQL standard which means
131357132005
** that it returns NULL if it sums over no inputs. TOTAL returns
131358132006
** 0.0 in that case. In addition, TOTAL always returns a float where
131359132007
** 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
131361132009
** it overflows an integer.
131362132010
*/
131363132011
static void sumStep(sqlite3_context *context, int argc, sqlite3_value **argv){
131364132012
SumCtx *p;
131365132013
int type;
@@ -132275,11 +132923,13 @@
132275132923
VFUNCTION(randomblob, 1, 0, 0, randomBlob ),
132276132924
FUNCTION(nullif, 2, 0, 1, nullifFunc ),
132277132925
DFUNCTION(sqlite_version, 0, 0, 0, versionFunc ),
132278132926
DFUNCTION(sqlite_source_id, 0, 0, 0, sourceidFunc ),
132279132927
FUNCTION(sqlite_log, 2, 0, 0, errlogFunc ),
132928
+ FUNCTION(unistr, 1, 0, 0, unistrFunc ),
132280132929
FUNCTION(quote, 1, 0, 0, quoteFunc ),
132930
+ FUNCTION(unistr_quote, 1, 1, 0, quoteFunc ),
132281132931
VFUNCTION(last_insert_rowid, 0, 0, 0, last_insert_rowid),
132282132932
VFUNCTION(changes, 0, 0, 0, changes ),
132283132933
VFUNCTION(total_changes, 0, 0, 0, total_changes ),
132284132934
FUNCTION(replace, 3, 0, 0, replaceFunc ),
132285132935
FUNCTION(zeroblob, 1, 0, 0, zeroblobFunc ),
@@ -134562,11 +135212,11 @@
134562135212
}else if( pLeft->pPrior ){
134563135213
/* In this case set the SF_MultiValue flag only if it was set on pLeft */
134564135214
f = (f & pLeft->selFlags);
134565135215
}
134566135216
pSelect = sqlite3SelectNew(pParse, pRow, 0, 0, 0, 0, 0, f, 0);
134567
- pLeft->selFlags &= ~SF_MultiValue;
135217
+ pLeft->selFlags &= ~(u32)SF_MultiValue;
134568135218
if( pSelect ){
134569135219
pSelect->op = TK_ALL;
134570135220
pSelect->pPrior = pLeft;
134571135221
pLeft = pSelect;
134572135222
}
@@ -139168,52 +139818,52 @@
139168139818
/* 11 */ "notnull",
139169139819
/* 12 */ "dflt_value",
139170139820
/* 13 */ "pk",
139171139821
/* 14 */ "hidden",
139172139822
/* table_info reuses 8 */
139173
- /* 15 */ "schema", /* Used by: table_list */
139174
- /* 16 */ "name",
139823
+ /* 15 */ "name", /* Used by: function_list */
139824
+ /* 16 */ "builtin",
139175139825
/* 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",
139201139851
/* 43 */ "table", /* Used by: foreign_key_check */
139202139852
/* 44 */ "rowid",
139203139853
/* 45 */ "parent",
139204139854
/* 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 */
139213139862
/* 53 */ "database", /* Used by: lock_status */
139214139863
/* 54 */ "status",
139864
+ /* collation_list reuses 33 */
139215139865
/* 55 */ "cache_size", /* Used by: default_cache_size */
139216139866
/* module_list pragma_list reuses 9 */
139217139867
/* 56 */ "timeout", /* Used by: busy_timeout */
139218139868
};
139219139869
@@ -139302,11 +139952,11 @@
139302139952
#endif
139303139953
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
139304139954
{/* zName: */ "collation_list",
139305139955
/* ePragTyp: */ PragTyp_COLLATION_LIST,
139306139956
/* ePragFlg: */ PragFlg_Result0,
139307
- /* ColNames: */ 38, 2,
139957
+ /* ColNames: */ 33, 2,
139308139958
/* iArg: */ 0 },
139309139959
#endif
139310139960
#if !defined(SQLITE_OMIT_COMPILEOPTION_DIAGS)
139311139961
{/* zName: */ "compile_options",
139312139962
/* ePragTyp: */ PragTyp_COMPILE_OPTIONS,
@@ -139337,11 +139987,11 @@
139337139987
#endif
139338139988
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
139339139989
{/* zName: */ "database_list",
139340139990
/* ePragTyp: */ PragTyp_DATABASE_LIST,
139341139991
/* ePragFlg: */ PragFlg_Result0,
139342
- /* ColNames: */ 47, 3,
139992
+ /* ColNames: */ 50, 3,
139343139993
/* iArg: */ 0 },
139344139994
#endif
139345139995
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS) && !defined(SQLITE_OMIT_DEPRECATED)
139346139996
{/* zName: */ "default_cache_size",
139347139997
/* ePragTyp: */ PragTyp_DEFAULT_CACHE_SIZE,
@@ -139417,11 +140067,11 @@
139417140067
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
139418140068
#if !defined(SQLITE_OMIT_INTROSPECTION_PRAGMAS)
139419140069
{/* zName: */ "function_list",
139420140070
/* ePragTyp: */ PragTyp_FUNCTION_LIST,
139421140071
/* ePragFlg: */ PragFlg_Result0,
139422
- /* ColNames: */ 27, 6,
140072
+ /* ColNames: */ 15, 6,
139423140073
/* iArg: */ 0 },
139424140074
#endif
139425140075
#endif
139426140076
{/* zName: */ "hard_heap_limit",
139427140077
/* ePragTyp: */ PragTyp_HARD_HEAP_LIMIT,
@@ -139446,21 +140096,21 @@
139446140096
#endif
139447140097
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS)
139448140098
{/* zName: */ "index_info",
139449140099
/* ePragTyp: */ PragTyp_INDEX_INFO,
139450140100
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
139451
- /* ColNames: */ 21, 3,
140101
+ /* ColNames: */ 27, 3,
139452140102
/* iArg: */ 0 },
139453140103
{/* zName: */ "index_list",
139454140104
/* ePragTyp: */ PragTyp_INDEX_LIST,
139455140105
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
139456
- /* ColNames: */ 38, 5,
140106
+ /* ColNames: */ 33, 5,
139457140107
/* iArg: */ 0 },
139458140108
{/* zName: */ "index_xinfo",
139459140109
/* ePragTyp: */ PragTyp_INDEX_INFO,
139460140110
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
139461
- /* ColNames: */ 21, 6,
140111
+ /* ColNames: */ 27, 6,
139462140112
/* iArg: */ 1 },
139463140113
#endif
139464140114
#if !defined(SQLITE_OMIT_INTEGRITY_CHECK)
139465140115
{/* zName: */ "integrity_check",
139466140116
/* ePragTyp: */ PragTyp_INTEGRITY_CHECK,
@@ -139635,11 +140285,11 @@
139635140285
#endif
139636140286
#if !defined(SQLITE_OMIT_SCHEMA_PRAGMAS) && defined(SQLITE_DEBUG)
139637140287
{/* zName: */ "stats",
139638140288
/* ePragTyp: */ PragTyp_STATS,
139639140289
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result0|PragFlg_SchemaReq,
139640
- /* ColNames: */ 33, 5,
140290
+ /* ColNames: */ 38, 5,
139641140291
/* iArg: */ 0 },
139642140292
#endif
139643140293
#if !defined(SQLITE_OMIT_PAGER_PRAGMAS)
139644140294
{/* zName: */ "synchronous",
139645140295
/* ePragTyp: */ PragTyp_SYNCHRONOUS,
@@ -139654,11 +140304,11 @@
139654140304
/* ColNames: */ 8, 6,
139655140305
/* iArg: */ 0 },
139656140306
{/* zName: */ "table_list",
139657140307
/* ePragTyp: */ PragTyp_TABLE_LIST,
139658140308
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1,
139659
- /* ColNames: */ 15, 6,
140309
+ /* ColNames: */ 21, 6,
139660140310
/* iArg: */ 0 },
139661140311
{/* zName: */ "table_xinfo",
139662140312
/* ePragTyp: */ PragTyp_TABLE_INFO,
139663140313
/* ePragFlg: */ PragFlg_NeedSchema|PragFlg_Result1|PragFlg_SchemaOpt,
139664140314
/* ColNames: */ 8, 7,
@@ -139731,11 +140381,11 @@
139731140381
/* ColNames: */ 0, 0,
139732140382
/* iArg: */ 0 },
139733140383
{/* zName: */ "wal_checkpoint",
139734140384
/* ePragTyp: */ PragTyp_WAL_CHECKPOINT,
139735140385
/* ePragFlg: */ PragFlg_NeedSchema,
139736
- /* ColNames: */ 50, 3,
140386
+ /* ColNames: */ 47, 3,
139737140387
/* iArg: */ 0 },
139738140388
#endif
139739140389
#if !defined(SQLITE_OMIT_FLAG_PRAGMAS)
139740140390
{/* zName: */ "writable_schema",
139741140391
/* ePragTyp: */ PragTyp_FLAG,
@@ -139753,11 +140403,11 @@
139753140403
** When the 0x10 bit of PRAGMA optimize is set, any ANALYZE commands
139754140404
** will be run with an analysis_limit set to the lessor of the value of
139755140405
** the following macro or to the actual analysis_limit if it is non-zero,
139756140406
** in order to prevent PRAGMA optimize from running for too long.
139757140407
**
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
139759140409
** for PRAGMA optimize does not exceed 100 milliseconds against a variety
139760140410
** of test databases on a RaspberryPI-4 compiled using -Os and without
139761140411
** -DSQLITE_DEBUG. Of course, your mileage may vary. For the purpose of
139762140412
** this paragraph, "worst-case" means that ANALYZE ends up being
139763140413
** run on every table in the database. The worst case typically only
@@ -144042,11 +144692,11 @@
144042144692
pNew->iOffset = 0;
144043144693
pNew->selId = ++pParse->nSelect;
144044144694
pNew->addrOpenEphm[0] = -1;
144045144695
pNew->addrOpenEphm[1] = -1;
144046144696
pNew->nSelectRow = 0;
144047
- if( pSrc==0 ) pSrc = sqlite3DbMallocZero(pParse->db, sizeof(*pSrc));
144697
+ if( pSrc==0 ) pSrc = sqlite3DbMallocZero(pParse->db, SZ_SRCLIST_1);
144048144698
pNew->pSrc = pSrc;
144049144699
pNew->pWhere = pWhere;
144050144700
pNew->pGroupBy = pGroupBy;
144051144701
pNew->pHaving = pHaving;
144052144702
pNew->pOrderBy = pOrderBy;
@@ -145425,20 +146075,20 @@
145425146075
/*
145426146076
** Allocate a KeyInfo object sufficient for an index of N key columns and
145427146077
** X extra columns.
145428146078
*/
145429146079
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);
145432146082
if( p ){
145433146083
p->aSortFlags = (u8*)&p->aColl[N+X];
145434146084
p->nKeyField = (u16)N;
145435146085
p->nAllField = (u16)(N+X);
145436146086
p->enc = ENC(db);
145437146087
p->db = db;
145438146088
p->nRef = 1;
145439
- memset(&p[1], 0, nExtra);
146089
+ memset(p->aColl, 0, nExtra);
145440146090
}else{
145441146091
return (KeyInfo*)sqlite3OomFault(db);
145442146092
}
145443146093
return p;
145444146094
}
@@ -149534,11 +150184,11 @@
149534150184
p->pNext = 0;
149535150185
p->pWith = 0;
149536150186
#ifndef SQLITE_OMIT_WINDOWFUNC
149537150187
p->pWinDefn = 0;
149538150188
#endif
149539
- p->selFlags &= ~SF_Compound;
150189
+ p->selFlags &= ~(u32)SF_Compound;
149540150190
assert( (p->selFlags & SF_Converted)==0 );
149541150191
p->selFlags |= SF_Converted;
149542150192
assert( pNew->pPrior!=0 );
149543150193
pNew->pPrior->pNext = pNew;
149544150194
pNew->pLimit = 0;
@@ -149950,11 +150600,11 @@
149950150600
}
149951150601
pTabList = p->pSrc;
149952150602
pEList = p->pEList;
149953150603
if( pParse->pWith && (p->selFlags & SF_View) ){
149954150604
if( p->pWith==0 ){
149955
- p->pWith = (With*)sqlite3DbMallocZero(db, sizeof(With));
150605
+ p->pWith = (With*)sqlite3DbMallocZero(db, SZ_WITH(1) );
149956150606
if( p->pWith==0 ){
149957150607
return WRC_Abort;
149958150608
}
149959150609
}
149960150610
p->pWith->bView = 1;
@@ -151089,10 +151739,11 @@
151089151739
** * The subquery is a UNION ALL of two or more terms
151090151740
** * The subquery does not have a LIMIT clause
151091151741
** * There is no WHERE or GROUP BY or HAVING clauses on the subqueries
151092151742
** * The outer query is a simple count(*) with no WHERE clause or other
151093151743
** extraneous syntax.
151744
+** * None of the subqueries are DISTINCT (forumpost/a860f5fb2e 2025-03-10)
151094151745
**
151095151746
** Return TRUE if the optimization is undertaken.
151096151747
*/
151097151748
static int countOfViewOptimization(Parse *pParse, Select *p){
151098151749
Select *pSub, *pPrior;
@@ -151121,11 +151772,15 @@
151121151772
if( pSub->selFlags & SF_CopyCte ) return 0; /* Not a CTE */
151122151773
do{
151123151774
if( pSub->op!=TK_ALL && pSub->pPrior ) return 0; /* Must be UNION ALL */
151124151775
if( pSub->pWhere ) return 0; /* No WHERE clause */
151125151776
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
+ }
151127151782
assert( pSub->pHaving==0 ); /* Due to the previous */
151128151783
pSub = pSub->pPrior; /* Repeat over compound */
151129151784
}while( pSub );
151130151785
151131151786
/* If we reach this point then it is OK to perform the transformation */
@@ -151133,18 +151788,18 @@
151133151788
db = pParse->db;
151134151789
pCount = pExpr;
151135151790
pExpr = 0;
151136151791
pSub = sqlite3SubqueryDetach(db, pFrom);
151137151792
sqlite3SrcListDelete(db, p->pSrc);
151138
- p->pSrc = sqlite3DbMallocZero(pParse->db, sizeof(*p->pSrc));
151793
+ p->pSrc = sqlite3DbMallocZero(pParse->db, SZ_SRCLIST_1);
151139151794
while( pSub ){
151140151795
Expr *pTerm;
151141151796
pPrior = pSub->pPrior;
151142151797
pSub->pPrior = 0;
151143151798
pSub->pNext = 0;
151144151799
pSub->selFlags |= SF_Aggregate;
151145
- pSub->selFlags &= ~SF_Compound;
151800
+ pSub->selFlags &= ~(u32)SF_Compound;
151146151801
pSub->nSelectRow = 0;
151147151802
sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, pSub->pEList);
151148151803
pTerm = pPrior ? sqlite3ExprDup(db, pCount, 0) : pCount;
151149151804
pSub->pEList = sqlite3ExprListAppend(pParse, 0, pTerm);
151150151805
pTerm = sqlite3PExpr(pParse, TK_SELECT, 0, 0);
@@ -151155,11 +151810,11 @@
151155151810
pExpr = sqlite3PExpr(pParse, TK_PLUS, pTerm, pExpr);
151156151811
}
151157151812
pSub = pPrior;
151158151813
}
151159151814
p->pEList->a[0].pExpr = pExpr;
151160
- p->selFlags &= ~SF_Aggregate;
151815
+ p->selFlags &= ~(u32)SF_Aggregate;
151161151816
151162151817
#if TREETRACE_ENABLED
151163151818
if( sqlite3TreeTrace & 0x200 ){
151164151819
TREETRACE(0x200,pParse,p,("After count-of-view optimization:\n"));
151165151820
sqlite3TreeViewSelect(0, p, 0);
@@ -151362,11 +152017,11 @@
151362152017
sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric,
151363152018
p->pOrderBy);
151364152019
testcase( pParse->earlyCleanup );
151365152020
p->pOrderBy = 0;
151366152021
}
151367
- p->selFlags &= ~SF_Distinct;
152022
+ p->selFlags &= ~(u32)SF_Distinct;
151368152023
p->selFlags |= SF_NoopOrderBy;
151369152024
}
151370152025
sqlite3SelectPrep(pParse, p, 0);
151371152026
if( pParse->nErr ){
151372152027
goto select_end;
@@ -151401,11 +152056,11 @@
151401152056
151402152057
/* Clear the SF_UFSrcCheck flag. The check has already been performed,
151403152058
** and leaving this flag set can cause errors if a compound sub-query
151404152059
** in p->pSrc is flattened into this query and this function called
151405152060
** again as part of compound SELECT processing. */
151406
- p->selFlags &= ~SF_UFSrcCheck;
152061
+ p->selFlags &= ~(u32)SF_UFSrcCheck;
151407152062
}
151408152063
151409152064
if( pDest->eDest==SRT_Output ){
151410152065
sqlite3GenerateColumnNames(pParse, p);
151411152066
}
@@ -151890,11 +152545,11 @@
151890152545
&& OptimizationEnabled(db, SQLITE_GroupByOrder)
151891152546
#ifndef SQLITE_OMIT_WINDOWFUNC
151892152547
&& p->pWin==0
151893152548
#endif
151894152549
){
151895
- p->selFlags &= ~SF_Distinct;
152550
+ p->selFlags &= ~(u32)SF_Distinct;
151896152551
pGroupBy = p->pGroupBy = sqlite3ExprListDup(db, pEList, 0);
151897152552
if( pGroupBy ){
151898152553
for(i=0; i<pGroupBy->nExpr; i++){
151899152554
pGroupBy->a[i].u.x.iOrderByCol = i+1;
151900152555
}
@@ -153924,11 +154579,12 @@
153924154579
Vdbe *v = pParse->pVdbe;
153925154580
sqlite3 *db = pParse->db;
153926154581
ExprList *pNew;
153927154582
Returning *pReturning;
153928154583
Select sSelect;
153929
- SrcList sFrom;
154584
+ SrcList *pFrom;
154585
+ u8 fromSpace[SZ_SRCLIST_1];
153930154586
153931154587
assert( v!=0 );
153932154588
if( !pParse->bReturning ){
153933154589
/* This RETURNING trigger must be for a different statement as
153934154590
** this statement lacks a RETURNING clause. */
@@ -153940,17 +154596,18 @@
153940154596
if( pTrigger != &(pReturning->retTrig) ){
153941154597
/* This RETURNING trigger is for a different statement */
153942154598
return;
153943154599
}
153944154600
memset(&sSelect, 0, sizeof(sSelect));
153945
- memset(&sFrom, 0, sizeof(sFrom));
154601
+ pFrom = (SrcList*)fromSpace;
154602
+ memset(pFrom, 0, SZ_SRCLIST_1);
153946154603
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;
153952154609
sqlite3SelectPrep(pParse, &sSelect, 0);
153953154610
if( pParse->nErr==0 ){
153954154611
assert( db->mallocFailed==0 );
153955154612
sqlite3GenerateColumnNames(pParse, &sSelect);
153956154613
}
@@ -156347,11 +157004,11 @@
156347157004
saved_flags = db->flags;
156348157005
saved_mDbFlags = db->mDbFlags;
156349157006
saved_nChange = db->nChange;
156350157007
saved_nTotalChange = db->nTotalChange;
156351157008
saved_mTrace = db->mTrace;
156352
- db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks;
157009
+ db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks | SQLITE_Comments;
156353157010
db->mDbFlags |= DBFLAG_PreferBuiltin | DBFLAG_Vacuum;
156354157011
db->flags &= ~(u64)(SQLITE_ForeignKeys | SQLITE_ReverseOrder
156355157012
| SQLITE_Defensive | SQLITE_CountRows);
156356157013
db->mTrace = 0;
156357157014
@@ -158476,13 +159133,18 @@
158476159133
WhereLoop *pLoops; /* List of all WhereLoop objects */
158477159134
WhereMemBlock *pMemToFree;/* Memory to free when this object destroyed */
158478159135
Bitmask revMask; /* Mask of ORDER BY terms that need reversing */
158479159136
WhereClause sWC; /* Decomposition of the WHERE clause */
158480159137
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 */
158482159139
};
158483159140
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
+
158484159146
/*
158485159147
** Private interfaces - callable only by other where.c routines.
158486159148
**
158487159149
** where.c:
158488159150
*/
@@ -160929,12 +161591,11 @@
160929161591
*/
160930161592
if( pWInfo->nLevel>1 ){
160931161593
int nNotReady; /* The number of notReady tables */
160932161594
SrcItem *origSrc; /* Original list of tables */
160933161595
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));
160936161597
if( pOrTab==0 ) return notReady;
160937161598
pOrTab->nAlloc = (u8)(nNotReady + 1);
160938161599
pOrTab->nSrc = pOrTab->nAlloc;
160939161600
memcpy(pOrTab->a, pTabItem, sizeof(*pTabItem));
160940161601
origSrc = pWInfo->pTabList->a;
@@ -161473,11 +162134,12 @@
161473162134
Expr *pSubWhere = 0;
161474162135
WhereClause *pWC = &pWInfo->sWC;
161475162136
WhereInfo *pSubWInfo;
161476162137
WhereLoop *pLoop = pLevel->pWLoop;
161477162138
SrcItem *pTabItem = &pWInfo->pTabList->a[pLevel->iFrom];
161478
- SrcList sFrom;
162139
+ SrcList *pFrom;
162140
+ u8 fromSpace[SZ_SRCLIST_1];
161479162141
Bitmask mAll = 0;
161480162142
int k;
161481162143
161482162144
ExplainQueryPlan((pParse, 1, "RIGHT-JOIN %s", pTabItem->pSTab->zName));
161483162145
sqlite3VdbeNoJumpsOutsideSubrtn(v, pRJ->addrSubrtn, pRJ->endSubrtn,
@@ -161517,17 +162179,18 @@
161517162179
if( ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) ) continue;
161518162180
pSubWhere = sqlite3ExprAnd(pParse, pSubWhere,
161519162181
sqlite3ExprDup(pParse->db, pTerm->pExpr, 0));
161520162182
}
161521162183
}
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;
161526162189
assert( pParse->withinRJSubrtn < 100 );
161527162190
pParse->withinRJSubrtn++;
161528
- pSubWInfo = sqlite3WhereBegin(pParse, &sFrom, pSubWhere, 0, 0, 0,
162191
+ pSubWInfo = sqlite3WhereBegin(pParse, pFrom, pSubWhere, 0, 0, 0,
161529162192
WHERE_RIGHT_JOIN, 0);
161530162193
if( pSubWInfo ){
161531162194
int iCur = pLevel->iTabCur;
161532162195
int r = ++pParse->nMem;
161533162196
int nPk;
@@ -163511,15 +164174,20 @@
163511164174
WhereClause *pWC; /* The Where clause being analyzed */
163512164175
Parse *pParse; /* The parsing context */
163513164176
int eDistinct; /* Value to return from sqlite3_vtab_distinct() */
163514164177
u32 mIn; /* Mask of terms that are <col> IN (...) */
163515164178
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 */
163519164182
};
163520164183
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
+
163521164189
/* Forward declaration of methods */
163522164190
static int whereLoopResize(sqlite3*, WhereLoop*, int);
163523164191
163524164192
/*
163525164193
** Return the estimated number of output rows from a WHERE clause
@@ -164580,10 +165248,12 @@
164580165248
if( pSrc->colUsed & MASKBIT(BMS-1) ){
164581165249
nKeyCol += pTable->nCol - BMS + 1;
164582165250
}
164583165251
164584165252
/* 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 */
164585165255
pIdx = sqlite3AllocateIndexObject(pParse->db, nKeyCol+HasRowid(pTable),
164586165256
0, &zNotUsed);
164587165257
if( pIdx==0 ) goto end_auto_index_create;
164588165258
pLoop->u.btree.pIndex = pIdx;
164589165259
pIdx->zName = "auto-index";
@@ -164991,12 +165661,12 @@
164991165661
164992165662
/* Allocate the sqlite3_index_info structure
164993165663
*/
164994165664
pIdxInfo = sqlite3DbMallocZero(pParse->db, sizeof(*pIdxInfo)
164995165665
+ (sizeof(*pIdxCons) + sizeof(*pUsage))*nTerm
164996
- + sizeof(*pIdxOrderBy)*nOrderBy + sizeof(*pHidden)
164997
- + sizeof(sqlite3_value*)*nTerm );
165666
+ + sizeof(*pIdxOrderBy)*nOrderBy
165667
+ + SZ_HIDDENINDEXINFO(nTerm) );
164998165668
if( pIdxInfo==0 ){
164999165669
sqlite3ErrorMsg(pParse, "out of memory");
165000165670
return 0;
165001165671
}
165002165672
pHidden = (struct HiddenIndexInfo*)&pIdxInfo[1];
@@ -170186,14 +170856,11 @@
170186170856
** struct, the contents of WhereInfo.a[], the WhereClause structure
170187170857
** and the WhereMaskSet structure. Since WhereClause contains an 8-byte
170188170858
** field (type Bitmask) it must be aligned on an 8-byte boundary on
170189170859
** some architectures. Hence the ROUND8() below.
170190170860
*/
170191
- nByteWInfo = ROUND8P(sizeof(WhereInfo));
170192
- if( nTabList>1 ){
170193
- nByteWInfo = ROUND8P(nByteWInfo + (nTabList-1)*sizeof(WhereLevel));
170194
- }
170861
+ nByteWInfo = SZ_WHEREINFO(nTabList);
170195170862
pWInfo = sqlite3DbMallocRawNN(db, nByteWInfo + sizeof(WhereLoop));
170196170863
if( db->mallocFailed ){
170197170864
sqlite3DbFree(db, pWInfo);
170198170865
pWInfo = 0;
170199170866
goto whereBeginError;
@@ -172141,11 +172808,11 @@
172141172808
172142172809
p->pSrc = 0;
172143172810
p->pWhere = 0;
172144172811
p->pGroupBy = 0;
172145172812
p->pHaving = 0;
172146
- p->selFlags &= ~SF_Aggregate;
172813
+ p->selFlags &= ~(u32)SF_Aggregate;
172147172814
p->selFlags |= SF_WinRewrite;
172148172815
172149172816
/* Create the ORDER BY clause for the sub-select. This is the concatenation
172150172817
** of the window PARTITION and ORDER BY clauses. Then, if this makes it
172151172818
** redundant, remove the ORDER BY from the parent SELECT. */
@@ -178280,12 +178947,12 @@
178280178947
pRhs = sqlite3SelectNew(pParse,0,pFrom,0,0,0,0,0,0);
178281178948
}
178282178949
if( pRhs ){
178283178950
pRhs->op = (u8)yymsp[-1].minor.yy502;
178284178951
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;
178287178954
if( yymsp[-1].minor.yy502!=TK_ALL ) pParse->hasCompound = 1;
178288178955
}else{
178289178956
sqlite3SelectDelete(pParse->db, pLhs);
178290178957
}
178291178958
yymsp[-2].minor.yy637 = pRhs;
@@ -181025,11 +181692,15 @@
181025181692
tokenType = analyzeOverKeyword((const u8*)&zSql[4], lastTokenParsed);
181026181693
}else if( tokenType==TK_FILTER ){
181027181694
assert( n==6 );
181028181695
tokenType = analyzeFilterKeyword((const u8*)&zSql[6], lastTokenParsed);
181029181696
#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). */
181031181702
zSql += n;
181032181703
continue;
181033181704
}else if( tokenType!=TK_QNUMBER ){
181034181705
Token x;
181035181706
x.z = zSql;
@@ -181920,10 +182591,18 @@
181920182591
}
181921182592
#endif
181922182593
if( rc==SQLITE_OK ){
181923182594
sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage,
181924182595
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 ){
181925182604
sqlite3MemoryBarrier();
181926182605
sqlite3GlobalConfig.isInit = 1;
181927182606
#ifdef SQLITE_EXTRA_INIT
181928182607
bRunExtraInit = 1;
181929182608
#endif
@@ -183396,10 +184075,13 @@
183396184075
sqlite3_mutex_enter(db->mutex);
183397184076
db->busyHandler.xBusyHandler = xBusy;
183398184077
db->busyHandler.pBusyArg = pArg;
183399184078
db->busyHandler.nBusy = 0;
183400184079
db->busyTimeout = 0;
184080
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
184081
+ db->setlkTimeout = 0;
184082
+#endif
183401184083
sqlite3_mutex_leave(db->mutex);
183402184084
return SQLITE_OK;
183403184085
}
183404184086
183405184087
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
@@ -183445,15 +184127,50 @@
183445184127
#endif
183446184128
if( ms>0 ){
183447184129
sqlite3_busy_handler(db, (int(*)(void*,int))sqliteDefaultBusyCallback,
183448184130
(void*)db);
183449184131
db->busyTimeout = ms;
184132
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
184133
+ db->setlkTimeout = ms;
184134
+#endif
183450184135
}else{
183451184136
sqlite3_busy_handler(db, 0, 0);
183452184137
}
183453184138
return SQLITE_OK;
183454184139
}
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
+}
183455184172
183456184173
/*
183457184174
** Cause any pending operation to stop at its earliest opportunity.
183458184175
*/
183459184176
SQLITE_API void sqlite3_interrupt(sqlite3 *db){
@@ -185416,11 +186133,11 @@
185416186133
}else if( pData==0 ){
185417186134
sqlite3_mutex_leave(db->mutex);
185418186135
return SQLITE_OK;
185419186136
}else{
185420186137
size_t n = strlen(zName);
185421
- p = sqlite3_malloc64( sizeof(DbClientData)+n+1 );
186138
+ p = sqlite3_malloc64( SZ_DBCLIENTDATA(n+1) );
185422186139
if( p==0 ){
185423186140
if( xDestructor ) xDestructor(pData);
185424186141
sqlite3_mutex_leave(db->mutex);
185425186142
return SQLITE_NOMEM;
185426186143
}
@@ -185782,12 +186499,12 @@
185782186499
#endif
185783186500
185784186501
/* sqlite3_test_control(SQLITE_TESTCTRL_FK_NO_ACTION, sqlite3 *db, int b);
185785186502
**
185786186503
** 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
185789186506
** they were NO ACTION, regardless of how they are defined.
185790186507
**
185791186508
** NB: One must usually run "PRAGMA writable_schema=RESET" after
185792186509
** using this test-control, before it will take full effect. failing
185793186510
** to reset the schema can result in some unexpected behavior.
@@ -187130,11 +187847,11 @@
187130187847
** }
187131187848
**
187132187849
** Here, array { X } means zero or more occurrences of X, adjacent in
187133187850
** memory. A "position" is an index of a token in the token stream
187134187851
** 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
187136187853
** ending a position list array. POS_END is 0. POS_COLUMN is 1.
187137187854
** The positions numbers are not stored literally but rather as two more
187138187855
** than the difference from the prior position, or the just the position plus
187139187856
** 2 for the first position. Example:
187140187857
**
@@ -187817,10 +188534,23 @@
187817188534
187818188535
#define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32))
187819188536
#define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64)
187820188537
187821188538
#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
+
187822188552
187823188553
#endif /* SQLITE_AMALGAMATION */
187824188554
187825188555
#ifdef SQLITE_DEBUG
187826188556
SQLITE_PRIVATE int sqlite3Fts3Corrupt(void);
@@ -187922,11 +188652,11 @@
187922188652
int inTransaction; /* True after xBegin but before xCommit/xRollback */
187923188653
int mxSavepoint; /* Largest valid xSavepoint integer */
187924188654
#endif
187925188655
187926188656
#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
187928188658
** by special insert command 'test-no-incr-doclist'. */
187929188659
int bNoIncrDoclist;
187930188660
187931188661
/* Number of segments in a level */
187932188662
int nMergeCount;
@@ -187974,11 +188704,11 @@
187974188704
#define FTS3_EVAL_NEXT 1
187975188705
#define FTS3_EVAL_MATCHINFO 2
187976188706
187977188707
/*
187978188708
** 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
187980188710
** FTS3_FULLTEXT_SEARCH. If so, then Fts3Cursor.eSearch - 2 is the index
187981188711
** of the column to be searched. For example, in
187982188712
**
187983188713
** CREATE VIRTUAL TABLE ex1 USING fts3(a,b,c,d);
187984188714
** SELECT docid FROM ex1 WHERE b MATCH 'one two three';
@@ -188047,12 +188777,16 @@
188047188777
/* Variables below this point are populated by fts3_expr.c when parsing
188048188778
** a MATCH expression. Everything above is part of the evaluation phase.
188049188779
*/
188050188780
int nToken; /* Number of tokens in the phrase */
188051188781
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 */
188053188783
};
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))
188054188788
188055188789
/*
188056188790
** A tree of these objects forms the RHS of a MATCH operator.
188057188791
**
188058188792
** If Fts3Expr.eType is FTSQUERY_PHRASE and isLoaded is true, then aDoclist
@@ -190627,11 +191361,11 @@
190627191361
**
190628191362
** The space required to store the output is therefore the sum of the
190629191363
** sizes of the two inputs, plus enough space for exactly one of the input
190630191364
** docids to grow.
190631191365
**
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
190633191367
** order.
190634191368
*/
190635191369
aOut = sqlite3_malloc64((i64)n1+n2+FTS3_VARINT_MAX-1+FTS3_BUFFER_PADDING);
190636191370
if( !aOut ) return SQLITE_NOMEM;
190637191371
@@ -192725,11 +193459,11 @@
192725193459
**
192726193460
** * features at least one token that uses an incremental doclist, and
192727193461
**
192728193462
** * does not contain any deferred tokens.
192729193463
**
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
192731193465
** the Fts3Doclist.pList and nList fields.
192732193466
**
192733193467
** If there is no "next" entry and no error occurs, then *pbEof is set to
192734193468
** 1 before returning. Otherwise, if no error occurs and the iterator is
192735193469
** successfully advanced, *pbEof is set to 0.
@@ -193732,11 +194466,11 @@
193732194466
193733194467
return rc;
193734194468
}
193735194469
193736194470
/*
193737
-** Restart interation for expression pExpr so that the next call to
194471
+** Restart iteration for expression pExpr so that the next call to
193738194472
** fts3EvalNext() visits the first row. Do not allow incremental
193739194473
** loading or merging of phrase doclists for this iteration.
193740194474
**
193741194475
** If *pRc is other than SQLITE_OK when this function is called, it is
193742194476
** a no-op. If an error occurs within this function, *pRc is set to an
@@ -194923,10 +195657,27 @@
194923195657
/*
194924195658
** Function getNextNode(), which is called by fts3ExprParse(), may itself
194925195659
** call fts3ExprParse(). So this forward declaration is required.
194926195660
*/
194927195661
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
+}
194928195679
194929195680
/*
194930195681
** Extract the next token from buffer z (length n) using the tokenizer
194931195682
** and other information (column names etc.) in pParse. Create an Fts3Expr
194932195683
** structure of type FTSQUERY_PHRASE containing a phrase consisting of this
@@ -194948,38 +195699,42 @@
194948195699
sqlite3_tokenizer *pTokenizer = pParse->pTokenizer;
194949195700
sqlite3_tokenizer_module const *pModule = pTokenizer->pModule;
194950195701
int rc;
194951195702
sqlite3_tokenizer_cursor *pCursor;
194952195703
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);
194963195707
if( rc==SQLITE_OK ){
194964195708
const char *zToken;
194965195709
int nToken = 0, iStart = 0, iEnd = 0, iPosition = 0;
194966195710
sqlite3_int64 nByte; /* total space to allocate */
194967195711
194968195712
rc = pModule->xNext(pCursor, &zToken, &nToken, &iStart, &iEnd, &iPosition);
194969195713
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;
194971195726
pRet = (Fts3Expr *)sqlite3Fts3MallocZero(nByte);
194972195727
if( !pRet ){
194973195728
rc = SQLITE_NOMEM;
194974195729
}else{
194975195730
pRet->eType = FTSQUERY_PHRASE;
194976195731
pRet->pPhrase = (Fts3Phrase *)&pRet[1];
194977195732
pRet->pPhrase->nToken = 1;
194978195733
pRet->pPhrase->iColumn = iCol;
194979195734
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];
194981195736
memcpy(pRet->pPhrase->aToken[0].z, zToken, nToken);
194982195737
194983195738
if( iEnd<n && z[iEnd]=='*' ){
194984195739
pRet->pPhrase->aToken[0].isPrefix = 1;
194985195740
iEnd++;
@@ -194999,11 +195754,15 @@
194999195754
}
195000195755
}
195001195756
195002195757
}
195003195758
*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
+ }
195005195764
rc = SQLITE_OK;
195006195765
}
195007195766
195008195767
pModule->xClose(pCursor);
195009195768
}
@@ -195048,11 +195807,11 @@
195048195807
Fts3Expr *p = 0;
195049195808
sqlite3_tokenizer_cursor *pCursor = 0;
195050195809
char *zTemp = 0;
195051195810
i64 nTemp = 0;
195052195811
195053
- const int nSpace = sizeof(Fts3Expr) + sizeof(Fts3Phrase);
195812
+ const int nSpace = sizeof(Fts3Expr) + SZ_FTS3PHRASE(1);
195054195813
int nToken = 0;
195055195814
195056195815
/* The final Fts3Expr data structure, including the Fts3Phrase,
195057195816
** Fts3PhraseToken structures token buffers are all stored as a single
195058195817
** allocation so that the expression can be freed with a single call to
@@ -195420,11 +196179,11 @@
195420196179
int eType = p->eType;
195421196180
isPhrase = (eType==FTSQUERY_PHRASE || p->pLeft);
195422196181
195423196182
/* The isRequirePhrase variable is set to true if a phrase or
195424196183
** 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
195426196185
** isRequirePhrase is set, this is a syntax error.
195427196186
*/
195428196187
if( !isPhrase && isRequirePhrase ){
195429196188
sqlite3Fts3ExprFree(p);
195430196189
rc = SQLITE_ERROR;
@@ -196002,11 +196761,10 @@
196002196761
pTokenizer, 0, azCol, 0, nCol, nCol, zExpr, nExpr, &pExpr
196003196762
);
196004196763
}
196005196764
196006196765
if( rc!=SQLITE_OK && rc!=SQLITE_NOMEM ){
196007
- sqlite3Fts3ExprFree(pExpr);
196008196766
sqlite3_result_error(context, "Error parsing expression", -1);
196009196767
}else if( rc==SQLITE_NOMEM || !(zBuf = exprToString(pExpr, 0)) ){
196010196768
sqlite3_result_error_nomem(context);
196011196769
}else{
196012196770
sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT);
@@ -196245,11 +197003,11 @@
196245197003
pEntry->count++;
196246197004
pEntry->chain = pNew;
196247197005
}
196248197006
196249197007
196250
-/* Resize the hash table so that it cantains "new_size" buckets.
197008
+/* Resize the hash table so that it contains "new_size" buckets.
196251197009
** "new_size" must be a power of 2. The hash table might fail
196252197010
** to resize if sqliteMalloc() fails.
196253197011
**
196254197012
** Return non-zero if a memory allocation error occurs.
196255197013
*/
@@ -196700,11 +197458,11 @@
196700197458
isConsonant(z+2);
196701197459
}
196702197460
196703197461
/*
196704197462
** 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
196706197464
** ending to zTo.
196707197465
**
196708197466
** The input word *pz and zFrom are both in reverse order. zTo
196709197467
** is in normal order.
196710197468
**
@@ -202283,11 +203041,11 @@
202283203041
** previous term. Before this function returns, it is updated to contain a
202284203042
** copy of zTerm/nTerm.
202285203043
**
202286203044
** It is assumed that the buffer associated with pNode is already large
202287203045
** 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.
202289203047
**
202290203048
** If an error (i.e. OOM condition) occurs, an SQLite error code is
202291203049
** returned. Otherwise, SQLITE_OK.
202292203050
*/
202293203051
static int fts3AppendToNode(
@@ -203946,11 +204704,11 @@
203946204704
#endif
203947204705
203948204706
/*
203949204707
** SQLite value pRowid contains the rowid of a row that may or may not be
203950204708
** 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.
203952204710
*/
203953204711
static int fts3DeleteByRowid(
203954204712
Fts3Table *p,
203955204713
sqlite3_value *pRowid,
203956204714
int *pnChng, /* IN/OUT: Decrement if row is deleted */
@@ -204272,12 +205030,16 @@
204272205030
struct MatchinfoBuffer {
204273205031
u8 aRef[3];
204274205032
int nElem;
204275205033
int bGlobal; /* Set if global data is loaded */
204276205034
char *zMatchinfo;
204277
- u32 aMatchinfo[1];
205035
+ u32 aMI[FLEXARRAY];
204278205036
};
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))
204279205041
204280205042
204281205043
/*
204282205044
** The snippet() and offsets() functions both return text values. An instance
204283205045
** of the following structure is used to accumulate those values while the
@@ -204299,17 +205061,17 @@
204299205061
** Allocate a two-slot MatchinfoBuffer object.
204300205062
*/
204301205063
static MatchinfoBuffer *fts3MIBufferNew(size_t nElem, const char *zMatchinfo){
204302205064
MatchinfoBuffer *pRet;
204303205065
sqlite3_int64 nByte = sizeof(u32) * (2*(sqlite3_int64)nElem + 1)
204304
- + sizeof(MatchinfoBuffer);
205066
+ + SZ_MATCHINFOBUFFER(1);
204305205067
sqlite3_int64 nStr = strlen(zMatchinfo);
204306205068
204307205069
pRet = sqlite3Fts3MallocZero(nByte + nStr+1);
204308205070
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]
204311205073
+ sizeof(u32)*((int)nElem+1);
204312205074
pRet->nElem = (int)nElem;
204313205075
pRet->zMatchinfo = ((char*)pRet) + nByte;
204314205076
memcpy(pRet->zMatchinfo, zMatchinfo, nStr+1);
204315205077
pRet->aRef[0] = 1;
@@ -204319,14 +205081,14 @@
204319205081
}
204320205082
204321205083
static void fts3MIBufferFree(void *p){
204322205084
MatchinfoBuffer *pBuf = (MatchinfoBuffer*)((u8*)p - ((u32*)p)[-1]);
204323205085
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]
204326205088
);
204327
- if( (u32*)p==&pBuf->aMatchinfo[1] ){
205089
+ if( (u32*)p==&pBuf->aMI[1] ){
204328205090
pBuf->aRef[1] = 0;
204329205091
}else{
204330205092
pBuf->aRef[2] = 0;
204331205093
}
204332205094
@@ -204339,32 +205101,32 @@
204339205101
void (*xRet)(void*) = 0;
204340205102
u32 *aOut = 0;
204341205103
204342205104
if( p->aRef[1]==0 ){
204343205105
p->aRef[1] = 1;
204344
- aOut = &p->aMatchinfo[1];
205106
+ aOut = &p->aMI[1];
204345205107
xRet = fts3MIBufferFree;
204346205108
}
204347205109
else if( p->aRef[2]==0 ){
204348205110
p->aRef[2] = 1;
204349
- aOut = &p->aMatchinfo[p->nElem+2];
205111
+ aOut = &p->aMI[p->nElem+2];
204350205112
xRet = fts3MIBufferFree;
204351205113
}else{
204352205114
aOut = (u32*)sqlite3_malloc64(p->nElem * sizeof(u32));
204353205115
if( aOut ){
204354205116
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));
204356205118
}
204357205119
}
204358205120
204359205121
*paOut = aOut;
204360205122
return xRet;
204361205123
}
204362205124
204363205125
static void fts3MIBufferSetGlobal(MatchinfoBuffer *p){
204364205126
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));
204366205128
}
204367205129
204368205130
/*
204369205131
** Free a MatchinfoBuffer object allocated using fts3MIBufferNew()
204370205132
*/
@@ -204775,11 +205537,11 @@
204775205537
if( nAppend<0 ){
204776205538
nAppend = (int)strlen(zAppend);
204777205539
}
204778205540
204779205541
/* 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
204781205543
** appended data.
204782205544
*/
204783205545
if( pStr->n+nAppend+1>=pStr->nAlloc ){
204784205546
sqlite3_int64 nAlloc = pStr->nAlloc+(sqlite3_int64)nAppend+100;
204785205547
char *zNew = sqlite3_realloc64(pStr->z, nAlloc);
@@ -207150,11 +207912,11 @@
207150207912
**
207151207913
** When a match if found, the matching entry is moved to become the
207152207914
** most-recently used entry if it isn't so already.
207153207915
**
207154207916
** 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
207156207918
** linger, it needs to increment the nPJRef reference counter.
207157207919
*/
207158207920
static JsonParse *jsonCacheSearch(
207159207921
sqlite3_context *ctx, /* The SQL statement context holding the cache */
207160207922
sqlite3_value *pArg /* Function argument containing SQL text */
@@ -210195,11 +210957,11 @@
210195210957
**
210196210958
** This goes against all historical documentation about how the SQLite
210197210959
** JSON functions were suppose to work. From the beginning, blob was
210198210960
** reserved for expansion and a blob value should have raised an error.
210199210961
** 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
210201210963
** JSON text using readfile(), which returns a blob. For this reason
210202210964
** we will continue to support the bug moving forward.
210203210965
** See for example https://sqlite.org/forum/forumpost/012136abd5292b8d
210204210966
*/
210205210967
}
@@ -212295,10 +213057,18 @@
212295213057
# define NEVER(X) ((X)?(assert(0),1):0)
212296213058
#else
212297213059
# define ALWAYS(X) (X)
212298213060
# define NEVER(X) (X)
212299213061
#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
212300213070
#endif /* !defined(SQLITE_AMALGAMATION) */
212301213071
212302213072
/* Macro to check for 4-byte alignment. Only used inside of assert() */
212303213073
#ifdef SQLITE_DEBUG
212304213074
# define FOUR_BYTE_ALIGNED(X) ((((char*)(X) - (char*)0) & 3)==0)
@@ -212615,13 +213385,17 @@
212615213385
struct RtreeMatchArg {
212616213386
u32 iSize; /* Size of this object */
212617213387
RtreeGeomCallback cb; /* Info about the callback functions */
212618213388
int nParam; /* Number of parameters to the SQL function */
212619213389
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 */
212621213391
};
212622213392
213393
+/* Size of an RtreeMatchArg object with N parameters */
213394
+#define SZ_RTREEMATCHARG(N) \
213395
+ (offsetof(RtreeMatchArg,aParam)+(N)*sizeof(RtreeDValue))
213396
+
212623213397
#ifndef MAX
212624213398
# define MAX(x,y) ((x) < (y) ? (y) : (x))
212625213399
#endif
212626213400
#ifndef MIN
212627213401
# define MIN(x,y) ((x) > (y) ? (y) : (x))
@@ -214306,11 +215080,11 @@
214306215080
214307215081
return rc;
214308215082
}
214309215083
214310215084
/*
214311
-** Return the N-dimensional volumn of the cell stored in *p.
215085
+** Return the N-dimensional volume of the cell stored in *p.
214312215086
*/
214313215087
static RtreeDValue cellArea(Rtree *pRtree, RtreeCell *p){
214314215088
RtreeDValue area = (RtreeDValue)1;
214315215089
assert( pRtree->nDim>=1 && pRtree->nDim<=5 );
214316215090
#ifndef SQLITE_RTREE_INT_ONLY
@@ -216072,11 +216846,11 @@
216072216846
}
216073216847
216074216848
/*
216075216849
** The second and subsequent arguments to this function are a printf()
216076216850
** 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.
216078216852
*/
216079216853
static void rtreeCheckAppendMsg(RtreeCheck *pCheck, const char *zFmt, ...){
216080216854
va_list ap;
216081216855
va_start(ap, zFmt);
216082216856
if( pCheck->rc==SQLITE_OK && pCheck->nErr<RTREE_CHECK_MAX_ERROR ){
@@ -217260,11 +218034,11 @@
217260218034
217261218035
/*
217262218036
** Determine if point (x0,y0) is beneath line segment (x1,y1)->(x2,y2).
217263218037
** Returns:
217264218038
**
217265
-** +2 x0,y0 is on the line segement
218039
+** +2 x0,y0 is on the line segment
217266218040
**
217267218041
** +1 x0,y0 is beneath line segment
217268218042
**
217269218043
** 0 x0,y0 is not on or beneath the line segment or the line segment
217270218044
** is vertical and x0,y0 is not on the line segment
@@ -217366,11 +218140,11 @@
217366218140
}
217367218141
sqlite3_free(p1);
217368218142
sqlite3_free(p2);
217369218143
}
217370218144
217371
-/* Objects used by the overlap algorihm. */
218145
+/* Objects used by the overlap algorithm. */
217372218146
typedef struct GeoEvent GeoEvent;
217373218147
typedef struct GeoSegment GeoSegment;
217374218148
typedef struct GeoOverlap GeoOverlap;
217375218149
struct GeoEvent {
217376218150
double x; /* X coordinate at which event occurs */
@@ -218413,12 +219187,11 @@
218413219187
RtreeGeomCallback *pGeomCtx = (RtreeGeomCallback *)sqlite3_user_data(ctx);
218414219188
RtreeMatchArg *pBlob;
218415219189
sqlite3_int64 nBlob;
218416219190
int memErr = 0;
218417219191
218418
- nBlob = sizeof(RtreeMatchArg) + (nArg-1)*sizeof(RtreeDValue)
218419
- + nArg*sizeof(sqlite3_value*);
219192
+ nBlob = SZ_RTREEMATCHARG(nArg) + nArg*sizeof(sqlite3_value*);
218420219193
pBlob = (RtreeMatchArg *)sqlite3_malloc64(nBlob);
218421219194
if( !pBlob ){
218422219195
sqlite3_result_error_nomem(ctx);
218423219196
}else{
218424219197
int i;
@@ -219509,11 +220282,11 @@
219509220282
** to read from the original database snapshot. In other words, partially
219510220283
** applied transactions are not visible to other clients.
219511220284
**
219512220285
** "RBU" stands for "Resumable Bulk Update". As in a large database update
219513220286
** 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".
219515220288
**
219516220289
**
219517220290
** LIMITATIONS
219518220291
**
219519220292
** An "RBU update" transaction is subject to the following limitations:
@@ -219806,11 +220579,11 @@
219806220579
** sqlite3rbu_close() returns any value other than SQLITE_OK, the contents
219807220580
** of the state tables within the state database are zeroed. This way,
219808220581
** the next call to sqlite3rbu_vacuum() opens a handle that starts a
219809220582
** new RBU vacuum operation.
219810220583
**
219811
-** As with sqlite3rbu_open(), Zipvfs users should rever to the comment
220584
+** As with sqlite3rbu_open(), Zipvfs users should refer to the comment
219812220585
** describing the sqlite3rbu_create_vfs() API function below for
219813220586
** a description of the complications associated with using RBU with
219814220587
** zipvfs databases.
219815220588
*/
219816220589
SQLITE_API sqlite3rbu *sqlite3rbu_vacuum(
@@ -219902,11 +220675,11 @@
219902220675
/*
219903220676
** Close an RBU handle.
219904220677
**
219905220678
** If the RBU update has been completely applied, mark the RBU database
219906220679
** 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.
219908220681
**
219909220682
** If an error has already occurred as part of an sqlite3rbu_step()
219910220683
** or sqlite3rbu_open() call, or if one occurs within this function, an
219911220684
** SQLite error code is returned. Additionally, if pzErrmsg is not NULL,
219912220685
** *pzErrmsg may be set to point to a buffer containing a utf-8 formatted
@@ -224828,11 +225601,11 @@
224828225601
int rc;
224829225602
rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);
224830225603
224831225604
/* If this is an RBU vacuum operation and this is the target database,
224832225605
** 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
224834225607
** similar logic. */
224835225608
if( rc==SQLITE_OK && *pSize==0
224836225609
&& p->pRbu && rbuIsVacuum(p->pRbu)
224837225610
&& (p->openFlags & SQLITE_OPEN_MAIN_DB)
224838225611
){
@@ -228058,11 +228831,11 @@
228058228831
}
228059228832
228060228833
/*
228061228834
** This function is called to initialize the SessionTable.nCol, azCol[]
228062228835
** 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.
228064228837
**
228065228838
** If an error occurs, an error code is stored in sqlite3_session.rc and
228066228839
** non-zero returned. Or, if no error occurs but the table has no primary
228067228840
** key, sqlite3_session.rc is left set to SQLITE_OK and non-zero returned to
228068228841
** indicate that updates on this table should be ignored. SessionTable.abPK
@@ -229881,11 +230654,11 @@
229881230654
int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */
229882230655
void **ppChangeset /* OUT: Buffer containing changeset */
229883230656
){
229884230657
sqlite3 *db = pSession->db; /* Source database handle */
229885230658
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 */
229887230660
int rc; /* Return code */
229888230661
229889230662
assert( xOutput==0 || (pnChangeset==0 && ppChangeset==0) );
229890230663
assert( xOutput!=0 || (pnChangeset!=0 && ppChangeset!=0) );
229891230664
@@ -234315,10 +235088,22 @@
234315235088
# define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&3)==0)
234316235089
#else
234317235090
# define EIGHT_BYTE_ALIGNMENT(X) ((((uptr)(X) - (uptr)0)&7)==0)
234318235091
#endif
234319235092
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
+
234320235105
#endif
234321235106
234322235107
/* Truncate very long tokens to this many bytes. Hard limit is
234323235108
** (65536-1-1-4-9)==65521 bytes. The limiting factor is the 16-bit offset
234324235109
** field that occurs at the start of each leaf page (see fts5_index.c). */
@@ -234387,14 +235172,15 @@
234387235172
**
234388235173
** This object is used by fts5_expr.c and fts5_index.c.
234389235174
*/
234390235175
struct Fts5Colset {
234391235176
int nCol;
234392
- int aiCol[1];
235177
+ int aiCol[FLEXARRAY];
234393235178
};
234394235179
234395
-
235180
+/* Size (int bytes) of a complete Fts5Colset object with N columns. */
235181
+#define SZ_FTS5COLSET(N) (sizeof(i64)*((N+2)/2))
234396235182
234397235183
/**************************************************************************
234398235184
** Interface to code in fts5_config.c. fts5_config.c contains contains code
234399235185
** to parse the arguments passed to the CREATE VIRTUAL TABLE statement.
234400235186
*/
@@ -235219,11 +236005,11 @@
235219236005
*************************************************************************
235220236006
** Driver template for the LEMON parser generator.
235221236007
**
235222236008
** The "lemon" program processes an LALR(1) input grammar file, then uses
235223236009
** 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
235225236011
** interstitial "-" characters) contained in this template is changed into
235226236012
** the value of the %name directive from the grammar. Otherwise, the content
235227236013
** of this template is copied straight through into the generate parser
235228236014
** source file.
235229236015
**
@@ -237373,11 +238159,11 @@
237373238159
** where "N" is the total number of documents in the set and nHit
237374238160
** is the number that contain at least one instance of the phrase
237375238161
** under consideration.
237376238162
**
237377238163
** 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
237379238165
** (1e-6) - roughly the same as a term that appears in just over
237380238166
** half of set of 5,000,000 documents. */
237381238167
double idf = log( (nRow - nHit + 0.5) / (nHit + 0.5) );
237382238168
if( idf<=0.0 ) idf = 1e-6;
237383238169
p->aIDF[i] = idf;
@@ -237836,11 +238622,11 @@
237836238622
**
237837238623
** * All non-ASCII characters,
237838238624
** * The 52 upper and lower case ASCII characters, and
237839238625
** * The 10 integer ASCII characters.
237840238626
** * The underscore character "_" (0x5F).
237841
-** * The unicode "subsitute" character (0x1A).
238627
+** * The unicode "substitute" character (0x1A).
237842238628
*/
237843238629
static int sqlite3Fts5IsBareword(char t){
237844238630
u8 aBareword[128] = {
237845238631
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00 .. 0x0F */
237846238632
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* 0x10 .. 0x1F */
@@ -239154,12 +239940,16 @@
239154239940
Fts5ExprNearset *pNear; /* For FTS5_STRING - cluster of phrases */
239155239941
239156239942
/* Child nodes. For a NOT node, this array always contains 2 entries. For
239157239943
** AND or OR nodes, it contains 2 or more entries. */
239158239944
int nChild; /* Number of child nodes */
239159
- Fts5ExprNode *apChild[1]; /* Array of child nodes */
239945
+ Fts5ExprNode *apChild[FLEXARRAY]; /* Array of child nodes */
239160239946
};
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*))
239161239951
239162239952
#define Fts5NodeIsString(p) ((p)->eType==FTS5_TERM || (p)->eType==FTS5_STRING)
239163239953
239164239954
/*
239165239955
** Invoke the xNext method of an Fts5ExprNode object. This macro should be
@@ -239187,24 +239977,31 @@
239187239977
*/
239188239978
struct Fts5ExprPhrase {
239189239979
Fts5ExprNode *pNode; /* FTS5_STRING node this phrase is part of */
239190239980
Fts5Buffer poslist; /* Current position list */
239191239981
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 */
239193239983
};
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))
239194239988
239195239989
/*
239196239990
** One or more phrases that must appear within a certain token distance of
239197239991
** each other within each matching document.
239198239992
*/
239199239993
struct Fts5ExprNearset {
239200239994
int nNear; /* NEAR parameter */
239201239995
Fts5Colset *pColset; /* Columns to search (NULL -> all columns) */
239202239996
int nPhrase; /* Number of entries in aPhrase[] array */
239203
- Fts5ExprPhrase *apPhrase[1]; /* Array of phrase pointers */
239997
+ Fts5ExprPhrase *apPhrase[FLEXARRAY]; /* Array of phrase pointers */
239204239998
};
239205239999
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*))
239206240003
239207240004
/*
239208240005
** Parse context.
239209240006
*/
239210240007
struct Fts5Parse {
@@ -239360,11 +240157,11 @@
239360240157
assert_expr_depth_ok(sParse.rc, sParse.pExpr);
239361240158
239362240159
/* If the LHS of the MATCH expression was a user column, apply the
239363240160
** implicit column-filter. */
239364240161
if( sParse.rc==SQLITE_OK && iCol<pConfig->nCol ){
239365
- int n = sizeof(Fts5Colset);
240162
+ int n = SZ_FTS5COLSET(1);
239366240163
Fts5Colset *pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&sParse.rc, n);
239367240164
if( pColset ){
239368240165
pColset->nCol = 1;
239369240166
pColset->aiCol[0] = iCol;
239370240167
sqlite3Fts5ParseSetColset(&sParse, sParse.pExpr, pColset);
@@ -240718,11 +241515,11 @@
240718241515
Fts5ExprNearset *pRet = 0;
240719241516
240720241517
if( pParse->rc==SQLITE_OK ){
240721241518
if( pNear==0 ){
240722241519
sqlite3_int64 nByte;
240723
- nByte = sizeof(Fts5ExprNearset) + SZALLOC * sizeof(Fts5ExprPhrase*);
241520
+ nByte = SZ_FTS5EXPRNEARSET(SZALLOC+1);
240724241521
pRet = sqlite3_malloc64(nByte);
240725241522
if( pRet==0 ){
240726241523
pParse->rc = SQLITE_NOMEM;
240727241524
}else{
240728241525
memset(pRet, 0, (size_t)nByte);
@@ -240729,11 +241526,11 @@
240729241526
}
240730241527
}else if( (pNear->nPhrase % SZALLOC)==0 ){
240731241528
int nNew = pNear->nPhrase + SZALLOC;
240732241529
sqlite3_int64 nByte;
240733241530
240734
- nByte = sizeof(Fts5ExprNearset) + nNew * sizeof(Fts5ExprPhrase*);
241531
+ nByte = SZ_FTS5EXPRNEARSET(nNew+1);
240735241532
pRet = (Fts5ExprNearset*)sqlite3_realloc64(pNear, nByte);
240736241533
if( pRet==0 ){
240737241534
pParse->rc = SQLITE_NOMEM;
240738241535
}
240739241536
}else{
@@ -240820,16 +241617,16 @@
240820241617
if( pPhrase==0 || (pPhrase->nTerm % SZALLOC)==0 ){
240821241618
Fts5ExprPhrase *pNew;
240822241619
int nNew = SZALLOC + (pPhrase ? pPhrase->nTerm : 0);
240823241620
240824241621
pNew = (Fts5ExprPhrase*)sqlite3_realloc64(pPhrase,
240825
- sizeof(Fts5ExprPhrase) + sizeof(Fts5ExprTerm) * nNew
241622
+ SZ_FTS5EXPRPHRASE(nNew+1)
240826241623
);
240827241624
if( pNew==0 ){
240828241625
rc = SQLITE_NOMEM;
240829241626
}else{
240830
- if( pPhrase==0 ) memset(pNew, 0, sizeof(Fts5ExprPhrase));
241627
+ if( pPhrase==0 ) memset(pNew, 0, SZ_FTS5EXPRPHRASE(1));
240831241628
pCtx->pPhrase = pPhrase = pNew;
240832241629
pNew->nTerm = nNew - SZALLOC;
240833241630
}
240834241631
}
240835241632
@@ -240933,11 +241730,11 @@
240933241730
}
240934241731
240935241732
if( sCtx.pPhrase==0 ){
240936241733
/* This happens when parsing a token or quoted phrase that contains
240937241734
** 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));
240939241736
}else if( sCtx.pPhrase->nTerm ){
240940241737
sCtx.pPhrase->aTerm[sCtx.pPhrase->nTerm-1].bPrefix = (u8)bPrefix;
240941241738
}
240942241739
assert( pParse->apPhrase!=0 );
240943241740
pParse->apPhrase[pParse->nPhrase-1] = sCtx.pPhrase;
@@ -240968,23 +241765,22 @@
240968241765
if( rc==SQLITE_OK ){
240969241766
pNew->apExprPhrase = (Fts5ExprPhrase**)sqlite3Fts5MallocZero(&rc,
240970241767
sizeof(Fts5ExprPhrase*));
240971241768
}
240972241769
if( rc==SQLITE_OK ){
240973
- pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&rc,
240974
- sizeof(Fts5ExprNode));
241770
+ pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&rc, SZ_FTS5EXPRNODE(1));
240975241771
}
240976241772
if( rc==SQLITE_OK ){
240977241773
pNew->pRoot->pNear = (Fts5ExprNearset*)sqlite3Fts5MallocZero(&rc,
240978
- sizeof(Fts5ExprNearset) + sizeof(Fts5ExprPhrase*));
241774
+ SZ_FTS5EXPRNEARSET(2));
240979241775
}
240980241776
if( rc==SQLITE_OK && ALWAYS(pOrig!=0) ){
240981241777
Fts5Colset *pColsetOrig = pOrig->pNode->pNear->pColset;
240982241778
if( pColsetOrig ){
240983241779
sqlite3_int64 nByte;
240984241780
Fts5Colset *pColset;
240985
- nByte = sizeof(Fts5Colset) + (pColsetOrig->nCol-1) * sizeof(int);
241781
+ nByte = SZ_FTS5COLSET(pColsetOrig->nCol);
240986241782
pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&rc, nByte);
240987241783
if( pColset ){
240988241784
memcpy(pColset, pColsetOrig, (size_t)nByte);
240989241785
}
240990241786
pNew->pRoot->pNear->pColset = pColset;
@@ -241008,11 +241804,11 @@
241008241804
}
241009241805
}
241010241806
}else{
241011241807
/* This happens when parsing a token or quoted phrase that contains
241012241808
** no token characters at all. (e.g ... MATCH '""'). */
241013
- sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprPhrase));
241809
+ sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, SZ_FTS5EXPRPHRASE(1));
241014241810
}
241015241811
}
241016241812
241017241813
if( rc==SQLITE_OK && ALWAYS(sCtx.pPhrase) ){
241018241814
/* All the allocations succeeded. Put the expression object together. */
@@ -241102,11 +241898,11 @@
241102241898
Fts5Colset *pNew; /* New colset object to return */
241103241899
241104241900
assert( pParse->rc==SQLITE_OK );
241105241901
assert( iCol>=0 && iCol<pParse->pConfig->nCol );
241106241902
241107
- pNew = sqlite3_realloc64(p, sizeof(Fts5Colset) + sizeof(int)*nCol);
241903
+ pNew = sqlite3_realloc64(p, SZ_FTS5COLSET(nCol+1));
241108241904
if( pNew==0 ){
241109241905
pParse->rc = SQLITE_NOMEM;
241110241906
}else{
241111241907
int *aiCol = pNew->aiCol;
241112241908
int i, j;
@@ -241137,11 +241933,11 @@
241137241933
static Fts5Colset *sqlite3Fts5ParseColsetInvert(Fts5Parse *pParse, Fts5Colset *p){
241138241934
Fts5Colset *pRet;
241139241935
int nCol = pParse->pConfig->nCol;
241140241936
241141241937
pRet = (Fts5Colset*)sqlite3Fts5MallocZero(&pParse->rc,
241142
- sizeof(Fts5Colset) + sizeof(int)*nCol
241938
+ SZ_FTS5COLSET(nCol+1)
241143241939
);
241144241940
if( pRet ){
241145241941
int i;
241146241942
int iOld = 0;
241147241943
for(i=0; i<nCol; i++){
@@ -241198,11 +241994,11 @@
241198241994
** fails, (*pRc) is set to SQLITE_NOMEM and NULL is returned.
241199241995
*/
241200241996
static Fts5Colset *fts5CloneColset(int *pRc, Fts5Colset *pOrig){
241201241997
Fts5Colset *pRet;
241202241998
if( pOrig ){
241203
- sqlite3_int64 nByte = sizeof(Fts5Colset) + (pOrig->nCol-1) * sizeof(int);
241999
+ sqlite3_int64 nByte = SZ_FTS5COLSET(pOrig->nCol);
241204242000
pRet = (Fts5Colset*)sqlite3Fts5MallocZero(pRc, nByte);
241205242001
if( pRet ){
241206242002
memcpy(pRet, pOrig, (size_t)nByte);
241207242003
}
241208242004
}else{
@@ -241366,21 +242162,21 @@
241366242162
Fts5ExprNode *pRet;
241367242163
241368242164
assert( pNear->nPhrase==1 );
241369242165
assert( pParse->bPhraseToAnd );
241370242166
241371
- nByte = sizeof(Fts5ExprNode) + nTerm*sizeof(Fts5ExprNode*);
242167
+ nByte = SZ_FTS5EXPRNODE(nTerm+1);
241372242168
pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte);
241373242169
if( pRet ){
241374242170
pRet->eType = FTS5_AND;
241375242171
pRet->nChild = nTerm;
241376242172
pRet->iHeight = 1;
241377242173
fts5ExprAssignXNext(pRet);
241378242174
pParse->nPhrase--;
241379242175
for(ii=0; ii<nTerm; ii++){
241380242176
Fts5ExprPhrase *pPhrase = (Fts5ExprPhrase*)sqlite3Fts5MallocZero(
241381
- &pParse->rc, sizeof(Fts5ExprPhrase)
242177
+ &pParse->rc, SZ_FTS5EXPRPHRASE(1)
241382242178
);
241383242179
if( pPhrase ){
241384242180
if( parseGrowPhraseArray(pParse) ){
241385242181
fts5ExprPhraseFree(pPhrase);
241386242182
}else{
@@ -241445,11 +242241,11 @@
241445242241
nChild = 2;
241446242242
if( pLeft->eType==eType ) nChild += pLeft->nChild-1;
241447242243
if( pRight->eType==eType ) nChild += pRight->nChild-1;
241448242244
}
241449242245
241450
- nByte = sizeof(Fts5ExprNode) + sizeof(Fts5ExprNode*)*(nChild-1);
242246
+ nByte = SZ_FTS5EXPRNODE(nChild);
241451242247
pRet = (Fts5ExprNode*)sqlite3Fts5MallocZero(&pParse->rc, nByte);
241452242248
241453242249
if( pRet ){
241454242250
pRet->eType = eType;
241455242251
pRet->pNear = pNear;
@@ -242320,11 +243116,11 @@
242320243116
}
242321243117
return rc;
242322243118
}
242323243119
242324243120
/*
242325
-** Clear the token mappings for all Fts5IndexIter objects mannaged by
243121
+** Clear the token mappings for all Fts5IndexIter objects managed by
242326243122
** the expression passed as the only argument.
242327243123
*/
242328243124
static void sqlite3Fts5ExprClearTokens(Fts5Expr *pExpr){
242329243125
int ii;
242330243126
for(ii=0; ii<pExpr->nPhrase; ii++){
@@ -242355,11 +243151,11 @@
242355243151
242356243152
typedef struct Fts5HashEntry Fts5HashEntry;
242357243153
242358243154
/*
242359243155
** 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
242361243157
** segment.
242362243158
*/
242363243159
242364243160
242365243161
struct Fts5Hash {
@@ -242412,11 +243208,11 @@
242412243208
int iPos; /* Position of last value written */
242413243209
i64 iRowid; /* Rowid of last value written */
242414243210
};
242415243211
242416243212
/*
242417
-** Eqivalent to:
243213
+** Equivalent to:
242418243214
**
242419243215
** char *fts5EntryKey(Fts5HashEntry *pEntry){ return zKey; }
242420243216
*/
242421243217
#define fts5EntryKey(p) ( ((char *)(&(p)[1])) )
242422243218
@@ -243348,13 +244144,17 @@
243348244144
int nRef; /* Object reference count */
243349244145
u64 nWriteCounter; /* Total leaves written to level 0 */
243350244146
u64 nOriginCntr; /* Origin value for next top-level segment */
243351244147
int nSegment; /* Total segments in this structure */
243352244148
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 */
243354244150
};
243355244151
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
+
243356244156
/*
243357244157
** An object of type Fts5SegWriter is used to write to segments.
243358244158
*/
243359244159
struct Fts5PageWriter {
243360244160
int pgno; /* Page number for this page */
@@ -243480,14 +244280,18 @@
243480244280
243481244281
/*
243482244282
** Array of tombstone pages. Reference counted.
243483244283
*/
243484244284
struct Fts5TombstoneArray {
243485
- int nRef; /* Number of pointers to this object */
244285
+ int nRef; /* Number of pointers to this object */
243486244286
int nTombstone;
243487
- Fts5Data *apTombstone[1]; /* Array of tombstone pages */
244287
+ Fts5Data *apTombstone[FLEXARRAY]; /* Array of tombstone pages */
243488244288
};
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*))
243489244293
243490244294
/*
243491244295
** Argument is a pointer to an Fts5Data structure that contains a
243492244296
** leaf page.
243493244297
*/
@@ -243553,12 +244357,15 @@
243553244357
int bRev; /* True to iterate in reverse order */
243554244358
u8 bSkipEmpty; /* True to skip deleted entries */
243555244359
243556244360
i64 iSwitchRowid; /* Firstest rowid of other than aFirst[1] */
243557244361
Fts5CResult *aFirst; /* Current merge state (see above) */
243558
- Fts5SegIter aSeg[1]; /* Array of segment iterators */
244362
+ Fts5SegIter aSeg[FLEXARRAY]; /* Array of segment iterators */
243559244363
};
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))
243560244367
243561244368
/*
243562244369
** An instance of the following type is used to iterate through the contents
243563244370
** of a doclist-index record.
243564244371
**
@@ -243582,12 +244389,16 @@
243582244389
i64 iRowid; /* First rowid on leaf iLeafPgno */
243583244390
};
243584244391
struct Fts5DlidxIter {
243585244392
int nLvl;
243586244393
int iSegid;
243587
- Fts5DlidxLvl aLvl[1];
244394
+ Fts5DlidxLvl aLvl[FLEXARRAY];
243588244395
};
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))
243589244400
243590244401
static void fts5PutU16(u8 *aOut, u16 iVal){
243591244402
aOut[0] = (iVal>>8);
243592244403
aOut[1] = (iVal&0xFF);
243593244404
}
@@ -243952,11 +244763,11 @@
243952244763
** an error occurs, (*pRc) is set to an SQLite error code before returning.
243953244764
*/
243954244765
static void fts5StructureMakeWritable(int *pRc, Fts5Structure **pp){
243955244766
Fts5Structure *p = *pp;
243956244767
if( *pRc==SQLITE_OK && p->nRef>1 ){
243957
- i64 nByte = sizeof(Fts5Structure)+(p->nLevel-1)*sizeof(Fts5StructureLevel);
244768
+ i64 nByte = SZ_FTS5STRUCTURE(p->nLevel);
243958244769
Fts5Structure *pNew;
243959244770
pNew = (Fts5Structure*)sqlite3Fts5MallocZero(pRc, nByte);
243960244771
if( pNew ){
243961244772
int i;
243962244773
memcpy(pNew, p, nByte);
@@ -244026,14 +244837,11 @@
244026244837
if( nLevel>FTS5_MAX_SEGMENT || nLevel<0
244027244838
|| nSegment>FTS5_MAX_SEGMENT || nSegment<0
244028244839
){
244029244840
return FTS5_CORRUPT;
244030244841
}
244031
- nByte = (
244032
- sizeof(Fts5Structure) + /* Main structure */
244033
- sizeof(Fts5StructureLevel) * (nLevel-1) /* aLevel[] array */
244034
- );
244842
+ nByte = SZ_FTS5STRUCTURE(nLevel);
244035244843
pRet = (Fts5Structure*)sqlite3Fts5MallocZero(&rc, nByte);
244036244844
244037244845
if( pRet ){
244038244846
pRet->nRef = 1;
244039244847
pRet->nLevel = nLevel;
@@ -244109,14 +244917,11 @@
244109244917
fts5StructureMakeWritable(pRc, ppStruct);
244110244918
assert( (ppStruct!=0 && (*ppStruct)!=0) || (*pRc)!=SQLITE_OK );
244111244919
if( *pRc==SQLITE_OK ){
244112244920
Fts5Structure *pStruct = *ppStruct;
244113244921
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);
244118244923
244119244924
pStruct = sqlite3_realloc64(pStruct, nByte);
244120244925
if( pStruct ){
244121244926
memset(&pStruct->aLevel[nLevel], 0, sizeof(Fts5StructureLevel));
244122244927
pStruct->nLevel++;
@@ -244651,11 +245456,11 @@
244651245456
Fts5DlidxIter *pIter = 0;
244652245457
int i;
244653245458
int bDone = 0;
244654245459
244655245460
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);
244657245462
Fts5DlidxIter *pNew;
244658245463
244659245464
pNew = (Fts5DlidxIter*)sqlite3_realloc64(pIter, nByte);
244660245465
if( pNew==0 ){
244661245466
p->rc = SQLITE_NOMEM;
@@ -244869,11 +245674,11 @@
244869245674
** leave an error in the Fts5Index object.
244870245675
*/
244871245676
static void fts5SegIterAllocTombstone(Fts5Index *p, Fts5SegIter *pIter){
244872245677
const int nTomb = pIter->pSeg->nPgTombstone;
244873245678
if( nTomb>0 ){
244874
- int nByte = nTomb * sizeof(Fts5Data*) + sizeof(Fts5TombstoneArray);
245679
+ int nByte = SZ_FTS5TOMBSTONEARRAY(nTomb+1);
244875245680
Fts5TombstoneArray *pNew;
244876245681
pNew = (Fts5TombstoneArray*)sqlite3Fts5MallocZero(&p->rc, nByte);
244877245682
if( pNew ){
244878245683
pNew->nTombstone = nTomb;
244879245684
pNew->nRef = 1;
@@ -246330,12 +247135,11 @@
246330247135
Fts5Iter *pNew;
246331247136
i64 nSlot; /* Power of two >= nSeg */
246332247137
246333247138
for(nSlot=2; nSlot<nSeg; nSlot=nSlot*2);
246334247139
pNew = fts5IdxMalloc(p,
246335
- sizeof(Fts5Iter) + /* pNew */
246336
- sizeof(Fts5SegIter) * (nSlot-1) + /* pNew->aSeg[] */
247140
+ SZ_FTS5ITER(nSlot) + /* pNew + pNew->aSeg[] */
246337247141
sizeof(Fts5CResult) * nSlot /* pNew->aFirst[] */
246338247142
);
246339247143
if( pNew ){
246340247144
pNew->nSeg = nSlot;
246341247145
pNew->aFirst = (Fts5CResult*)&pNew->aSeg[nSlot];
@@ -248697,11 +249501,11 @@
248697249501
static Fts5Structure *fts5IndexOptimizeStruct(
248698249502
Fts5Index *p,
248699249503
Fts5Structure *pStruct
248700249504
){
248701249505
Fts5Structure *pNew = 0;
248702
- sqlite3_int64 nByte = sizeof(Fts5Structure);
249506
+ sqlite3_int64 nByte = SZ_FTS5STRUCTURE(1);
248703249507
int nSeg = pStruct->nSegment;
248704249508
int i;
248705249509
248706249510
/* Figure out if this structure requires optimization. A structure does
248707249511
** not require optimization if either:
@@ -248727,10 +249531,11 @@
248727249531
}
248728249532
assert( pStruct->aLevel[i].nMerge<=nThis );
248729249533
}
248730249534
248731249535
nByte += (((i64)pStruct->nLevel)+1) * sizeof(Fts5StructureLevel);
249536
+ assert( nByte==SZ_FTS5STRUCTURE(pStruct->nLevel+2) );
248732249537
pNew = (Fts5Structure*)sqlite3Fts5MallocZero(&p->rc, nByte);
248733249538
248734249539
if( pNew ){
248735249540
Fts5StructureLevel *pLvl;
248736249541
nByte = nSeg * sizeof(Fts5StructureSegment);
@@ -249303,12 +250108,16 @@
249303250108
/* The following are used for other full-token tokendata queries only. */
249304250109
int nIter;
249305250110
int nIterAlloc;
249306250111
Fts5PoslistReader *aPoslistReader;
249307250112
int *aPoslistToIter;
249308
- Fts5Iter *apIter[1];
250113
+ Fts5Iter *apIter[FLEXARRAY];
249309250114
};
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))
249310250119
249311250120
/*
249312250121
** The two input arrays - a1[] and a2[] - are in sorted order. This function
249313250122
** merges the two arrays together and writes the result to output array
249314250123
** aOut[]. aOut[] is guaranteed to be large enough to hold the result.
@@ -249377,11 +250186,11 @@
249377250186
}
249378250187
249379250188
/*
249380250189
** Sort the contents of the pT->aMap[] array.
249381250190
**
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
249383250192
** is left in Fts5Index.rc before returning.
249384250193
*/
249385250194
static void fts5TokendataIterSortMap(Fts5Index *p, Fts5TokenDataIter *pT){
249386250195
Fts5TokenDataMap *aTmp = 0;
249387250196
int nByte = pT->nMap * sizeof(Fts5TokenDataMap);
@@ -249568,11 +250377,11 @@
249568250377
if( iIdx==0
249569250378
&& p->pConfig->eDetail==FTS5_DETAIL_FULL
249570250379
&& p->pConfig->bPrefixInsttoken
249571250380
){
249572250381
s.pTokendata = &s2;
249573
- s2.pT = (Fts5TokenDataIter*)fts5IdxMalloc(p, sizeof(*s2.pT));
250382
+ s2.pT = (Fts5TokenDataIter*)fts5IdxMalloc(p, SZ_FTS5TOKENDATAITER(1));
249574250383
}
249575250384
249576250385
if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){
249577250386
s.xMerge = fts5MergeRowidLists;
249578250387
s.xAppend = fts5AppendRowid;
@@ -249696,19 +250505,21 @@
249696250505
** The %_data table is completely empty when this function is called. This
249697250506
** function populates it with the initial structure objects for each index,
249698250507
** and the initial version of the "averages" record (a zero-byte blob).
249699250508
*/
249700250509
static int sqlite3Fts5IndexReinit(Fts5Index *p){
249701
- Fts5Structure s;
250510
+ Fts5Structure *pTmp;
250511
+ u8 tmpSpace[SZ_FTS5STRUCTURE(1)];
249702250512
fts5StructureInvalidate(p);
249703250513
fts5IndexDiscardData(p);
249704
- memset(&s, 0, sizeof(Fts5Structure));
250514
+ pTmp = (Fts5Structure*)tmpSpace;
250515
+ memset(pTmp, 0, SZ_FTS5STRUCTURE(1));
249705250516
if( p->pConfig->bContentlessDelete ){
249706
- s.nOriginCntr = 1;
250517
+ pTmp->nOriginCntr = 1;
249707250518
}
249708250519
fts5DataWrite(p, FTS5_AVERAGES_ROWID, (const u8*)"", 0);
249709
- fts5StructureWrite(p, &s);
250520
+ fts5StructureWrite(p, pTmp);
249710250521
return fts5IndexReturn(p);
249711250522
}
249712250523
249713250524
/*
249714250525
** Open a new Fts5Index handle. If the bCreate argument is true, create
@@ -249912,11 +250723,11 @@
249912250723
Fts5TokenDataIter *pRet = pIn;
249913250724
249914250725
if( p->rc==SQLITE_OK ){
249915250726
if( pIn==0 || pIn->nIter==pIn->nIterAlloc ){
249916250727
int nAlloc = pIn ? pIn->nIterAlloc*2 : 16;
249917
- int nByte = nAlloc * sizeof(Fts5Iter*) + sizeof(Fts5TokenDataIter);
250728
+ int nByte = SZ_FTS5TOKENDATAITER(nAlloc+1);
249918250729
Fts5TokenDataIter *pNew = (Fts5TokenDataIter*)sqlite3_realloc(pIn, nByte);
249919250730
249920250731
if( pNew==0 ){
249921250732
p->rc = SQLITE_NOMEM;
249922250733
}else{
@@ -250428,11 +251239,12 @@
250428251239
250429251240
memset(&ctx, 0, sizeof(ctx));
250430251241
250431251242
fts5BufferGrow(&p->rc, &token, nToken+1);
250432251243
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));
250434251246
250435251247
if( p->rc==SQLITE_OK ){
250436251248
250437251249
/* Fill in the token prefix to search for */
250438251250
token.p[0] = FTS5_MAIN_PREFIX;
@@ -250559,11 +251371,12 @@
250559251371
assert( p->pConfig->eDetail!=FTS5_DETAIL_FULL );
250560251372
assert( pIter->pTokenDataIter || pIter->nSeg>0 );
250561251373
if( pIter->nSeg>0 ){
250562251374
/* This is a prefix term iterator. */
250563251375
if( pT==0 ){
250564
- pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, sizeof(*pT));
251376
+ pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc,
251377
+ SZ_FTS5TOKENDATAITER(1));
250565251378
pIter->pTokenDataIter = pT;
250566251379
}
250567251380
if( pT ){
250568251381
fts5TokendataIterAppendMap(p, pT, pT->terms.n, nToken, iRowid, iPos);
250569251382
fts5BufferAppendBlob(&p->rc, &pT->terms, nToken, (const u8*)pToken);
@@ -251593,11 +252406,11 @@
251593252406
}
251594252407
#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */
251595252408
251596252409
#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
251597252410
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 */
251599252412
fts5DecodeRowid(iKey, &bTomb, &iSegid, &bDlidx, &iHeight, &iPgno);
251600252413
251601252414
if( iSegid==0 ){
251602252415
if( iKey==FTS5_AVERAGES_ROWID ){
251603252416
sqlite3Fts5BufferAppendPrintf(pRc, pBuf, "{averages} ");
@@ -252554,13 +253367,15 @@
252554253367
struct Fts5Sorter {
252555253368
sqlite3_stmt *pStmt;
252556253369
i64 iRowid; /* Current rowid */
252557253370
const u8 *aPoslist; /* Position lists for current row */
252558253371
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 */
252560253373
};
252561253374
253375
+/* Size (int bytes) of an Fts5Sorter object with N indexes */
253376
+#define SZ_FTS5SORTER(N) (offsetof(Fts5Sorter,nIdx)+((N+2)/2)*sizeof(i64))
252562253377
252563253378
/*
252564253379
** Virtual-table cursor object.
252565253380
**
252566253381
** iSpecial:
@@ -253434,11 +254249,11 @@
253434254249
int rc;
253435254250
const char *zRank = pCsr->zRank;
253436254251
const char *zRankArgs = pCsr->zRankArgs;
253437254252
253438254253
nPhrase = sqlite3Fts5ExprPhraseCount(pCsr->pExpr);
253439
- nByte = sizeof(Fts5Sorter) + sizeof(int) * (nPhrase-1);
254254
+ nByte = SZ_FTS5SORTER(nPhrase);
253440254255
pSorter = (Fts5Sorter*)sqlite3_malloc64(nByte);
253441254256
if( pSorter==0 ) return SQLITE_NOMEM;
253442254257
memset(pSorter, 0, (size_t)nByte);
253443254258
pSorter->nIdx = nPhrase;
253444254259
@@ -255960,11 +256775,11 @@
255960256775
int nArg, /* Number of args */
255961256776
sqlite3_value **apUnused /* Function arguments */
255962256777
){
255963256778
assert( nArg==0 );
255964256779
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);
255966256781
}
255967256782
255968256783
/*
255969256784
** Implementation of fts5_locale(LOCALE, TEXT) function.
255970256785
**
@@ -256185,12 +257000,12 @@
256185257000
/* If SQLITE_FTS5_ENABLE_TEST_MI is defined, assume that the file
256186257001
** fts5_test_mi.c is compiled and linked into the executable. And call
256187257002
** its entry point to enable the matchinfo() demo. */
256188257003
#ifdef SQLITE_FTS5_ENABLE_TEST_MI
256189257004
if( rc==SQLITE_OK ){
256190
- extern int sqlite3Fts5TestRegisterMatchinfo(sqlite3*);
256191
- rc = sqlite3Fts5TestRegisterMatchinfo(db);
257005
+ extern int sqlite3Fts5TestRegisterMatchinfoAPI(fts5_api*);
257006
+ rc = sqlite3Fts5TestRegisterMatchinfoAPI(&pGlobal->api);
256192257007
}
256193257008
#endif
256194257009
256195257010
return rc;
256196257011
}
256197257012
--- 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 @@
146146
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
147147
** [sqlite_version()] and [sqlite_source_id()].
148148
*/
149149
#define SQLITE_VERSION "3.50.0"
150150
#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"
152152
153153
/*
154154
** CAPI3REF: Run-Time Library Version Numbers
155155
** KEYWORDS: sqlite3_version sqlite3_sourceid
156156
**
@@ -1160,10 +1160,16 @@
11601160
** to block for up to M milliseconds before failing when attempting to
11611161
** obtain a file lock using the xLock or xShmLock methods of the VFS.
11621162
** The parameter is a pointer to a 32-bit signed integer that contains
11631163
** the value that M is to be set to. Before returning, the 32-bit signed
11641164
** 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.
11651171
**
11661172
** <li>[[SQLITE_FCNTL_DATA_VERSION]]
11671173
** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to
11681174
** a database file. The argument is a pointer to a 32-bit unsigned integer.
11691175
** The "data version" for the pager is written into the pointer. The
@@ -1257,10 +1263,11 @@
12571263
#define SQLITE_FCNTL_CKPT_START 39
12581264
#define SQLITE_FCNTL_EXTERNAL_READER 40
12591265
#define SQLITE_FCNTL_CKSM_FILE 41
12601266
#define SQLITE_FCNTL_RESET_CACHE 42
12611267
#define SQLITE_FCNTL_NULL_IO 43
1268
+#define SQLITE_FCNTL_BLOCK_ON_CONNECT 44
12621269
12631270
/* deprecated names */
12641271
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
12651272
#define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE
12661273
#define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO
@@ -3013,10 +3020,48 @@
30133020
**
30143021
** See also: [PRAGMA busy_timeout]
30153022
*/
30163023
SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms);
30173024
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
+
30183063
/*
30193064
** CAPI3REF: Convenience Routines For Running Queries
30203065
** METHOD: sqlite3
30213066
**
30223067
** This is a legacy interface that is preserved for backwards compatibility.
@@ -5128,11 +5173,11 @@
51285173
** For all versions of SQLite up to and including 3.6.23.1, a call to
51295174
** [sqlite3_reset()] was required after sqlite3_step() returned anything
51305175
** other than [SQLITE_ROW] before any subsequent invocation of
51315176
** sqlite3_step(). Failure to reset the prepared statement using
51325177
** [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]),
51345179
** sqlite3_step() began
51355180
** calling [sqlite3_reset()] automatically in this circumstance rather
51365181
** than returning [SQLITE_MISUSE]. This is not considered a compatibility
51375182
** break because any application that ever receives an SQLITE_MISUSE error
51385183
** is broken by definition. The [SQLITE_OMIT_AUTORESET] compile-time option
@@ -7024,10 +7069,12 @@
70247069
** ^Any callback set by a previous call to this function
70257070
** for the same database connection is overridden.
70267071
**
70277072
** ^The second argument is a pointer to the function to invoke when a
70287073
** 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.
70297076
** ^The first argument to the callback is a copy of the third argument
70307077
** to sqlite3_update_hook().
70317078
** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE],
70327079
** or [SQLITE_UPDATE], depending on the operation that caused the callback
70337080
** to be invoked.
70347081
--- 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
--- skins/default/css.txt
+++ skins/default/css.txt
@@ -356,13 +356,14 @@
356356
.content pre, table.numbered-lines > tbody > tr {
357357
hyphens: none;
358358
line-height: 1.25;
359359
}
360360
361
-.content ul:not(.browser) li {
361
+.content ul:not(.browser) > li {
362362
list-style-type: disc;
363363
}
364
+
364365
div.filetree ul li.dir,
365366
div.filetree ul li.subdir,
366367
div.filetree ul li.file{
367368
list-style-type: none;
368369
}
369370
--- 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 @@
7878
static int numManifests;
7979
8080
if( cachedManifest == -1 ){
8181
int i;
8282
Blob repo;
83
- cachedManifest = db_get_manifest_setting();
83
+ cachedManifest = db_get_manifest_setting(0);
8484
numManifests = 0;
8585
for(i=0; i<count(aManifestflags); i++){
8686
if( cachedManifest&aManifestflags[i].flg ) {
8787
azManifests[numManifests++] = aManifestflags[i].fname;
8888
}
8989
--- 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 @@
5151
@ -- f - Forum posts
5252
@ -- k - ** Special: Unsubscribed using /oneclickunsub
5353
@ -- n - New forum threads
5454
@ -- r - Replies to my own forum posts
5555
@ -- t - Ticket changes
56
+@ -- u - Elevation of users' permissions (admins only)
5657
@ -- w - Wiki changes
5758
@ -- x - Edits to forum posts
5859
@ -- Probably different codes will be added in the future. In the future
5960
@ -- we might also add a separate table that allows subscribing to email
6061
@ -- notifications for specific branches or tags or tickets.
@@ -1133,11 +1134,11 @@
11331134
** designated host and port and all times.
11341135
*/
11351136
11361137
11371138
/*
1138
-** COMMAND: alerts*
1139
+** COMMAND: alerts* abbrv-subcom
11391140
**
11401141
** Usage: %fossil alerts SUBCOMMAND ARGS...
11411142
**
11421143
** Subcommands:
11431144
**
@@ -1258,11 +1259,11 @@
12581259
g.argc = 3;
12591260
}
12601261
pSetting = setting_info(&nSetting);
12611262
for(; nSetting>0; nSetting--, pSetting++ ){
12621263
if( strncmp(pSetting->name,"email-",6)!=0 ) continue;
1263
- print_setting(pSetting, 0);
1264
+ print_setting(pSetting, 0, 0);
12641265
}
12651266
}else
12661267
if( strncmp(zCmd, "status", nCmd)==0 ){
12671268
Stmt q;
12681269
int iCutoff;
@@ -1273,11 +1274,11 @@
12731274
verify_all_options();
12741275
if( g.argc!=3 ) usage("status");
12751276
pSetting = setting_info(&nSetting);
12761277
for(; nSetting>0; nSetting--, pSetting++ ){
12771278
if( strncmp(pSetting->name,"email-",6)!=0 ) continue;
1278
- print_setting(pSetting, 0);
1279
+ print_setting(pSetting, 0, 0);
12791280
}
12801281
n = db_int(0,"SELECT count(*) FROM pending_alert WHERE NOT sentSep");
12811282
fossil_print(zFmt/*works-like:"%s%d"*/, "pending-alerts", n);
12821283
n = db_int(0,"SELECT count(*) FROM pending_alert WHERE NOT sentDigest");
12831284
fossil_print(zFmt/*works-like:"%s%d"*/, "pending-digest-alerts", n);
@@ -1563,10 +1564,11 @@
15631564
if( g.perm.Read && PB("sc") ) ssub[nsub++] = 'c';
15641565
if( g.perm.RdForum && PB("sf") ) ssub[nsub++] = 'f';
15651566
if( g.perm.RdForum && PB("sn") ) ssub[nsub++] = 'n';
15661567
if( g.perm.RdForum && PB("sr") ) ssub[nsub++] = 'r';
15671568
if( g.perm.RdTkt && PB("st") ) ssub[nsub++] = 't';
1569
+ if( g.perm.Admin && PB("su") ) ssub[nsub++] = 'u';
15681570
if( g.perm.RdWiki && PB("sw") ) ssub[nsub++] = 'w';
15691571
if( g.perm.RdForum && PB("sx") ) ssub[nsub++] = 'x';
15701572
ssub[nsub] = 0;
15711573
zCode = db_text(0,
15721574
"INSERT INTO subscriber(semail,suname,"
@@ -1627,10 +1629,11 @@
16271629
if( g.perm.Read ) cgi_set_parameter_nocopy("sc","1",1);
16281630
if( g.perm.RdForum ) cgi_set_parameter_nocopy("sf","1",1);
16291631
if( g.perm.RdForum ) cgi_set_parameter_nocopy("sn","1",1);
16301632
if( g.perm.RdForum ) cgi_set_parameter_nocopy("sr","1",1);
16311633
if( g.perm.RdTkt ) cgi_set_parameter_nocopy("st","1",1);
1634
+ if( g.perm.Admin ) cgi_set_parameter_nocopy("su","1",1);
16321635
if( g.perm.RdWiki ) cgi_set_parameter_nocopy("sw","1",1);
16331636
}
16341637
@ <p>To receive email notifications for changes to this
16351638
@ repository, fill out the form below and press the "Submit" button.</p>
16361639
form_begin(0, "%R/subscribe");
@@ -1699,10 +1702,14 @@
16991702
}
17001703
if( g.perm.RdWiki ){
17011704
@ <label><input type="checkbox" name="sw" %s(PCK("sw"))> \
17021705
@ Wiki</label><br>
17031706
}
1707
+ if( g.perm.Admin ){
1708
+ @ <label><input type="checkbox" name="su" %s(PCK("su"))> \
1709
+ @ User permission elevation</label>
1710
+ }
17041711
di = PB("di");
17051712
@ </td></tr>
17061713
@ <tr>
17071714
@ <td class="form_label">Delivery:</td>
17081715
@ <td><select size="1" name="di">
@@ -1820,11 +1827,11 @@
18201827
** verifying the email address.
18211828
*/
18221829
void alert_page(void){
18231830
const char *zName = 0; /* Value of the name= query parameter */
18241831
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 */
18261833
int sn, sr;
18271834
int sdigest = 0, sdonotcall = 0, sverified = 0; /* Other fields */
18281835
int isLogin; /* True if logged in as an individual */
18291836
const char *ssub = 0; /* Subscription flags */
18301837
const char *semail = 0; /* Email address */
@@ -1881,10 +1888,11 @@
18811888
if( g.perm.Read && PB("sc") ) newSsub[nsub++] = 'c';
18821889
if( g.perm.RdForum && PB("sf") ) newSsub[nsub++] = 'f';
18831890
if( g.perm.RdForum && PB("sn") ) newSsub[nsub++] = 'n';
18841891
if( g.perm.RdForum && PB("sr") ) newSsub[nsub++] = 'r';
18851892
if( g.perm.RdTkt && PB("st") ) newSsub[nsub++] = 't';
1893
+ if( g.perm.Admin && PB("su") ) newSsub[nsub++] = 'u';
18861894
if( g.perm.RdWiki && PB("sw") ) newSsub[nsub++] = 'w';
18871895
if( g.perm.RdForum && PB("sx") ) newSsub[nsub++] = 'x';
18881896
newSsub[nsub] = 0;
18891897
ssub = newSsub;
18901898
blob_init(&update, "UPDATE subscriber SET", -1);
@@ -1979,10 +1987,11 @@
19791987
sc = strchr(ssub,'c')!=0;
19801988
sf = strchr(ssub,'f')!=0;
19811989
sn = strchr(ssub,'n')!=0;
19821990
sr = strchr(ssub,'r')!=0;
19831991
st = strchr(ssub,'t')!=0;
1992
+ su = strchr(ssub,'u')!=0;
19841993
sw = strchr(ssub,'w')!=0;
19851994
sx = strchr(ssub,'x')!=0;
19861995
smip = db_column_text(&q, 5);
19871996
mtime = db_column_text(&q, 7);
19881997
sctime = db_column_text(&q, 8);
@@ -2097,11 +2106,19 @@
20972106
@ <label><input type="checkbox" name="st" %s(st?"checked":"")>\
20982107
@ Ticket changes</label><br>
20992108
}
21002109
if( g.perm.RdWiki ){
21012110
@ <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>
21032120
}
21042121
@ </td></tr>
21052122
if( strchr(ssub,'k')!=0 ){
21062123
@ <tr><td></td><td>&nbsp;&uarr;&nbsp;
21072124
@ Note: User did a one-click unsubscribe</td></tr>
@@ -2529,15 +2546,16 @@
25292546
** f An original forum post
25302547
** n New forum threads
25312548
** r Replies to my forum posts
25322549
** x An edit to a prior forum post
25332550
** t A new ticket or a change to an existing ticket
2551
+** u A user was added or received new permissions
25342552
** w A change to a wiki page
25352553
** x Edits to forum posts
25362554
*/
25372555
struct EmailEvent {
2538
- int type; /* 'c', 'f', 'n', 'r', 't', 'w', 'x' */
2556
+ int type; /* 'c', 'f', 'n', 'r', 't', 'u', 'w', 'x' */
25392557
int needMod; /* Pending moderator approval */
25402558
Blob hdr; /* Header content, for forum entries */
25412559
Blob txt; /* Text description to appear in an alert */
25422560
char *zFromName; /* Human name of the sender */
25432561
char *zPriors; /* Upthread sender IDs for forum posts */
@@ -2932,10 +2950,11 @@
29322950
);
29332951
if( strchr(zSub, 'a') ) blob_appendf(pBody, " * Announcements\n");
29342952
if( strchr(zSub, 'c') ) blob_appendf(pBody, " * Check-ins\n");
29352953
if( strchr(zSub, 'f') ) blob_appendf(pBody, " * Forum posts\n");
29362954
if( strchr(zSub, 't') ) blob_appendf(pBody, " * Ticket changes\n");
2955
+ if( strchr(zSub, 'u') ) blob_appendf(pBody, " * User permission elevation\n");
29372956
if( strchr(zSub, 'w') ) blob_appendf(pBody, " * Wiki changes\n");
29382957
blob_appendf(pBody, "\n"
29392958
"If you take no action, your subscription will expire and you will be\n"
29402959
"unsubscribed in about %d days. To make other changes or to unsubscribe\n"
29412960
"immediately, visit the following webpage:\n\n"
@@ -3144,10 +3163,11 @@
31443163
switch( p->type ){
31453164
case 'x': case 'f':
31463165
case 'n': case 'r': xType = '5'; break;
31473166
case 't': xType = 'q'; break;
31483167
case 'w': xType = 'l'; break;
3168
+ /* Note: case 'u' is not handled here */
31493169
}
31503170
if( strchr(zCap,xType)==0 ) continue;
31513171
}
31523172
}else if( strchr(zCap,'s')!=0 || strchr(zCap,'a')!=0 ){
31533173
/* Setup and admin users can get any notification that does not
@@ -3160,10 +3180,11 @@
31603180
case 'c': xType = 'o'; break;
31613181
case 'x': case 'f':
31623182
case 'n': case 'r': xType = '2'; break;
31633183
case 't': xType = 'r'; break;
31643184
case 'w': xType = 'j'; break;
3185
+ /* Note: case 'u' is not handled here */
31653186
}
31663187
if( strchr(zCap,xType)==0 ) continue;
31673188
}
31683189
if( blob_size(&p->hdr)>0 ){
31693190
/* This alert should be sent as a separate email */
31703191
--- 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>&nbsp;&uarr;&nbsp;
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>&nbsp;&uarr;&nbsp;
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 @@
5151
blob_appendf(pExtra, " %s", g.argv[i]);
5252
}
5353
}
5454
5555
/*
56
-** COMMAND: all
56
+** COMMAND: all abbrv-subcom
5757
**
5858
** Usage: %fossil all SUBCOMMAND ...
5959
**
6060
** The ~/.fossil file records the location of all repositories for a
6161
** user. This command performs certain operations on all repositories
@@ -316,10 +316,11 @@
316316
zCmd = "repack";
317317
}else if( fossil_strcmp(zCmd, "set")==0
318318
|| fossil_strcmp(zCmd, "setting")==0
319319
|| fossil_strcmp(zCmd, "settings")==0 ){
320320
zCmd = "settings -R";
321
+ collect_argument(&extra, "changed", 0);
321322
collect_argv(&extra, 3);
322323
}else if( fossil_strcmp(zCmd, "unset")==0 ){
323324
zCmd = "unset -R";
324325
collect_argv(&extra, 3);
325326
}else if( fossil_strcmp(zCmd, "fts-config")==0 ){
326327
--- 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 @@
431431
blob_reset(&in);
432432
}
433433
434434
435435
/*
436
-** COMMAND: test-wiki-relink
436
+** COMMAND: test-relink-wiki
437437
**
438
-** Usage: %fossil test-wiki-relink WIKI-PAGE-NAME
438
+** Usage: %fossil test-relink-wiki WIKI-PAGE-NAME
439439
**
440440
** Run the backlink_wiki_refresh() procedure on the wiki page
441441
** named. WIKI-PAGE-NAME can be a glob pattern or a prefix
442442
** of the wiki page.
443443
*/
444444
--- 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 @@
3737
void bisect_path(void){
3838
PathNode *p;
3939
bisect.bad = db_lget_int("bisect-bad", 0);
4040
bisect.good = db_lget_int("bisect-good", 0);
4141
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);
4343
}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);
4545
}else if( bisect.bad==0 && bisect.good==0 ){
4646
fossil_fatal("neither \"good\" nor \"bad\" versions have been identified");
4747
}else{
4848
Bag skip;
4949
int bDirect = bisect_option("direct-only");
@@ -55,11 +55,11 @@
5555
if( blob_str(&id)[0]=='s' ){
5656
bag_insert(&skip, atoi(blob_str(&id)+1));
5757
}
5858
}
5959
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);
6161
bag_clear(&skip);
6262
if( p==0 ){
6363
char *zBad = db_text(0,"SELECT uuid FROM blob WHERE rid=%d",bisect.bad);
6464
char *zGood = db_text(0,"SELECT uuid FROM blob WHERE rid=%d",bisect.good);
6565
fossil_fatal("no path from good ([%S]) to bad ([%S]) or back",
@@ -292,11 +292,11 @@
292292
if( iCurrent>0 ){
293293
bisect_log_append(&ins, ++cnt, "CURRENT", iCurrent);
294294
}
295295
if( bDetail && lastGood>0 && lastBad>0 ){
296296
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);
298298
while( p ){
299299
bisect_log_append(&ins, ++cnt, 0, p->rid);
300300
p = p->u.pTo;
301301
}
302302
path_reset();
303303
--- 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 @@
19941994
zUtf8 = blob_str(pBlob) + bomSize;
19951995
zUtf8 = fossil_unicode_to_utf8(zUtf8);
19961996
blob_reset(pBlob);
19971997
blob_set_dynamic(pBlob, zUtf8);
19981998
}else if( useMbcs && invalid_utf8(pBlob) ){
1999
-#if defined(_WIN32) || defined(__CYGWIN__)
1999
+#if defined(_WIN32)
20002000
zUtf8 = fossil_mbcs_to_utf8(blob_str(pBlob));
20012001
blob_reset(pBlob);
20022002
blob_append(pBlob, zUtf8, -1);
20032003
fossil_mbcs_free(zUtf8);
20042004
#else
20052005
--- 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 @@
598598
** Usage: %fossil branch SUBCOMMAND ... ?OPTIONS?
599599
**
600600
** Run various subcommands to manage branches of the open repository or
601601
** of the repository identified by the -R or --repository option.
602602
**
603
-** > fossil branch close|reopen ?OPTIONS? BRANCH-NAME ?...BRANCH-NAMES?
603
+** > fossil branch close|reopen ?OPTIONS? BRANCH-NAME ?...BRANCH-NAMES?
604604
**
605605
** Adds or cancels the "closed" tag to one or more branches.
606606
** It accepts arbitrary unambiguous symbolic names but
607607
** will only resolve check-in names and skips any which resolve
608608
** to non-leaf check-ins.
@@ -612,26 +612,26 @@
612612
** to stdout
613613
** -v|--verbose Output more information
614614
** --date-override DATE DATE to use instead of 'now'
615615
** --user-override USER USER to use instead of the current default
616616
**
617
-** > fossil branch current
617
+** > fossil branch current
618618
**
619619
** Print the name of the branch for the current check-out
620620
**
621
-** > fossil branch hide|unhide ?OPTIONS? BRANCH-NAME ?...BRANCH-NAMES?
621
+** > fossil branch hide|unhide ?OPTIONS? BRANCH-NAME ?...BRANCH-NAMES?
622622
**
623623
** Adds or cancels the "hidden" tag for the specified branches or
624624
** or check-in IDs. Accepts the same options as the close
625625
** subcommand.
626626
**
627
-** > fossil branch info BRANCH-NAME
627
+** > fossil branch info BRANCH-NAME
628628
**
629629
** Print information about a branch
630630
**
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?
633633
**
634634
** List all branches.
635635
**
636636
** Options:
637637
** -a|--all List all branches. Default show only open branches
@@ -653,11 +653,11 @@
653653
** The "lsh" variant of this subcommand shows recently changed branches,
654654
** and accepts an optional LIMIT argument (defaults to 5) to cap output,
655655
** but no GLOB argument. All other options are supported, with -t being
656656
** an implied no-op.
657657
**
658
-** > fossil branch new BRANCH-NAME BASIS ?OPTIONS?
658
+** > fossil branch new BRANCH-NAME BASIS ?OPTIONS?
659659
**
660660
** Create a new branch BRANCH-NAME off of check-in BASIS.
661661
**
662662
** Options:
663663
** --private Branch is private (i.e., remains local)
@@ -870,11 +870,12 @@
870870
const char *zLastCkin = db_column_text(&q, 5);
871871
const char *zBgClr = db_column_text(&q, 6);
872872
char *zAge = human_readable_age(rNow - rMtime);
873873
sqlite3_int64 iMtime = (sqlite3_int64)(rMtime*86400.0);
874874
if( zMergeTo && zMergeTo[0]==0 ) zMergeTo = 0;
875
- if( zBgClr == 0 ){
875
+ if( zBgClr ) zBgClr = reasonable_bg_color(zBgClr, 0);
876
+ if( zBgClr==0 ){
876877
if( zBranch==0 || strcmp(zBranch,"trunk")==0 ){
877878
zBgClr = 0;
878879
}else{
879880
zBgClr = hash_color(zBranch);
880881
}
881882
--- 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
--- src/builtin.c
+++ src/builtin.c
@@ -52,17 +52,33 @@
5252
return -1;
5353
}
5454
5555
/*
5656
** 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.
5763
*/
5864
const unsigned char *builtin_file(const char *zFilename, int *piSize){
5965
int i = builtin_file_index(zFilename);
6066
if( i>=0 ){
6167
if( piSize ) *piSize = aBuiltinFiles[i].nByte;
6268
return aBuiltinFiles[i].pData;
6369
}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
+ }
6480
if( piSize ) *piSize = 0;
6581
return 0;
6682
}
6783
}
6884
const char *builtin_text(const char *zFilename){
6985
--- 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 @@
255255
void cache_initialize(void){
256256
sqlite3_close(cacheOpen(1));
257257
}
258258
259259
/*
260
-** COMMAND: cache*
260
+** COMMAND: cache* abbrv-subcom
261261
**
262262
** Usage: %fossil cache SUBCOMMAND
263263
**
264264
** Manage the cache used for potentially expensive web pages such as
265265
** /zip and /tarball. SUBCOMMAND can be:
266266
--- 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 @@
688688
}
689689
690690
691691
/*
692692
** 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.
693697
*/
694
-int cgi_same_origin(void){
698
+int cgi_same_origin(int bErrorLog){
695699
const char *zRef;
700
+ char *zToFree = 0;
696701
int nBase;
702
+ int rc;
697703
if( g.zBaseURL==0 ) return 0;
698704
zRef = P("HTTP_REFERER");
699705
if( zRef==0 ) return 0;
706
+ if( strchr(zRef,'%')!=0 ){
707
+ zToFree = strdup(zRef);
708
+ dehttpize(zToFree);
709
+ zRef = zToFree;
710
+ }
700711
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;
704724
}
705725
706726
/*
707727
** Return true if the current CGI request is a POST request
708728
*/
@@ -733,11 +753,11 @@
733753
** 3: (2) plus there is a valid "csrf" token in the request
734754
*/
735755
int cgi_csrf_safe(int securityLevel){
736756
if( g.okCsrf<0 ) return 0;
737757
if( g.okCsrf==0 ){
738
- if( !cgi_same_origin() ){
758
+ if( !cgi_same_origin(1) ){
739759
g.okCsrf = -1;
740760
}else{
741761
g.okCsrf = 1;
742762
if( cgi_is_post_request() ){
743763
g.okCsrf = 2;
744764
--- 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 @@
14671467
CheckinInfo *p,
14681468
int parent_rid,
14691469
int dryRunFlag
14701470
){
14711471
Blob prompt;
1472
+ int wikiFlags;
14721473
#if defined(_WIN32) || defined(__CYGWIN__)
14731474
int bomSize;
14741475
const unsigned char *bom = get_utf8_bom(&bomSize);
14751476
blob_init(&prompt, (const char *) bom, bomSize);
14761477
if( zInit && zInit[0]){
@@ -1479,14 +1480,37 @@
14791480
#else
14801481
blob_init(&prompt, zInit, -1);
14811482
#endif
14821483
blob_append(&prompt,
14831484
"\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
14871488
);
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
+
14881512
if( dryRunFlag ){
14891513
blob_appendf(&prompt, "# DRY-RUN: This is a test commit. No changes "
14901514
"will be made to the repository\n#\n");
14911515
}
14921516
blob_appendf(&prompt, "# user: %s\n",
@@ -2282,41 +2306,112 @@
22822306
char **pA = (char**)a;
22832307
char **pB = (char**)b;
22842308
return fossil_strcmp(pA[0], pB[0]);
22852309
}
22862310
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
+
22872391
/*
22882392
** COMMAND: ci#
22892393
** COMMAND: commit
22902394
**
22912395
** Usage: %fossil commit ?OPTIONS? ?FILE...?
22922396
** or: %fossil ci ?OPTIONS? ?FILE...?
22932397
**
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.
23012402
**
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.
23042410
**
23052411
** 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.
23182413
**
23192414
** A check-in is not permitted to fork unless the --allow-fork option
23202415
** appears. An empty check-in (i.e. with nothing changed) is not
23212416
** allowed unless the --allow-empty option appears. A check-in may not
23222417
** be older than its ancestor unless the --allow-older option appears.
@@ -2328,21 +2423,16 @@
23282423
** unless the interactive user chooses to proceed. If there is no
23292424
** interactive user or these warnings should be skipped for some other
23302425
** reason, the --no-warnings option may be used. A check-in is not
23312426
** allowed against a closed leaf.
23322427
**
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
-**
23372428
** The --private option creates a private check-in that is never synced.
23382429
** Children of private check-ins are automatically private.
23392430
**
23402431
** 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 ..."
23442434
**
23452435
** Options:
23462436
** --allow-conflict Allow unresolved merge conflicts
23472437
** --allow-empty Allow a commit with no changes
23482438
** --allow-fork Allow the commit to fork
@@ -2350,45 +2440,45 @@
23502440
** --baseline Use a baseline manifest in the commit process
23512441
** --bgcolor COLOR Apply COLOR to this one check-in only
23522442
** --branch NEW-BRANCH-NAME Check in to this new branch
23532443
** --branchcolor COLOR Apply given COLOR to the branch
23542444
** --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.
23562448
** --delta Use a delta manifest in the commit process
23572449
** --hash Verify file status using hashing rather
2358
-** than relying on file mtimes
2450
+** than relying on filesystem mtimes
23592451
** --if-changes Make this command a silent no-op if there
23602452
** are no changes
23612453
** --ignore-clock-skew If a clock skew is detected, ignore it and
23622454
** behave as if the user had entered 'yes' to
23632455
** the question of whether to proceed despite
23642456
** the skew.
23652457
** --ignore-oversize Do not warn the user about oversized files
23662458
** --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.
23712463
** -v|--verbose Show a diff in the commit message prompt
23722464
** --no-prompt This option disables prompting the user for
23732465
** input and assumes an answer of 'No' for every
23742466
** question.
23752467
** --no-warnings Omit all warnings about file contents
23762468
** --no-verify Do not run before-commit hooks
2469
+** --no-verify-comment Do not validate the check-in comment
23772470
** --nosign Do not attempt to sign this commit with gpg
23782471
** --nosync Do not auto-sync prior to committing
23792472
** --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.
23822477
** --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.
23902480
**
23912481
** See also: [[branch]], [[changes]], [[update]], [[extras]], [[sync]]
23922482
*/
23932483
void commit_cmd(void){
23942484
int hasChanges; /* True if unsaved changes exist */
@@ -2414,10 +2504,11 @@
24142504
int allowConflict = 0; /* Allow unresolve merge conflicts */
24152505
int allowEmpty = 0; /* Allow a commit with no changes */
24162506
int onlyIfChanges = 0; /* No-op if there are no changes */
24172507
int allowFork = 0; /* Allow the commit to fork */
24182508
int allowOlder = 0; /* Allow a commit older than its ancestor */
2509
+ int noVerifyCom = 0; /* Allow suspicious check-in comments */
24192510
char *zManifestFile; /* Name of the manifest file */
24202511
int useCksum; /* True if checksums should be computed and verified */
24212512
int outputManifest; /* True to output "manifest" and "manifest.uuid" */
24222513
int dryRunFlag; /* True for a test run. Debugging only */
24232514
CheckinInfo sCiInfo; /* Information about this check-in */
@@ -2439,10 +2530,11 @@
24392530
int bRecheck = 0; /* Repeat fork and closed-branch checks*/
24402531
int bIgnoreSkew = 0; /* --ignore-clock-skew flag */
24412532
int mxSize;
24422533
char *zCurBranch = 0; /* The current branch name of checkout */
24432534
char *zNewBranch = 0; /* The branch name after update */
2535
+ int ckComFlgs; /* Flags passed to verify_comment() */
24442536
24452537
memset(&sCiInfo, 0, sizeof(sCiInfo));
24462538
url_proxy_options();
24472539
/* --sha1sum is an undocumented alias for --hash for backwards compatiblity */
24482540
useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0;
@@ -2469,24 +2561,30 @@
24692561
}
24702562
zComment = find_option("comment","m",1);
24712563
forceFlag = find_option("force", "f", 0)!=0;
24722564
allowConflict = find_option("allow-conflict",0,0)!=0;
24732565
allowEmpty = find_option("allow-empty",0,0)!=0;
2566
+ noVerifyCom = find_option("no-verify-comment",0,0)!=0;
24742567
onlyIfChanges = find_option("if-changes",0,0)!=0;
24752568
allowFork = find_option("allow-fork",0,0)!=0;
24762569
if( find_option("override-lock",0,0)!=0 ) allowFork = 1;
24772570
allowOlder = find_option("allow-older",0,0)!=0;
24782571
noPrompt = find_option("no-prompt", 0, 0)!=0;
24792572
noWarningFlag = find_option("no-warnings", 0, 0)!=0;
24802573
noVerify = find_option("no-verify",0,0)!=0;
24812574
bTrace = find_option("trace",0,0)!=0;
24822575
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
+
24852584
sCiInfo.closeFlag = find_option("close",0,0)!=0;
24862585
sCiInfo.integrateFlag = find_option("integrate",0,0)!=0;
2487
- sCiInfo.zMimetype = find_option("mimetype",0,1);
24882586
sCiInfo.verboseFlag = find_option("verbose", "v", 0)!=0;
24892587
while( (zTag = find_option("tag",0,1))!=0 ){
24902588
if( zTag[0]==0 ) continue;
24912589
sCiInfo.azTag = fossil_realloc((void*)sCiInfo.azTag,
24922590
sizeof(char*)*(nTag+2));
@@ -2498,14 +2596,20 @@
24982596
sCiInfo.zUserOvrd = find_option("user-override",0,1);
24992597
noSign = db_get_boolean("omitsign", 0)|noSign;
25002598
if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; }
25012599
useCksum = db_get_boolean("repo-cksum", 1);
25022600
bIgnoreSkew = find_option("ignore-clock-skew",0,0)!=0;
2503
- outputManifest = db_get_manifest_setting();
2601
+ outputManifest = db_get_manifest_setting(0);
25042602
mxSize = db_large_file_size();
25052603
if( find_option("ignore-oversize",0,0)!=0 ) mxSize = 0;
25062604
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
+ }
25072611
25082612
/* Get the ID of the parent manifest artifact */
25092613
vid = db_lget_int("checkout", 0);
25102614
if( vid==0 ){
25112615
useCksum = 1;
@@ -2606,33 +2710,35 @@
26062710
fossil_exit(1);
26072711
}
26082712
}
26092713
26102714
/* 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
26122718
** delta-manifest unless this repository already contains one or more
26132719
** delta-manifests, or unless the delta-manifest is explicitly requested
26142720
** by the --delta option.
26152721
**
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.
26172724
**
26182725
** 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
26202727
** --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.
26232730
*/
2624
- if( !db_get_boolean("seen-delta-manifest",0)
2731
+ if( !(forceDelta || db_get_boolean("seen-delta-manifest",0))
26252732
|| db_get_boolean("forbid-delta-manifests",0)
26262733
|| g.bAvoidDeltaManifests
26272734
){
2628
- if( !forceDelta ) forceBaseline = 1;
2735
+ forceBaseline = 1;
26292736
}
2630
-
26312737
26322738
/* Require confirmation to continue with the check-in if there is
2633
- ** clock skew
2739
+ ** clock skew. This helps to prevent timewarps.
26342740
*/
26352741
if( g.clockSkewSeen ){
26362742
if( bIgnoreSkew!=0 ){
26372743
cReply = 'y';
26382744
fossil_warning("Clock skew ignored due to --ignore-clock-skew.");
@@ -2787,35 +2893,82 @@
27872893
fossil_free(zNewBranch);
27882894
27892895
/* Always exit the loop on the second pass */
27902896
if( bRecheck ) break;
27912897
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
+ }
27922912
27932913
/* Get the check-in comment. This might involve prompting the
27942914
** user for the check-in comment, in which case we should resync
27952915
** to renew the check-in lock and repeat the checks for conflicts.
27962916
*/
27972917
if( zComment ){
27982918
blob_zero(&comment);
27992919
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
+ }
28002925
}else if( zComFile ){
28012926
blob_zero(&comment);
28022927
blob_read_from_file(&comment, zComFile, ExtFILE);
28032928
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
+ }
28042934
}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
+
28172970
db_end_transaction(0);
28182971
db_begin_transaction();
28192972
if( !g.markPrivate && vid!=0 && !allowFork && !forceFlag ){
28202973
/* Do another auto-pull, renewing the check-in lock. Then set
28212974
** bRecheck so that we loop back above to verify that the check-in
28222975
--- 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 @@
173173
*/
174174
void manifest_to_disk(int vid){
175175
char *zManFile;
176176
int flg;
177177
178
- flg = db_get_manifest_setting();
178
+ flg = db_get_manifest_setting(0);
179179
180180
if( flg & MFESTFLG_RAW ){
181181
Blob manifest = BLOB_INITIALIZER;
182182
content_get(vid, &manifest);
183183
sterilize_manifest(&manifest, CFTYPE_MANIFEST);
@@ -276,13 +276,14 @@
276276
**
277277
** The --latest flag can be used in place of VERSION to check-out the
278278
** latest version in the repository.
279279
**
280280
** 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)
283283
** --force-missing Force check-out even if content is missing
284
+** --prompt Prompt before overwriting when --force is used
284285
** --setmtime Set timestamps of all files to match their SCM-side
285286
** times (the timestamp of the last check-in which modified
286287
** them)
287288
**
288289
** See also: [[update]]
@@ -298,16 +299,21 @@
298299
int setmtimeFlag; /* --setmtime. Set mtimes on files */
299300
Blob cksum1, cksum1b, cksum2;
300301
301302
db_must_be_within_tree();
302303
db_begin_transaction();
303
- forceFlag = find_option("force","f",0)!=0;
304304
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;
306307
latestFlag = find_option("latest",0,0)!=0;
307308
promptFlag = find_option("prompt",0,0)!=0 || forceFlag==0;
308309
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
+ }
309315
310316
/* We should be done with options.. */
311317
verify_all_options();
312318
313319
if( (latestFlag!=0 && g.argc!=2) || (latestFlag==0 && g.argc!=3) ){
314320
--- 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
--- src/clone.c
+++ src/clone.c
@@ -129,10 +129,11 @@
129129
** check-out
130130
** --nocompress Omit extra delta compression
131131
** --no-open Clone only. Do not open a check-out.
132132
** --once Don't remember the URI.
133133
** --private Also clone private branches
134
+** --proxy PROXY Use the specified HTTP proxy
134135
** --save-http-password Remember the HTTP password without asking
135136
** -c|--ssh-command SSH Use SSH as the "ssh" command
136137
** --ssl-identity FILENAME Use the SSL identity if requested by the server
137138
** --transport-command CMD Use CMD to move messages to the server and back
138139
** -u|--unversioned Also sync unversioned content
139140
--- 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 @@
2020
**
2121
*/
2222
#include "config.h"
2323
#include <string.h>
2424
#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
+}
25277
26278
/*
27279
** Compute a hash on a branch or user name
28280
*/
29281
static unsigned int hash_of_name(const char *z){
@@ -185,5 +437,86 @@
185437
@ <input type="submit" value="Submit">
186438
@ <input type="submit" name="rand" value="Random">
187439
@ </form>
188440
style_finish_page();
189441
}
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
+}
190523
--- 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 @@
2121
#include "config.h"
2222
#include "comformat.h"
2323
#include <assert.h>
2424
2525
#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
+*/
2834
#define COMMENT_PRINT_TRIM_CRLF ((u32)0x00000002) /* Trim leading CR/LF. */
2935
#define COMMENT_PRINT_TRIM_SPACE ((u32)0x00000004) /* Trim leading/trailing. */
3036
#define COMMENT_PRINT_WORD_BREAK ((u32)0x00000008) /* Break lines on words. */
3137
#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. */
3438
#endif
3539
3640
/********* Code copied from SQLite src/shell.c.in on 2024-09-30 **********/
3741
/* Lookup table to estimate the number of columns consumed by a Unicode
3842
** character.
@@ -220,10 +224,11 @@
220224
int maxChars, /* [in] Optimization hint to abort before space found. */
221225
int *sumWidth /* [out] Summated width of all characters to next space. */
222226
){
223227
int cchUTF8, utf32, wcwidth = 0;
224228
int nextIndex = index;
229
+ if( zLine[index]==0 ) return index;
225230
for(;;){
226231
char_info_utf8(&zLine[nextIndex],&cchUTF8,&utf32);
227232
nextIndex += cchUTF8;
228233
wcwidth += cli_wcwidth(utf32);
229234
if( zLine[nextIndex]==0 || fossil_isspace(zLine[nextIndex]) ||
@@ -234,29 +239,46 @@
234239
}
235240
return 0; /* NOT REACHED */
236241
}
237242
238243
/*
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.
248260
*/
249261
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) */
253265
){
254266
int i = 0; /* Counted bytes. */
255267
int cchUTF8 = 1; /* Code units consumed. */
256268
int maxUTF8 = 1; /* Expected sequence length. */
257269
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
+ }
258280
if( (c&0x80)==0x00 ){ /* 7-bit ASCII character. */
259281
*pCchUTF8 = 1;
260282
*pUtf32 = (int)z[0];
261283
return;
262284
}
@@ -465,21 +487,29 @@
465487
*pzLine = zLine + index;
466488
}
467489
}
468490
469491
/*
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.
472495
**
473496
** 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
475498
** and that a single line can contain no more than 'width' characters.
476499
** Indent all subsequent lines by 'indent'.
477500
**
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
+**
478508
** Returns the number of new lines emitted.
479509
*/
480
-static int comment_print_legacy(
510
+static int comment_print_canonical(
481511
const char *zText, /* The comment text to be printed. */
482512
int indent, /* Number of spaces to indent each non-initial line. */
483513
int width /* Maximum number of characters per line. */
484514
){
485515
int maxChars = width - indent;
@@ -564,13 +594,18 @@
564594
** This is the comment printing function. The comment printing algorithm
565595
** contained within it attempts to preserve the formatting present within
566596
** the comment string itself while honoring line width limitations. There
567597
** are several flags that modify the default behavior of this function:
568598
**
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.
572607
**
573608
** COMMENT_PRINT_TRIM_CRLF: Trims leading and trailing carriage-returns
574609
** and line-feeds where they do not materially
575610
** impact pre-existing formatting (i.e. at the
576611
** start of the comment string -AND- right
@@ -613,56 +648,61 @@
613648
int indent, /* Spaces to indent each non-initial line. */
614649
int width, /* Maximum number of characters per line. */
615650
int flags /* Zero or more "COMMENT_PRINT_*" flags. */
616651
){
617652
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
+ }
654695
}
655696
656697
/*
657698
** Return the "COMMENT_PRINT_*" flags specified by the following sources,
658699
** evaluated in the following cascading order:
659700
**
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.
664704
*/
665705
int get_comment_format(){
666706
int comFmtFlags;
667707
668708
/* We must cache this result, else running the timeline can end up
@@ -689,54 +729,70 @@
689729
690730
/*
691731
**
692732
** COMMAND: test-comment-format
693733
**
694
-** Usage: %fossil test-comment-format ?OPTIONS? PREFIX TEXT ?ORIGTEXT?
734
+** Usage: %fossil test-comment-format [OPTIONS] TEXT [PREFIX] [ORIGTEXT]
695735
**
696736
** Test comment formatting and printing. Use for testing only.
697737
**
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
+**
698748
** 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.
713764
*/
714765
void test_comment_format(void){
715766
const char *zWidth;
716767
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;
720771
int indent, width;
721
- int fromFile = find_option("file", 0, 0)!=0;
772
+ int i;
773
+ const char *fromFile = find_option("file", 0, 1);
722774
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;
726779
}
727780
if( find_option("trimcrlf", 0, 0) ){
728
- flags |= COMMENT_PRINT_TRIM_CRLF;
781
+ flags = COMMENT_PRINT_TRIM_CRLF;
729782
}
730783
if( find_option("trimspace", 0, 0) ){
731784
flags |= COMMENT_PRINT_TRIM_SPACE;
785
+ flags &= COMMENT_PRINT_CANONICAL;
732786
}
733787
if( find_option("wordbreak", 0, 0) ){
734788
flags |= COMMENT_PRINT_WORD_BREAK;
789
+ flags &= COMMENT_PRINT_CANONICAL;
735790
}
736791
if( find_option("origbreak", 0, 0) ){
737792
flags |= COMMENT_PRINT_ORIG_BREAK;
793
+ flags &= COMMENT_PRINT_CANONICAL;
738794
}
739795
zWidth = find_option("width","W",1);
740796
if( zWidth ){
741797
width = atoi(zWidth);
742798
}else{
@@ -747,45 +803,51 @@
747803
indent = atoi(zIndent);
748804
}else{
749805
indent = -1; /* automatic */
750806
}
751807
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;
762809
if( fromFile ){
763810
Blob fileData;
764
- blob_read_from_file(&fileData, zText, ExtFILE);
811
+ blob_read_from_file(&fileData, fromFile, ExtFILE);
765812
zText = mprintf("%s", blob_str(&fileData));
766813
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;
771833
}
834
+ usage("[OPTIONS] TEXT [PREFIX] [ORIGTEXT]");
772835
}
773836
if( decode ){
774837
zText = mprintf(fromFile?"%z":"%s" /*works-like:"%s"*/, zText);
775838
defossilize(zText);
776839
if( zOrigText ){
777840
zOrigText = mprintf(fromFile?"%z":"%s" /*works-like:"%s"*/, zOrigText);
778841
defossilize(zOrigText);
779842
}
780843
}
844
+ if( zPrefix==0 ) zPrefix = "00:00:00 ";
781845
if( indent<0 ){
782846
indent = strlen(zPrefix);
783847
}
784848
if( zPrefix && *zPrefix ){
785849
fossil_print("%s", zPrefix);
786850
}
787851
fossil_print("(%d lines output)\n",
788852
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);
791853
}
792854
--- 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 @@
100100
{ "logo-image", CONFIGSET_SKIN },
101101
{ "background-mimetype", CONFIGSET_SKIN },
102102
{ "background-image", CONFIGSET_SKIN },
103103
{ "icon-mimetype", CONFIGSET_SKIN },
104104
{ "icon-image", CONFIGSET_SKIN },
105
- { "timeline-block-markup", CONFIGSET_SKIN },
106105
{ "timeline-date-format", CONFIGSET_SKIN },
107106
{ "timeline-default-style", CONFIGSET_SKIN },
108107
{ "timeline-dwelltime", CONFIGSET_SKIN },
109108
{ "timeline-closetime", CONFIGSET_SKIN },
110109
{ "timeline-hard-newlines", CONFIGSET_SKIN },
@@ -817,10 +816,12 @@
817816
**
818817
** Synchronize configuration changes in the local repository with
819818
** the remote repository at URL.
820819
**
821820
** Options:
821
+** --proxy PROXY Use PROXY as http proxy during sync operation
822
+** (used by pull, push and sync subcommands)
822823
** -R|--repository REPO Affect repository REPO with changes
823824
**
824825
** See also: [[settings]], [[unset]]
825826
*/
826827
void configuration_cmd(void){
@@ -889,10 +890,11 @@
889890
}
890891
url_parse(zServer, URL_PROMPT_PW|URL_USE_CONFIG);
891892
if( g.url.protocol==0 ) fossil_fatal("no server URL specified");
892893
user_select();
893894
url_enable_proxy("via proxy: ");
895
+ g.zHttpAuth = get_httpauth();
894896
if( overwriteFlag ) mask |= CONFIGSET_OVERWRITE;
895897
if( strncmp(zMethod, "push", n)==0 ){
896898
client_sync(0,0,(unsigned)mask,0,0);
897899
}else if( strncmp(zMethod, "pull", n)==0 ){
898900
if( overwriteFlag ) db_unprotect(PROTECT_USER);
899901
--- 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
+207 -85
--- src/db.c
+++ src/db.c
@@ -1629,10 +1629,12 @@
16291629
sqlite3_create_function(db, "chat_msg_from_event", 4,
16301630
SQLITE_UTF8 | SQLITE_INNOCUOUS, 0,
16311631
chat_msg_from_event, 0, 0);
16321632
sqlite3_create_function(db, "inode", 1, SQLITE_UTF8, 0,
16331633
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);
16341636
16351637
}
16361638
16371639
#if USE_SEE
16381640
/*
@@ -3204,23 +3206,16 @@
32043206
32053207
db_unprotect(PROTECT_ALL);
32063208
db_set("content-schema", CONTENT_SCHEMA, 0);
32073209
db_set("aux-schema", AUX_SCHEMA_MAX, 0);
32083210
db_set("rebuilt", get_version(), 0);
3209
- db_set("admin-log", "1", 0);
3210
- db_set("access-log", "1", 0);
32113211
db_multi_exec(
32123212
"INSERT INTO config(name,value,mtime)"
32133213
" VALUES('server-code', lower(hex(randomblob(20))),now());"
32143214
"INSERT INTO config(name,value,mtime)"
32153215
" VALUES('project-code', lower(hex(randomblob(20))),now());"
32163216
);
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
- }
32223217
db_create_default_users(0, zDefaultUser);
32233218
if( zDefaultUser ) g.zLogin = zDefaultUser;
32243219
user_select();
32253220
32263221
if( zTemplate ){
@@ -3647,66 +3642,79 @@
36473642
**
36483643
** If the zNonVersionedSetting parameter is not NULL then it holds the
36493644
** non-versioned value for this setting. If both a versioned and a
36503645
** non-versioned value exist and are not equal, then a warning message
36513646
** 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.
36523654
*/
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
+){
36543660
char *zVersionedSetting = 0;
36553661
int noWarn = 0;
36563662
int found = 0;
36573663
struct _cacheEntry {
36583664
struct _cacheEntry *next;
36593665
const char *zName, *zValue;
36603666
} *cacheEntry = 0;
36613667
static struct _cacheEntry *cache = 0;
36623668
3663
- if( !g.localOpen && g.zOpenRevision==0 ) return zNonVersionedSetting;
3669
+ if( !g.localOpen && g.zOpenRevision==0 && zCkin==0 ){
3670
+ return zNonVersionedSetting;
3671
+ }
3672
+
36643673
/* 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
+
36733685
/* Attempt to read value from file in check-out if there wasn't a cache hit.*/
36743686
if( cacheEntry==0 ){
36753687
Blob versionedPathname;
36763688
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
+ }
37083716
}
37093717
}
37103718
blob_reset(&versionedPathname);
37113719
if( found ){
37123720
blob_strip_comment_lines(&setting, &setting);
@@ -3713,20 +3721,27 @@
37133721
blob_trim(&setting); /* Avoid non-obvious problems with line endings
37143722
** on boolean properties */
37153723
zVersionedSetting = fossil_strdup(blob_str(&setting));
37163724
}
37173725
blob_reset(&setting);
3726
+
37183727
/* 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
+ }
37243735
}
3736
+
37253737
/* 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
37283743
){
37293744
/* There's a versioned setting, and a non-versioned setting. Tell
37303745
** the user about the conflict */
37313746
fossil_warning(
37323747
"setting %s has both versioned and non-versioned values: using "
@@ -3735,10 +3750,11 @@
37353750
"\"%/.fossil-settings/%s.no-warn\" in the check-out root, or delete "
37363751
"the non-versioned setting with \"fossil unset %s\")", zName,
37373752
g.zLocalRoot, zName, g.zLocalRoot, zName, zName
37383753
);
37393754
}
3755
+
37403756
/* Prefer the versioned setting */
37413757
return ( zVersionedSetting!=0 ) ? zVersionedSetting : zNonVersionedSetting;
37423758
}
37433759
37443760
@@ -3778,11 +3794,11 @@
37783794
}
37793795
if( pSetting!=0 && pSetting->versionable ){
37803796
/* This is a versionable setting, try and get the info from a
37813797
** checked-out file */
37823798
char * zZ = z;
3783
- z = db_get_versioned(zName, z);
3799
+ z = db_get_versioned(zName, z, 0);
37843800
if(zZ != z){
37853801
fossil_free(zZ);
37863802
}
37873803
}
37883804
if( z==0 ){
@@ -3919,11 +3935,11 @@
39193935
}
39203936
fossil_free(zVal);
39213937
return dflt;
39223938
}
39233939
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);
39253941
if( zVal==0 ) return dflt;
39263942
if( is_truth(zVal) ) return 1;
39273943
if( is_false(zVal) ) return 0;
39283944
return dflt;
39293945
}
@@ -4048,14 +4064,30 @@
40484064
** Get the manifest setting. For backwards compatibility first check if the
40494065
** value is a boolean. If it's not a boolean, treat each character as a flag
40504066
** to enable a manifest type. This system puts certain boundary conditions on
40514067
** which letters can be used to represent flags (any permutation of flags must
40524068
** 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.
40534077
*/
4054
-int db_get_manifest_setting(void){
4078
+int db_get_manifest_setting(const char *zCkin){
40554079
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
+ }
40574089
if( zVal==0 || is_false(zVal) ){
40584090
return 0;
40594091
}else if( is_truth(zVal) ){
40604092
return MFESTFLG_RAW|MFESTFLG_UUID;
40614093
}
@@ -4068,10 +4100,37 @@
40684100
}
40694101
zVal++;
40704102
}
40714103
return flg;
40724104
}
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
+}
40734132
40744133
40754134
/*
40764135
** Record the name of a local repository in the global_config() database.
40774136
** The repository filename %s is recorded as an entry with a "name" field
@@ -4181,10 +4240,11 @@
41814240
** --force-missing Force opening a repository with missing content
41824241
** -k|--keep Only modify the manifest file(s)
41834242
** --nested Allow opening a repository inside an opened check-out
41844243
** --nosync Do not auto-sync the repository prior to opening even
41854244
** if the autosync setting is on.
4245
+** --proxy PROXY Use PROXY as http proxy during sync operation
41864246
** --repodir DIR If REPOSITORY is a URI that will be cloned, store
41874247
** the clone in DIR rather than in "."
41884248
** --setmtime Set timestamps of all files to match their SCM-side
41894249
** times (the timestamp of the last check-in which modified
41904250
** them).
@@ -4370,16 +4430,37 @@
43704430
}
43714431
}
43724432
g.argc = 2;
43734433
info_cmd();
43744434
}
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
+}
43754451
43764452
/*
43774453
** Print the current value of a setting identified by the pSetting
43784454
** 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.
43794460
*/
4380
-void print_setting(const Setting *pSetting, int valueOnly){
4461
+void print_setting(const Setting *pSetting, int valueOnly, int bIfChng){
43814462
Stmt q;
43824463
int versioned = 0;
43834464
if( pSetting->versionable && g.localOpen ){
43844465
/* Check to see if this is overridden by a versionable settings file */
43854466
Blob versionedPathname;
@@ -4390,11 +4471,16 @@
43904471
versioned = 1;
43914472
}
43924473
blob_reset(&versionedPathname);
43934474
}
43944475
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
+ }
43964482
return;
43974483
}
43984484
if( g.repositoryOpen ){
43994485
db_prepare(&q,
44004486
"SELECT '(local)', value FROM config WHERE name=%Q"
@@ -4407,20 +4493,47 @@
44074493
"SELECT '(global)', value FROM global_config WHERE name=%Q",
44084494
pSetting->name
44094495
);
44104496
}
44114497
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 ){
44134505
fossil_print("%s\n", db_column_text(&q, 1));
44144506
}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
+ }
44174527
}
4528
+ }else if( bIfChng ){
4529
+ /* Display nothing */
4530
+ versioned = 0;
44184531
}else if( valueOnly ){
44194532
fossil_print("\n");
44204533
}else{
4421
- fossil_print("%-20s\n", pSetting->name);
4534
+ fossil_print("%-24s\n", pSetting->name);
44224535
}
44234536
if( versioned ){
44244537
fossil_print(" (overridden by contents of file .fossil-settings/%s)\n",
44254538
pSetting->name);
44264539
}
@@ -4453,21 +4566,22 @@
44534566
char versionable; /* Is this setting versionable? */
44544567
char forceTextArea; /* Force using a text area for display? */
44554568
char sensitive; /* True if this a security-sensitive setting */
44564569
const char *def; /* Default value */
44574570
};
4571
+
44584572
#endif /* INTERFACE */
44594573
44604574
/*
4461
-** SETTING: access-log boolean default=off
4575
+** SETTING: access-log boolean default=on
44624576
**
44634577
** When the access-log setting is enabled, all login attempts (successful
44644578
** and unsuccessful) on the web interface are recorded in the "access" table
44654579
** of the repository.
44664580
*/
44674581
/*
4468
-** SETTING: admin-log boolean default=off
4582
+** SETTING: admin-log boolean default=on
44694583
**
44704584
** When the admin-log setting is enabled, configuration changes are recorded
44714585
** in the "admin_log" table of the repository.
44724586
*/
44734587
/*
@@ -4640,30 +4754,27 @@
46404754
** When enabled, fossil will attempt to sign all commits
46414755
** with gpg or ssh. When disabled, commits will be unsigned.
46424756
*/
46434757
/*
46444758
** 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.
46494760
**
46504761
** 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.
46524767
**
46534768
** Or a bitwise combination of the following flags:
4654
-** 0 Activate the newer (non-legacy) comment printing format.
46554769
** 2 Trim leading and trailing CR and LF characters.
46564770
** 4 Trim leading and trailing white space characters.
46574771
** 8 Attempt to break lines on word boundaries.
46584772
** 16 Break lines before the original comment embedded in other text.
46594773
**
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.
46654776
*/
46664777
/*
46674778
** SETTING: crlf-glob width=40 versionable block-text
46684779
** The VALUE of this setting is a list of GLOB patterns matching files
46694780
** in which it is allowed to have CR, CR+LF or mixed line endings,
@@ -4709,10 +4820,18 @@
47094820
*/
47104821
/*
47114822
** SETTING: editor width=32 sensitive
47124823
** The value is an external command that will launch the
47134824
** 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.
47144833
*/
47154834
/*
47164835
** SETTING: empty-dirs width=40 versionable block-text
47174836
** The value is a list of pathnames parsed according to the same rules as
47184837
** the *-glob settings. On update and checkout commands, if no directory
@@ -4759,13 +4878,14 @@
47594878
** send the "pragma avoid-delta-manifests" statement in its reply,
47604879
** which will cause the client to avoid generating a delta
47614880
** manifest.
47624881
*/
47634882
/*
4764
-** SETTING: gdiff-command width=40 default=gdiff sensitive
4883
+** SETTING: gdiff-command width=40 sensitive
47654884
** 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.
47674887
*/
47684888
/*
47694889
** SETTING: gmerge-command width=40 sensitive
47704890
** The value is a graphical merge conflict resolver command operating
47714891
** on four files. Examples:
@@ -4949,11 +5069,12 @@
49495069
**
49505070
** All repositories are searched (in lexicographical order) and the first
49515071
** repository with a non-zero "repolist-skin" value is used as the skin
49525072
** for the repository list page. If none of the repositories on the list
49535073
** 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.
49555076
**
49565077
** If repolist-skin has a value of 2, then the repository is omitted from
49575078
** the list in use cases 1 through 4, but not for 5 and 6.
49585079
*/
49595080
/*
@@ -5154,20 +5275,22 @@
51545275
** configuration database. If both a local and a global value exists for a
51555276
** setting, the local value takes precedence. This command normally operates
51565277
** on the local settings. Use the --global option to change global settings.
51575278
**
51585279
** Options:
5280
+** --changed Only show settings if the value differs from the default
5281
+** --exact Only consider exact name matches
51595282
** --global Set or unset the given property globally instead of
51605283
** setting or unsetting it for the open repository only
5161
-** --exact Only consider exact name matches
51625284
** --value Only show the value of a given property (implies --exact)
51635285
**
51645286
** See also: [[configuration]]
51655287
*/
51665288
void setting_cmd(void){
51675289
int i;
51685290
int globalFlag = find_option("global","g",0)!=0;
5291
+ int bIfChng = find_option("changed",0,0)!=0;
51695292
int exactFlag = find_option("exact",0,0)!=0;
51705293
int valueFlag = find_option("value",0,0)!=0;
51715294
/* Undocumented "--test-for-subsystem SUBSYS" option used to test
51725295
** the db_get_for_subsystem() interface: */
51735296
const char *zSubsys = find_option("test-for-subsystem",0,1);
@@ -5188,16 +5311,15 @@
51885311
}
51895312
if( valueFlag ){
51905313
if( g.argc!=3 ){
51915314
fossil_fatal("--value is only supported when qurying a given property");
51925315
}
5193
- exactFlag = 1;
51945316
}
51955317
51965318
if( g.argc==2 ){
51975319
for(i=0; i<nSetting; i++){
5198
- print_setting(&aSetting[i], 0);
5320
+ print_setting(&aSetting[i], 0, bIfChng);
51995321
}
52005322
}else if( g.argc==3 || g.argc==4 ){
52015323
const char *zName = g.argv[2];
52025324
int n = (int)strlen(zName);
52035325
const Setting *pSetting = db_find_setting(zName, !exactFlag);
@@ -5248,11 +5370,11 @@
52485370
fossil_print(" [%s]", zValue);
52495371
fossil_free(zValue);
52505372
}
52515373
fossil_print("\n");
52525374
}else{
5253
- print_setting(pSetting, valueFlag);
5375
+ print_setting(pSetting, valueFlag, bIfChng);
52545376
}
52555377
pSetting++;
52565378
}
52575379
}
52585380
}else{
52595381
--- 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
--- src/default.css
+++ src/default.css
@@ -750,10 +750,11 @@
750750
body.tkt div.content ol.tkt-changes > li:target > ol {
751751
border-left: 1px solid gold;
752752
}
753753
body.cpage-ckout .file-change-line,
754754
body.cpage-info .file-change-line,
755
+body.cpage-vinfo .file-change-line,
755756
body.cpage-vdiff .file-change-line {
756757
margin-top: 16px;
757758
margin-bottom: 16px;
758759
margin-right: 1em /* keep it from nudging right up against the scrollbar-reveal zone */;
759760
display: flex;
760761
--- 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
--- src/default.css
+++ src/default.css
@@ -750,10 +750,11 @@
750750
body.tkt div.content ol.tkt-changes > li:target > ol {
751751
border-left: 1px solid gold;
752752
}
753753
body.cpage-ckout .file-change-line,
754754
body.cpage-info .file-change-line,
755
+body.cpage-vinfo .file-change-line,
755756
body.cpage-vdiff .file-change-line {
756757
margin-top: 16px;
757758
margin-bottom: 16px;
758759
margin-right: 1em /* keep it from nudging right up against the scrollbar-reveal zone */;
759760
display: flex;
760761
--- 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
--- src/descendants.c
+++ src/descendants.c
@@ -156,10 +156,27 @@
156156
" AND tagxref.tagtype>0)",
157157
TAG_CLOSED
158158
);
159159
}
160160
}
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
+
161178
162179
/*
163180
** Load the record ID rid and up to |N|-1 closest ancestors into
164181
** the "ok" table. If N is zero, no limit. If ridBackTo is not zero
165182
** then stop the search upon reaching the ancestor with rid==ridBackTo.
@@ -197,13 +214,11 @@
197214
** (3) Cherrypick merge parents.
198215
** (4) All ancestores of 1 and 2 but not of 3.
199216
*/
200217
double rLimitMtime = 0.0;
201218
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);
205220
}
206221
db_multi_exec(
207222
"WITH RECURSIVE\n"
208223
" parent(pid,cid,isCP) AS (\n"
209224
" SELECT plink.pid, plink.cid, 0 AS xisCP FROM plink\n"
210225
--- 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 @@
31753175
}
31763176
g.diffCnt[1] += nIns;
31773177
g.diffCnt[2] += nDel;
31783178
if( nIns+nDel ){
31793179
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
+ }
31813183
}
31823184
}else if( pCfg->diffFlags & (DIFF_RAW|DIFF_BY_TOKEN) ){
31833185
const int *R = c.aEdit;
31843186
unsigned int r;
31853187
for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){
@@ -3335,10 +3337,14 @@
33353337
if( zDiffBinary ){
33363338
if( is_truth(zDiffBinary) ) diffFlags |= DIFF_INCBINARY;
33373339
}else if( db_get_boolean("diff-binary", 1) ){
33383340
diffFlags |= DIFF_INCBINARY;
33393341
}
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;
33403346
}
33413347
}
33423348
if( find_option("verbose","v",0)!=0 ) diffFlags |= DIFF_VERBOSE;
33433349
/* Deprecated, but retained for script compatibility. */
33443350
else if( find_option("new-file","N",0)!=0 ) diffFlags |= DIFF_VERBOSE;
33453351
--- 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 @@
9090
set x [lindex $difftxt $ii]
9191
incr ii
9292
return $x
9393
}
9494
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} {
96105
global difftxt debug
97106
if {![info exists difftxt]} {
98107
if {$debug} {
99108
puts "# [list open $fossilcmd r]"
100109
flush stdout
101110
}
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
+ }
106125
}
107126
set N [llength $difftxt]
108127
set ii 0
109128
set nDiffs 0
110129
set n1 0
111130
set n2 0
112131
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
+ }
113141
114142
115143
set fromIndex [lsearch -glob $fossilcmd *-from]
116144
set toIndex [lsearch -glob $fossilcmd *-to]
117145
set branchIndex [lsearch -glob $fossilcmd *-branch]
@@ -459,11 +487,11 @@
459487
::ttk::scrollbar .sby -command {.txtA yview} -orient vertical
460488
::ttk::scrollbar .sbxA -command {.txtA xview} -orient horizontal
461489
::ttk::scrollbar .sbxB -command {.txtB xview} -orient horizontal
462490
frame .spacer
463491
464
-if {[readDiffs $fossilcmd] == 0} {
492
+if {[readDiffs $fossilcmd 0] == 0} {
465493
tk_messageBox -type ok -title $CFG(TITLE) -message "No changes"
466494
exit
467495
}
468496
update idletasks
469497
@@ -574,14 +602,15 @@
574602
$w tag config search -background {#fcc000}
575603
}
576604
set ::search $w
577605
}
578606
::ttk::button .bb.quit -text {Quit} -command exit
607
+::ttk::button .bb.reload -text {Reload} -command reloadDiff
579608
::ttk::button .bb.invert -text {Invert} -command invertDiff
580609
::ttk::button .bb.save -text {Save As...} -command saveDiff
581610
::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
583612
if {$fossilcmd!=""} {pack .bb.save -side left}
584613
pack .bb.files .bb.search -side left
585614
grid rowconfigure . 1 -weight 1
586615
grid columnconfigure . 1 -weight 1
587616
grid columnconfigure . 4 -weight 1
588617
--- 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 @@
127127
DIFF_HTML|DIFF_WEBPAGE|DIFF_BROWSER|DIFF_JSON|DIFF_TCL))==0 ){
128128
fossil_print("Fossil-Diff-From: %s\n",
129129
zFrom[0]=='(' ? zFrom : mprintf("%S %s",
130130
rid_to_uuid(symbolic_name_to_rid(zFrom, "ci")),
131131
db_text("","SELECT datetime(%f)||' UTC'",
132
- symbolic_name_to_mtime(zFrom, 0))));
132
+ symbolic_name_to_mtime(zFrom, 0, 0))));
133133
fossil_print("Fossil-Diff-To: %s\n",
134134
zTo[0]=='(' ? zTo : mprintf("%S %s",
135135
rid_to_uuid(symbolic_name_to_rid(zTo, "ci")),
136136
db_text("","SELECT datetime(%f)||' UTC'",
137
- symbolic_name_to_mtime(zTo, 0))));
137
+ symbolic_name_to_mtime(zTo, 0, 1))));
138138
fossil_print("%.66c\n", '-');
139139
}
140140
}
141141
142142
/*
@@ -606,20 +606,22 @@
606606
blob_read_from_file(&file2, zFile2, ExtFILE);
607607
zName2 = zName;
608608
}
609609
610610
/* Compute and output the differences */
611
- if( pCfg->diffFlags & DIFF_BRIEF ){
611
+ if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){
612612
if( blob_compare(pFile1, &file2) ){
613613
fossil_print("CHANGED %s\n", zName);
614614
}
615615
}else{
616616
blob_zero(&out);
617617
text_diff(pFile1, &file2, &out, pCfg);
618618
if( blob_size(&out) ){
619619
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
+ }
621623
}else{
622624
diff_print_filenames(zName, zName2, pCfg, pOut);
623625
blob_appendf(pOut, "%s\n", blob_str(&out));
624626
}
625627
}
@@ -684,11 +686,16 @@
684686
blob_append_escaped_arg(&cmd, blob_str(&nameFile1), 1);
685687
blob_append_escaped_arg(&cmd, zFile2, 1);
686688
}
687689
688690
/* 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
+ }
690697
691698
/* Delete the temporary file and clean up memory used */
692699
if( useTempfile ) file_delete(blob_str(&nameFile1));
693700
blob_reset(&nameFile1);
694701
blob_reset(&cmd);
@@ -712,18 +719,22 @@
712719
Blob *pFile1, /* In memory content to compare from */
713720
Blob *pFile2, /* In memory content to compare to */
714721
const char *zName, /* Display name of the file */
715722
DiffConfig *pCfg /* Diff flags */
716723
){
717
- if( pCfg->diffFlags & DIFF_BRIEF ) return;
724
+ if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){
725
+ return;
726
+ }
718727
if( pCfg->zDiffCmd==0 ){
719728
Blob out; /* Diff output text */
720729
721730
blob_zero(&out);
722731
text_diff(pFile1, pFile2, &out, pCfg);
723732
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
+ }
725736
}else{
726737
diff_print_filenames(zName, zName, pCfg, 0);
727738
fossil_print("%s\n", blob_str(&out));
728739
}
729740
@@ -1009,11 +1020,13 @@
10091020
}else if( pTo ){
10101021
zName = pTo->zName;
10111022
}else{
10121023
zName = DIFF_NO_NAME;
10131024
}
1014
- if( pCfg->diffFlags & DIFF_BRIEF ) return;
1025
+ if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){
1026
+ return;
1027
+ }
10151028
diff_print_index(zName, pCfg, 0);
10161029
if( pFrom ){
10171030
rid = uuid_to_rid(pFrom->zUuid, 0);
10181031
content_get(rid, &f1);
10191032
}else{
@@ -1097,11 +1110,11 @@
10971110
(void)file_dir_match(pFileDir, pFromFile->zName); /* Record name usage */
10981111
pFromFile = manifest_file_next(pFrom,0);
10991112
pToFile = manifest_file_next(pTo,0);
11001113
}else{
11011114
if( file_dir_match(pFileDir, pToFile->zName) ){
1102
- if( pCfg->diffFlags & DIFF_BRIEF ){
1115
+ if((pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT)){
11031116
fossil_print("CHANGED %s\n", pFromFile->zName);
11041117
}else{
11051118
diff_manifest_entry(pFromFile, pToFile, pCfg);
11061119
}
11071120
}
@@ -1173,25 +1186,34 @@
11731186
/*
11741187
** Return the name of the external diff command, or return NULL if
11751188
** no external diff command is defined.
11761189
*/
11771190
const char *diff_command_external(int guiDiff){
1178
- const char *zDefault;
11791191
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;
11931215
}
11941216
11951217
/*
11961218
** Show diff output in a Tcl/Tk window, in response to the --tk option
11971219
** to the diff command.
@@ -1209,10 +1231,11 @@
12091231
const char *zTempFile = 0;
12101232
char *zCmd;
12111233
const char *zTclsh;
12121234
int bDebug = find_option("tkdebug",0,0)!=0;
12131235
int bDarkMode = find_option("dark",0,0)!=0;
1236
+ (void)find_option("debug",0,0);
12141237
blob_zero(&script);
12151238
/* Caution: When this routine is called from the merge-info command,
12161239
** the --tcl argument requires an argument. But merge-info does not
12171240
** use -i, so we can take -i as that argument. This routine needs to
12181241
** always have -i after --tcl.
@@ -1290,11 +1313,12 @@
12901313
** Usage: %fossil diff|gdiff ?OPTIONS? ?FILE1? ?FILE2 ...?
12911314
**
12921315
** Show the difference between the current version of each of the FILEs
12931316
** specified (as they exist on disk) and that same file as it was checked-
12941317
** 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.
12961320
**
12971321
** The default output format is a "unified patch" (the same as the
12981322
** output of "diff -u" on most unix systems). Many alternative formats
12991323
** are available. A few of the more useful alternatives:
13001324
**
@@ -1356,11 +1380,13 @@
13561380
** -i|--internal Use internal diff logic
13571381
** --invert Invert the diff
13581382
** --json Output formatted as JSON
13591383
** -n|--linenum Show line numbers
13601384
** -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.
13621388
** -y|--side-by-side Side-by-side diff
13631389
** --strip-trailing-cr Strip trailing CR
13641390
** --tcl Tcl-formatted output used internally by --tk
13651391
** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh")
13661392
** --tk Launch a Tcl/Tk GUI for display
@@ -1382,15 +1408,15 @@
13821408
int againstUndo = 0; /* Diff against files in the undo buffer */
13831409
FileDirList *pFileDir = 0; /* Restrict the diff to these files */
13841410
DiffConfig DCfg; /* Diff configuration object */
13851411
int bFromIsDir = 0; /* True if zFrom is a directory name */
13861412
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") ){
13881415
diff_tk("diff", 2);
13891416
return;
13901417
}
1391
- isGDiff = g.argv[1][0]=='g';
13921418
zFrom = find_option("from", "r", 1);
13931419
zTo = find_option("to", 0, 1);
13941420
zCheckin = find_option("checkin", "ci", 1);
13951421
zBranch = find_option("branch", 0, 1);
13961422
againstUndo = find_option("undo",0,0)!=0;
@@ -1402,10 +1428,11 @@
14021428
if( zTo || zFrom || zCheckin ){
14031429
fossil_fatal("cannot use --from, --to, or --checkin with --branch");
14041430
}
14051431
zTo = zBranch;
14061432
zFrom = mprintf("root:%s", zBranch);
1433
+ zBranch = 0;
14071434
}
14081435
if( zCheckin!=0 && (zFrom!=0 || zTo!=0) ){
14091436
fossil_fatal("cannot use --checkin together with --from or --to");
14101437
}
14111438
if( 0==zCheckin ){
@@ -1416,10 +1443,19 @@
14161443
}else{
14171444
db_find_and_open_repository(0, 0);
14181445
}
14191446
}else{
14201447
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;
14211457
}
14221458
determine_exec_relative_option(1);
14231459
if( zFrom!=file_tail(zFrom)
14241460
&& file_isdir(zFrom, ExtFILE)==1
14251461
&& !db_exists("SELECT 1 FROM tag WHERE tagname='sym-%q'", zFrom)
@@ -1448,11 +1484,14 @@
14481484
pFileDir[i-2].nName = blob_size(&fname);
14491485
pFileDir[i-2].nUsed = 0;
14501486
blob_reset(&fname);
14511487
}
14521488
}
1453
- if ( zCheckin!=0 ){
1489
+ if( DCfg.diffFlags & DIFF_NUMSTAT ){
1490
+ fossil_print("%10s %10s\n", "INSERTED", "DELETED");
1491
+ }
1492
+ if( zCheckin!=0 ){
14541493
int ridTo = name_to_typed_rid(zCheckin, "ci");
14551494
zTo = zCheckin;
14561495
zFrom = db_text(0,
14571496
"SELECT uuid FROM blob, plink"
14581497
" WHERE plink.cid=%d AND plink.isprim AND plink.pid=blob.rid",
@@ -1488,12 +1527,12 @@
14881527
}
14891528
fossil_free(pFileDir);
14901529
}
14911530
diff_end(&DCfg, 0);
14921531
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": "");
14951534
}
14961535
}
14971536
14981537
/*
14991538
** WEBPAGE: vpatch
15001539
--- 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 @@
127127
DIFF_HTML|DIFF_WEBPAGE|DIFF_BROWSER|DIFF_JSON|DIFF_TCL))==0 ){
128128
fossil_print("Fossil-Diff-From: %s\n",
129129
zFrom[0]=='(' ? zFrom : mprintf("%S %s",
130130
rid_to_uuid(symbolic_name_to_rid(zFrom, "ci")),
131131
db_text("","SELECT datetime(%f)||' UTC'",
132
- symbolic_name_to_mtime(zFrom, 0))));
132
+ symbolic_name_to_mtime(zFrom, 0, 0))));
133133
fossil_print("Fossil-Diff-To: %s\n",
134134
zTo[0]=='(' ? zTo : mprintf("%S %s",
135135
rid_to_uuid(symbolic_name_to_rid(zTo, "ci")),
136136
db_text("","SELECT datetime(%f)||' UTC'",
137
- symbolic_name_to_mtime(zTo, 0))));
137
+ symbolic_name_to_mtime(zTo, 0, 1))));
138138
fossil_print("%.66c\n", '-');
139139
}
140140
}
141141
142142
/*
@@ -606,20 +606,22 @@
606606
blob_read_from_file(&file2, zFile2, ExtFILE);
607607
zName2 = zName;
608608
}
609609
610610
/* Compute and output the differences */
611
- if( pCfg->diffFlags & DIFF_BRIEF ){
611
+ if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){
612612
if( blob_compare(pFile1, &file2) ){
613613
fossil_print("CHANGED %s\n", zName);
614614
}
615615
}else{
616616
blob_zero(&out);
617617
text_diff(pFile1, &file2, &out, pCfg);
618618
if( blob_size(&out) ){
619619
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
+ }
621623
}else{
622624
diff_print_filenames(zName, zName2, pCfg, pOut);
623625
blob_appendf(pOut, "%s\n", blob_str(&out));
624626
}
625627
}
@@ -684,11 +686,16 @@
684686
blob_append_escaped_arg(&cmd, blob_str(&nameFile1), 1);
685687
blob_append_escaped_arg(&cmd, zFile2, 1);
686688
}
687689
688690
/* 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
+ }
690697
691698
/* Delete the temporary file and clean up memory used */
692699
if( useTempfile ) file_delete(blob_str(&nameFile1));
693700
blob_reset(&nameFile1);
694701
blob_reset(&cmd);
@@ -712,18 +719,22 @@
712719
Blob *pFile1, /* In memory content to compare from */
713720
Blob *pFile2, /* In memory content to compare to */
714721
const char *zName, /* Display name of the file */
715722
DiffConfig *pCfg /* Diff flags */
716723
){
717
- if( pCfg->diffFlags & DIFF_BRIEF ) return;
724
+ if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){
725
+ return;
726
+ }
718727
if( pCfg->zDiffCmd==0 ){
719728
Blob out; /* Diff output text */
720729
721730
blob_zero(&out);
722731
text_diff(pFile1, pFile2, &out, pCfg);
723732
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
+ }
725736
}else{
726737
diff_print_filenames(zName, zName, pCfg, 0);
727738
fossil_print("%s\n", blob_str(&out));
728739
}
729740
@@ -1009,11 +1020,13 @@
10091020
}else if( pTo ){
10101021
zName = pTo->zName;
10111022
}else{
10121023
zName = DIFF_NO_NAME;
10131024
}
1014
- if( pCfg->diffFlags & DIFF_BRIEF ) return;
1025
+ if( (pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT) ){
1026
+ return;
1027
+ }
10151028
diff_print_index(zName, pCfg, 0);
10161029
if( pFrom ){
10171030
rid = uuid_to_rid(pFrom->zUuid, 0);
10181031
content_get(rid, &f1);
10191032
}else{
@@ -1097,11 +1110,11 @@
10971110
(void)file_dir_match(pFileDir, pFromFile->zName); /* Record name usage */
10981111
pFromFile = manifest_file_next(pFrom,0);
10991112
pToFile = manifest_file_next(pTo,0);
11001113
}else{
11011114
if( file_dir_match(pFileDir, pToFile->zName) ){
1102
- if( pCfg->diffFlags & DIFF_BRIEF ){
1115
+ if((pCfg->diffFlags & DIFF_BRIEF) && !(pCfg->diffFlags & DIFF_NUMSTAT)){
11031116
fossil_print("CHANGED %s\n", pFromFile->zName);
11041117
}else{
11051118
diff_manifest_entry(pFromFile, pToFile, pCfg);
11061119
}
11071120
}
@@ -1173,25 +1186,34 @@
11731186
/*
11741187
** Return the name of the external diff command, or return NULL if
11751188
** no external diff command is defined.
11761189
*/
11771190
const char *diff_command_external(int guiDiff){
1178
- const char *zDefault;
11791191
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;
11931215
}
11941216
11951217
/*
11961218
** Show diff output in a Tcl/Tk window, in response to the --tk option
11971219
** to the diff command.
@@ -1209,10 +1231,11 @@
12091231
const char *zTempFile = 0;
12101232
char *zCmd;
12111233
const char *zTclsh;
12121234
int bDebug = find_option("tkdebug",0,0)!=0;
12131235
int bDarkMode = find_option("dark",0,0)!=0;
1236
+ (void)find_option("debug",0,0);
12141237
blob_zero(&script);
12151238
/* Caution: When this routine is called from the merge-info command,
12161239
** the --tcl argument requires an argument. But merge-info does not
12171240
** use -i, so we can take -i as that argument. This routine needs to
12181241
** always have -i after --tcl.
@@ -1290,11 +1313,12 @@
12901313
** Usage: %fossil diff|gdiff ?OPTIONS? ?FILE1? ?FILE2 ...?
12911314
**
12921315
** Show the difference between the current version of each of the FILEs
12931316
** specified (as they exist on disk) and that same file as it was checked-
12941317
** 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.
12961320
**
12971321
** The default output format is a "unified patch" (the same as the
12981322
** output of "diff -u" on most unix systems). Many alternative formats
12991323
** are available. A few of the more useful alternatives:
13001324
**
@@ -1356,11 +1380,13 @@
13561380
** -i|--internal Use internal diff logic
13571381
** --invert Invert the diff
13581382
** --json Output formatted as JSON
13591383
** -n|--linenum Show line numbers
13601384
** -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.
13621388
** -y|--side-by-side Side-by-side diff
13631389
** --strip-trailing-cr Strip trailing CR
13641390
** --tcl Tcl-formatted output used internally by --tk
13651391
** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh")
13661392
** --tk Launch a Tcl/Tk GUI for display
@@ -1382,15 +1408,15 @@
13821408
int againstUndo = 0; /* Diff against files in the undo buffer */
13831409
FileDirList *pFileDir = 0; /* Restrict the diff to these files */
13841410
DiffConfig DCfg; /* Diff configuration object */
13851411
int bFromIsDir = 0; /* True if zFrom is a directory name */
13861412
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") ){
13881415
diff_tk("diff", 2);
13891416
return;
13901417
}
1391
- isGDiff = g.argv[1][0]=='g';
13921418
zFrom = find_option("from", "r", 1);
13931419
zTo = find_option("to", 0, 1);
13941420
zCheckin = find_option("checkin", "ci", 1);
13951421
zBranch = find_option("branch", 0, 1);
13961422
againstUndo = find_option("undo",0,0)!=0;
@@ -1402,10 +1428,11 @@
14021428
if( zTo || zFrom || zCheckin ){
14031429
fossil_fatal("cannot use --from, --to, or --checkin with --branch");
14041430
}
14051431
zTo = zBranch;
14061432
zFrom = mprintf("root:%s", zBranch);
1433
+ zBranch = 0;
14071434
}
14081435
if( zCheckin!=0 && (zFrom!=0 || zTo!=0) ){
14091436
fossil_fatal("cannot use --checkin together with --from or --to");
14101437
}
14111438
if( 0==zCheckin ){
@@ -1416,10 +1443,19 @@
14161443
}else{
14171444
db_find_and_open_repository(0, 0);
14181445
}
14191446
}else{
14201447
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;
14211457
}
14221458
determine_exec_relative_option(1);
14231459
if( zFrom!=file_tail(zFrom)
14241460
&& file_isdir(zFrom, ExtFILE)==1
14251461
&& !db_exists("SELECT 1 FROM tag WHERE tagname='sym-%q'", zFrom)
@@ -1448,11 +1484,14 @@
14481484
pFileDir[i-2].nName = blob_size(&fname);
14491485
pFileDir[i-2].nUsed = 0;
14501486
blob_reset(&fname);
14511487
}
14521488
}
1453
- if ( zCheckin!=0 ){
1489
+ if( DCfg.diffFlags & DIFF_NUMSTAT ){
1490
+ fossil_print("%10s %10s\n", "INSERTED", "DELETED");
1491
+ }
1492
+ if( zCheckin!=0 ){
14541493
int ridTo = name_to_typed_rid(zCheckin, "ci");
14551494
zTo = zCheckin;
14561495
zFrom = db_text(0,
14571496
"SELECT uuid FROM blob, plink"
14581497
" WHERE plink.cid=%d AND plink.isprim AND plink.pid=blob.rid",
@@ -1488,12 +1527,12 @@
14881527
}
14891528
fossil_free(pFileDir);
14901529
}
14911530
diff_end(&DCfg, 0);
14921531
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": "");
14951534
}
14961535
}
14971536
14981537
/*
14991538
** WEBPAGE: vpatch
15001539
--- 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 @@
5454
/* NOTE: 0x0400 = CMDFLAG_SENSITIVE in mkindex.c! */
5555
#define CMDFLAG_HIDDEN 0x0800 /* Elide from most listings */
5656
#define CMDFLAG_LDAVG_EXEMPT 0x1000 /* Exempt from load_control() */
5757
#define CMDFLAG_ALIAS 0x2000 /* Command aliases */
5858
#define CMDFLAG_KEEPEMPTY 0x4000 /* Do not unset empty settings */
59
+#define CMDFLAG_ABBREVSUBCMD 0x8000 /* Help text abbreviates subcommands */
5960
/**************************************************************************/
6061
6162
/* Values for the 2nd parameter to dispatch_name_search() */
6263
#define CMDFLAG_ANY 0x0038 /* Match anything */
6364
#define CMDFLAG_PREFIX 0x0200 /* Prefix match is ok */
@@ -522,13 +523,21 @@
522523
}
523524
524525
/*
525526
** Format help text for TTY display.
526527
*/
527
-static void help_to_text(const char *zHelp, Blob *pText){
528
+static void help_to_text(const char *zHelp, Blob *pText, int bUsage){
528529
int i, x;
529530
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
+ }
530539
for(i=0; (c = zHelp[i])!=0; i++){
531540
if( c=='%' && strncmp(zHelp+i,"%fossil",7)==0 ){
532541
if( i>0 ) blob_append(pText, zHelp, i);
533542
blob_append(pText, "fossil", 6);
534543
zHelp += i+7;
@@ -603,11 +612,11 @@
603612
fossil_print("# %s\n", aCommand[bktHelp[aCommand[i].iHelp][j]].zName);
604613
fossil_print("%s\n\n", aCommand[i].zHelp);
605614
}else{
606615
Blob txt;
607616
blob_init(&txt, 0, 0);
608
- help_to_text(aCommand[i].zHelp, &txt);
617
+ help_to_text(aCommand[i].zHelp, &txt, 0);
609618
for(j=0; j<occHelp[aCommand[i].iHelp]; j++){
610619
fossil_print("# %s%s\n",
611620
aCommand[bktHelp[aCommand[i].iHelp][j]].zName,
612621
(aCommand[i].eCmdFlags & CMDFLAG_VERSIONABLE)!=0 ?
613622
" (versionable)" : "");
@@ -802,10 +811,32 @@
802811
fossil_print(" %s\n", az[j]);
803812
}
804813
}
805814
}
806815
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
+
807838
/*
808839
** WEBPAGE: help
809840
** URL: /help?name=CMD
810841
**
811842
** Show the built-in help text for CMD. CMD can be a command-line interface
@@ -829,34 +860,46 @@
829860
if( zCmd && *zCmd ){
830861
int rc;
831862
const CmdOrPage *pCmd = 0;
832863
833864
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
+ }
837869
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=='/' ){
839878
/* Some of the webpages require query parameters in order to work.
840879
** @ <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>
842881
}else if( rc==0 && (pCmd->eCmdFlags & CMDFLAG_SETTING)!=0 ){
843882
@ <h1>The "%h(pCmd->zName)" setting:</h1>
844883
}else{
845
- @ <h1>The "%h(zCmd)" command:</h1>
884
+ @ <h1>The "%h(pCmd->zName)" command:</h1>
846885
}
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
+ }
849892
}else if( rc==2 ){
850
- @ ambiguous command prefix: %h(zCmd)
893
+ @ Ambiguous prefix: "%h(zCmd)"
851894
}else{
852895
if( pCmd->zHelp[0]==0 ){
853896
@ No help available for "%h(pCmd->zName)"
854897
}else if( P("plaintext") ){
855898
Blob txt;
856899
blob_init(&txt, 0, 0);
857
- help_to_text(pCmd->zHelp, &txt);
900
+ help_to_text(pCmd->zHelp, &txt, 0);
858901
@ <pre class="helpPage">
859902
@ %h(blob_str(&txt))
860903
@ </pre>
861904
blob_reset(&txt);
862905
}else if( P("raw") ){
@@ -873,10 +916,11 @@
873916
int i;
874917
const char *zWidth = "28ex";
875918
unsigned char occHelp[FOSSIL_MX_CMDIDX] = {0}; /* Help str occurrences */
876919
int bktHelp[FOSSIL_MX_CMDIDX][MX_HELP_DUP] = {{0}};/* Help str->commands */
877920
style_header("Help");
921
+ search_screen(SRCH_HELP, 0x02);
878922
879923
@ <a name='commands'></a>
880924
@ <h1>Available commands:</h1>
881925
@ <div class="columns" style="column-width: %s(zWidth);">
882926
@ <ul>
@@ -1055,10 +1099,229 @@
10551099
}
10561100
@ </dl>
10571101
blob_reset(&buf);
10581102
style_finish_page();
10591103
}
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
+
10601323
10611324
static void multi_column_list(const char **azWord, int nWord){
10621325
int i, j, len;
10631326
int mxLen = 0;
10641327
int nCol;
@@ -1125,12 +1388,10 @@
11251388
@
11261389
@ --args FILENAME Read additional arguments and options from FILENAME
11271390
@ --case-sensitive BOOL Set case sensitivity for file names
11281391
@ --cgitrace Active CGI tracing
11291392
@ --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
11321393
@ --errorlog FILENAME Log errors to FILENAME
11331394
@ --help Show help on the command rather than running it
11341395
@ --httptrace Trace outbound HTTP requests
11351396
@ --localtime Display times using the local timezone
11361397
@ --nocgi Do not act as CGI
@@ -1147,55 +1408,68 @@
11471408
;
11481409
11491410
/*
11501411
** COMMAND: help
11511412
**
1152
-** Usage: %fossil help [OPTIONS] [TOPIC]
1413
+** Usage: %fossil help [OPTIONS] [TOPIC] [SUBCOMMAND]
11531414
**
11541415
** Display information on how to use TOPIC, which may be a command, webpage, or
11551416
** 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.
11571420
**
11581421
** The following options can be used when TOPIC is omitted:
11591422
**
11601423
** -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
11611428
** -o|--options List command-line options common to all commands
11621429
** -s|--setting List setting names
11631430
** -t|--test List unsupported "test" commands
11641431
** -v|--verbose List both names and help text
11651432
** -x|--aux List only auxiliary commands
11661433
** -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
11711434
**
11721435
** These options can be used when TOPIC is present:
11731436
**
1174
-** -h|--html Format output as HTML rather than plain text
11751437
** -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
11761444
*/
11771445
void help_cmd(void){
11781446
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
+
11891464
verboseFlag = find_option("verbose","v",0)!=0;
11901465
commandsFlag = find_option("commands","c",0)!=0;
11911466
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) ){
11971471
command_list(CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER, verboseFlag, useHtml);
11981472
return;
11991473
}
12001474
else if( find_option("www","w",0) ){
12011475
command_list(CMDFLAG_WEBPAGE, verboseFlag, useHtml);
@@ -1223,34 +1497,45 @@
12231497
command_list(CMDFLAG_SETTING, verboseFlag, useHtml);
12241498
fossil_print("\nfossil web pages:\n\n");
12251499
command_list(CMDFLAG_WEBPAGE, verboseFlag, useHtml);
12261500
fossil_print("\nfossil test commands (unsupported):\n\n");
12271501
command_list(CMDFLAG_TEST, verboseFlag, useHtml);
1228
- fossil_print("\n");
1229
- version_cmd();
1502
+ if ( !verboseFlag ) {
1503
+ fossil_print("\n");
1504
+ version_cmd();
1505
+ }
12301506
return;
12311507
}
12321508
else if( find_option("everything","e",0) ){
12331509
display_all_help(CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER | CMDFLAG_WEBPAGE |
12341510
CMDFLAG_SETTING | CMDFLAG_TEST, useHtml, 0);
12351511
return;
12361512
}
12371513
verify_all_options();
12381514
if( g.argc<3 ){
1515
+ if( bOptions ){
1516
+ fossil_print("%s", zOptions);
1517
+ return;
1518
+ }
12391519
z = g.argv[0];
12401520
fossil_print(
12411521
"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);
12451529
command_list(CMDFLAG_1ST_TIER,verboseFlag,useHtml);
12461530
if( !verboseFlag ) version_cmd();
12471531
return;
12481532
}
12491533
zTopic = g.argv[2];
1534
+ zSubtopic = g.argc>=4 ? g.argv[3] : 0;
12501535
isPage = ('/' == zTopic[0]) ? 1 : 0;
1251
- if(isPage){
1536
+ if( isPage ){
12521537
zCmdOrPage = "page";
12531538
}else if( commandsFlag ){
12541539
mask = CMDFLAG_COMMAND;
12551540
zCmdOrPage = "command";
12561541
}else{
@@ -1259,10 +1544,14 @@
12591544
rc = dispatch_name_search(g.argv[2], mask|CMDFLAG_PREFIX, &pCmd);
12601545
if( rc ){
12611546
int i, n;
12621547
const char *az[5];
12631548
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
+ }
12641553
fossil_print("unknown %s: %s\n", zCmdOrPage, g.argv[2]);
12651554
}else{
12661555
fossil_print("ambiguous %s prefix: %s\n",
12671556
zCmdOrPage, g.argv[2]);
12681557
}
@@ -1269,24 +1558,48 @@
12691558
fossil_print("Did you mean one of these TOPICs:\n");
12701559
n = dispatch_approx_match(g.argv[2], 5, az);
12711560
for(i=0; i<n; i++){
12721561
fossil_print(" * %s\n", az[i]);
12731562
}
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;
12821572
z = pCmd->zHelp;
12831573
if( z==0 ){
12841574
fossil_fatal("no help available for the %s %s",
12851575
pCmd->zName, zCmdOrPage);
12861576
}
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 ){
12881601
const Setting *pSetting = db_find_setting(pCmd->zName, 0);
12891602
char *zDflt = 0;
12901603
if( pSetting!=0 && pSetting->def!=0 && *pSetting->def!=0 ){
12911604
zDflt = mprintf(" (default: %s)", pSetting->def);
12921605
}
@@ -1295,17 +1608,21 @@
12951608
(pCmd->eCmdFlags & CMDFLAG_VERSIONABLE)!=0 ? " (versionable)" : ""
12961609
);
12971610
fossil_free(zDflt);
12981611
}
12991612
blob_init(&txt, 0, 0);
1300
- if( useHtml ){
1613
+ if( bRaw ){
1614
+ blob_append(&txt, z, -1);
1615
+ }else if( useHtml ){
13011616
help_to_html(z, &txt);
13021617
}else{
1303
- help_to_text(z, &txt);
1618
+ help_to_text(z, &txt, bUsage || zSubtopic!=0);
13041619
}
1305
- fossil_print("%s\n", blob_str(&txt));
1620
+ if( blob_strlen(&txt)>0 ) fossil_print("%s\n", blob_str(&txt));
13061621
blob_reset(&txt);
1622
+ blob_reset(&subtext1);
1623
+ blob_reset(&subtext2);
13071624
}
13081625
13091626
/*
13101627
** Return a pointer to the setting information array.
13111628
**
@@ -1477,11 +1794,11 @@
14771794
sqlite3_result_text(ctx, pPage->zHelp, -1, SQLITE_STATIC);
14781795
break;
14791796
case 4: { /* formatted */
14801797
Blob txt;
14811798
blob_init(&txt, 0, 0);
1482
- help_to_text(pPage->zHelp, &txt);
1799
+ help_to_text(pPage->zHelp, &txt, 0);
14831800
sqlite3_result_text(ctx, blob_str(&txt), -1, fossil_free);
14841801
break;
14851802
}
14861803
case 5: { /* formatted */
14871804
Blob txt;
14881805
--- 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 @@
639639
** are special to HTML encoded. We need to decode these before turning
640640
** the text into a title, as the title text will be reencoded later */
641641
char *zTitle = mprintf("%.*s", nValue, zValue);
642642
int i;
643643
for(i=0; fossil_isspace(zTitle[i]); i++){}
644
- html_to_plaintext(zTitle+i, pTitle);
644
+ html_to_plaintext(zTitle+i, pTitle, 0);
645645
fossil_free(zTitle);
646646
seenTitle = 1;
647647
if( seenClass ) return 1;
648648
}
649649
}
@@ -807,10 +807,11 @@
807807
}else if( fossil_strcmp(zMime, "text/x-markdown")==0 ){
808808
Blob tail = BLOB_INITIALIZER;
809809
markdown_to_html(pBody, &title, &tail);
810810
if( !isPopup ){
811811
if( blob_size(&title)>0 ){
812
+ markdown_dehtmlize_blob(&title);
812813
style_header("%s", blob_str(&title));
813814
}else{
814815
style_header("%s", zDefaultTitle);
815816
}
816817
}
817818
--- 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 @@
244244
}
245245
246246
/*
247247
** Convert a single HEX digit to an integer
248248
*/
249
-static int AsciiToHex(int c){
249
+int fossil_hexvalue(int c){
250250
if( c>='a' && c<='f' ){
251251
c += 10 - 'a';
252252
}else if( c>='A' && c<='F' ){
253253
c += 10 - 'A';
254254
}else if( c>='0' && c<='9' ){
@@ -272,12 +272,12 @@
272272
i = j = 0;
273273
while( z[i] ){
274274
switch( z[i] ){
275275
case '%':
276276
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]);
279279
i += 2;
280280
}
281281
break;
282282
case '+':
283283
z[j] = ' ';
284284
--- 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 @@
480480
**
481481
** See also: import
482482
*/
483483
/*
484484
** COMMAND: export*
485
+**
486
+** Usage: %fossil export --git [REPOSITORY]
485487
**
486488
** This command is deprecated. Use "fossil git export" instead.
487489
*/
488490
void export_cmd(void){
489491
Stmt q, q2, q3;
@@ -1072,12 +1074,11 @@
10721074
*/
10731075
static int gitmirror_send_checkin(
10741076
FILE *xCmd, /* Write fast-import text on this pipe */
10751077
int rid, /* BLOB.RID for the check-in to export */
10761078
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 */
10791080
){
10801081
Manifest *pMan; /* The check-in to be output */
10811082
int i; /* Loop counter */
10821083
int iParent; /* Which immediate ancestor is primary. -1 for none */
10831084
Stmt q; /* An SQL query */
@@ -1087,10 +1088,12 @@
10871088
Blob comment; /* The comment text for the check-in */
10881089
int nErr = 0; /* Number of errors */
10891090
int bPhantomOk; /* True if phantom files should be ignored */
10901091
char buf[24];
10911092
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 */
10921095
10931096
pMan = manifest_get(rid, CFTYPE_MANIFEST, 0);
10941097
if( pMan==0 ){
10951098
/* Must be a phantom. Return without doing anything, and in particular
10961099
** without creating a mark for this check-in. */
@@ -1104,11 +1107,11 @@
11041107
char *zPMark = gitmirror_find_mark(pMan->azParent[i], 0, 0);
11051108
if( zPMark==0 ){
11061109
int prid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q",
11071110
pMan->azParent[i]);
11081111
int rc = gitmirror_send_checkin(xCmd, prid, pMan->azParent[i],
1109
- pnLimit, fManifest);
1112
+ pnLimit);
11101113
if( rc || *pnLimit<=0 ){
11111114
manifest_destroy(pMan);
11121115
return 1;
11131116
}
11141117
}
@@ -1213,10 +1216,11 @@
12131216
blob_reset(&comment);
12141217
iParent = -1; /* Which ancestor is the primary parent */
12151218
for(i=0; i<pMan->nParent; i++){
12161219
char *zOther = gitmirror_find_mark(pMan->azParent[i],0,0);
12171220
if( zOther==0 ) continue;
1221
+ fPManifest |= db_get_manifest_setting(pMan->azParent[i]);
12181222
if( iParent<0 ){
12191223
iParent = i;
12201224
fprintf(xCmd, "from %s\n", zOther);
12211225
}else{
12221226
fprintf(xCmd, "merge %s\n", zOther);
@@ -1269,29 +1273,36 @@
12691273
db_finalize(&q);
12701274
manifest_destroy(pMan);
12711275
pMan = 0;
12721276
12731277
/* Include Fossil-generated auxiliary files in the check-in */
1278
+ fManifest = db_get_manifest_setting(zUuid);
12741279
if( fManifest & MFESTFLG_RAW ){
12751280
Blob manifest;
12761281
content_get(rid, &manifest);
12771282
sterilize_manifest(&manifest, CFTYPE_MANIFEST);
12781283
fprintf(xCmd,"M 100644 inline manifest\ndata %d\n%s\n",
12791284
blob_strlen(&manifest), blob_str(&manifest));
12801285
blob_reset(&manifest);
1286
+ }else if( fPManifest & MFESTFLG_RAW ){
1287
+ fprintf(xCmd, "D manifest\n");
12811288
}
12821289
if( fManifest & MFESTFLG_UUID ){
12831290
int n = (int)strlen(zUuid);
12841291
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");
12851294
}
12861295
if( fManifest & MFESTFLG_TAGS ){
12871296
Blob tagslist;
12881297
blob_init(&tagslist, 0, 0);
12891298
get_checkin_taglist(rid, &tagslist);
12901299
fprintf(xCmd,"M 100644 inline manifest.tags\ndata %d\n%s\n",
12911300
blob_strlen(&tagslist), blob_str(&tagslist));
12921301
blob_reset(&tagslist);
1302
+ }else if( fPManifest & MFESTFLG_TAGS ){
1303
+ fprintf(xCmd, "D manifest.tags\n");
12931304
}
12941305
12951306
/* The check-in is finished, so decrement the counter */
12961307
(*pnLimit)--;
12971308
return 0;
@@ -1381,11 +1392,10 @@
13811392
char *zPushUrl; /* URL to sync the mirror to */
13821393
double rEnd; /* time of most recent export */
13831394
int rc; /* Result code */
13841395
int bForce; /* Do the export and sync even if no changes*/
13851396
int bNeedRepack = 0; /* True if we should run repack at the end */
1386
- int fManifest; /* Current "manifest" setting */
13871397
int bIfExists; /* The --if-mirrored flag */
13881398
FILE *xCmd; /* Pipe to the "git fast-import" command */
13891399
FILE *pMarks; /* Git mark files */
13901400
Stmt q; /* Queries */
13911401
char zLine[200]; /* One line of a mark file */
@@ -1519,13 +1529,10 @@
15191529
gitmirror_message(VERB_NORMAL, "no changes\n");
15201530
db_commit_transaction();
15211531
return;
15221532
}
15231533
1524
- /* Do we need to include manifest files in the clone? */
1525
- fManifest = db_get_manifest_setting();
1526
-
15271534
/* Change to the MIRROR directory so that the Git commands will work */
15281535
rc = file_chdir(zMirror, 0);
15291536
if( rc ) fossil_fatal("cannot change the working directory to \"%s\"",
15301537
zMirror);
15311538
@@ -1577,11 +1584,11 @@
15771584
while( nLimit && db_step(&q)==SQLITE_ROW ){
15781585
int rid = db_column_int(&q, 0);
15791586
double rMTime = db_column_double(&q, 1);
15801587
const char *zUuid = db_column_text(&q, 2);
15811588
if( rMTime>rEnd ) rEnd = rMTime;
1582
- rc = gitmirror_send_checkin(xCmd, rid, zUuid, &nLimit, fManifest);
1589
+ rc = gitmirror_send_checkin(xCmd, rid, zUuid, &nLimit);
15831590
if( rc ) break;
15841591
gitmirror_message(VERB_NORMAL,"%d/%d \r", nTotal-nLimit, nTotal);
15851592
fflush(stdout);
15861593
}
15871594
db_finalize(&q);
15881595
--- 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 @@
15721572
**
15731573
** Usage: %fossil test-which ARGS...
15741574
**
15751575
** For each argument, search the PATH for the executable with the name
15761576
** 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.
15771584
*/
15781585
void test_which_cmd(void){
15791586
int i;
15801587
for(i=2; i<g.argc; i++){
15811588
char *z = file_fullexename(g.argv[i]);
15821589
--- 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 @@
498498
" AND event.objid=mlink.mid\n",
499499
TAG_BRANCH
500500
);
501501
if( (zA = P("a"))!=0 ){
502502
blob_append_sql(&sql, " AND event.mtime>=%.16g\n",
503
- symbolic_name_to_mtime(zA,0));
503
+ symbolic_name_to_mtime(zA,0,0));
504504
url_add_parameter(&url, "a", zA);
505505
}
506506
if( (zB = P("b"))!=0 ){
507507
blob_append_sql(&sql, " AND event.mtime<=%.16g\n",
508
- symbolic_name_to_mtime(zB,0));
508
+ symbolic_name_to_mtime(zB,0,1));
509509
url_add_parameter(&url, "b", zB);
510510
}
511511
if( ridFrom ){
512512
blob_append_sql(&sql,
513513
" AND mlink.mid IN (SELECT rid FROM ancestor)\n"
@@ -636,10 +636,12 @@
636636
if( zBr==0 ) zBr = "trunk";
637637
if( uBg ){
638638
zBgClr = user_color(zUser);
639639
}else if( brBg || zBgClr==0 || zBgClr[0]==0 ){
640640
zBgClr = strcmp(zBr,"trunk")==0 ? "" : hash_color(zBr);
641
+ }else if( zBgClr ){
642
+ zBgClr = reasonable_bg_color(zBgClr,0);
641643
}
642644
gidx = graph_add_row(pGraph,
643645
frid>0 ? (GraphRowId)frid*(mxfnid+1)+fnid : fpid+1000000000,
644646
nParent, 0, aParent, zBr, zBgClr,
645647
zUuid, 0);
646648
--- 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
--- src/fossil.page.pikchrshowasm.js
+++ src/fossil.page.pikchrshowasm.js
@@ -120,11 +120,16 @@
120120
}
121121
});
122122
delete PS._config;
123123
}
124124
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');
126131
PS.worker.onmessage = (ev)=>PS.runMsgHandlers(ev.data);
127132
PS.addMsgHandler('stdout', console.log.bind(console));
128133
PS.addMsgHandler('stderr', console.error.bind(console));
129134
130135
/** Handles status updates from the Module object. */
@@ -172,21 +177,21 @@
172177
PS.renderModeLabels[PS.renderModes[PS.renderModes.selectedIndex]];
173178
174179
/**
175180
The 'pikchr-ready' event is fired (with no payload) when the
176181
wasm module has finished loading. */
177
- PS.addMsgHandler('pikchr-ready', function(){
182
+ PS.addMsgHandler('pikchr-ready', function(event){
178183
PS.clearMsgHandlers('pikchr-ready');
179
- F.page.onPikchrshowLoaded();
184
+ F.page.onPikchrshowLoaded(event.data);
180185
});
181186
182187
/**
183188
Performs all app initialization which must wait until after the
184189
worker module is loaded. This function removes itself when it's
185190
called.
186191
*/
187
- F.page.onPikchrshowLoaded = function(){
192
+ F.page.onPikchrshowLoaded = function(pikchrVersion){
188193
delete this.onPikchrshowLoaded;
189194
// Unhide all elements which start out hidden
190195
EAll('.initially-hidden').forEach((e)=>e.classList.remove('initially-hidden'));
191196
const taInput = E('#input');
192197
const btnClearIn = E('#btn-clear');
@@ -439,10 +444,13 @@
439444
if( src && (new URL(self.location.href).searchParams).has('fromSession') ){
440445
taInput.value = src;
441446
window.sessionStorage.removeItem('pikchr-xfer');
442447
}
443448
}
449
+ D.append(E('fieldset.options > div'),
450
+ D.append(D.addClass(D.span(), 'labeled-input'),
451
+ 'pikchr v. '+pikchrVersion));
444452
445453
PS.e.btnRender.click();
446454
447455
/** Debounce handler for auto-rendering while typing. */
448456
const debounceAutoRender = F.debounce(function f(){
@@ -524,11 +532,11 @@
524532
ForceResizeKludge();
525533
}/*onPikchrshowLoaded()*/;
526534
527535
528536
/**
529
- Predefined scripts. Each entry is an object:
537
+ Predefined example pikchr scripts. Each entry is an object:
530538
531539
{
532540
name: required string,
533541
code: optional code string. An entry with a falsy code is treated
534542
like a separator in the resulting SELECT element (a
535543
--- 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
--- src/fossil.page.wikiedit.js
+++ src/fossil.page.wikiedit.js
@@ -14,11 +14,11 @@
1414
cache), in the form of an "winfo" object:
1515
1616
{
1717
name: string,
1818
mimetype: mimetype string,
19
- type: "normal" | "tag" | "checkin" | "branch" | "sandbox",
19
+ type: "normal" | "tag" | "checkin" | "branch" | "ticket" | "sandbox",
2020
version: UUID string or null for a sandbox page or new page,
2121
parent: parent UUID string or null if no parent,
2222
isEmpty: true if page has no content (is "deleted").
2323
content: string, optional in most contexts
2424
}
@@ -192,11 +192,11 @@
192192
name: winfo.name
193193
});
194194
record.mimetype = winfo.mimetype;
195195
record.type = winfo.type;
196196
record.parent = winfo.parent;
197
- record.version = winfo.version;
197
+ record.version = winfo.version;
198198
record.stashTime = new Date().getTime();
199199
record.isEmpty = !!winfo.isEmpty;
200200
record.attachments = winfo.attachments;
201201
this.storeIndex();
202202
if(arguments.length>1){
@@ -207,11 +207,11 @@
207207
return this;
208208
},
209209
/**
210210
Returns the stashed content, if any, for the given winfo
211211
object.
212
- */
212
+ */
213213
stashedContent: function(winfo){
214214
return F.storage.get(this.contentKey(this.indexKey(winfo)));
215215
},
216216
/** Returns true if we have stashed content for the given winfo
217217
record or page name. */
@@ -270,11 +270,11 @@
270270
if(n) this._fireStashEvent();
271271
}
272272
};
273273
$stash.prune.defaultMaxCount = P.config.defaultMaxStashSize || 10;
274274
P.$stash = $stash /* we have to expose this for the new-page case :/ */;
275
-
275
+
276276
/**
277277
Internal workaround to select the current preview mode
278278
and fire a change event if the value actually changes
279279
or if forceEvent is truthy.
280280
*/
@@ -536,10 +536,11 @@
536536
name = name.trim();
537537
if(!this.validatePageName(name)) return false;
538538
var wtype = 'normal';
539539
if(0===name.indexOf('checkin/')) wtype = 'checkin';
540540
else if(0===name.indexOf('branch/')) wtype = 'branch';
541
+ else if(0===name.indexOf('ticket/')) wtype = 'ticket';
541542
else if(0===name.indexOf('tag/')) wtype = 'tag';
542543
/* ^^^ note that we're not validating that, e.g., checkin/XYZ
543544
has a full artifact ID after "checkin/". */
544545
const winfo = {
545546
name: name, type: wtype, mimetype: 'text/x-markdown',
@@ -573,11 +574,11 @@
573574
574575
/** Set up filter checkboxes for the various types
575576
of wiki pages... */
576577
const fsFilter = D.addClass(D.fieldset("Page types"),"page-types-list"),
577578
fsFilterBody = D.div(),
578
- filters = ['normal', 'branch/...', 'tag/...', 'checkin/...']
579
+ filters = ['normal', 'branch/...', 'tag/...', 'checkin/...', 'ticket/...']
579580
;
580581
D.append(fsFilter, fsFilterBody);
581582
D.addClass(fsFilterBody, 'flex-container', 'flex-column', 'stretch');
582583
583584
// Add filters by page type...
@@ -1045,11 +1046,11 @@
10451046
P.e.btnSave.addEventListener('click', ()=>doSave(), false);
10461047
P.e.btnSaveClose.addEventListener('click', ()=>doSave(true), false);
10471048
}
10481049
10491050
P.e.taEditor.addEventListener('change', ()=>P.notifyOfChange(), false);
1050
-
1051
+
10511052
P.selectMimetype(false, true);
10521053
P.e.selectMimetype.addEventListener(
10531054
'change',
10541055
function(e){
10551056
if(P.winfo && P.winfo.mimetype !== e.target.value){
@@ -1058,11 +1059,11 @@
10581059
P.stashContentChange(true);
10591060
}
10601061
},
10611062
false
10621063
);
1063
-
1064
+
10641065
const selectFontSize = E('select[name=editor_font_size]');
10651066
if(selectFontSize){
10661067
selectFontSize.addEventListener(
10671068
"change",function(e){
10681069
const ed = P.e.taEditor;
@@ -1590,11 +1591,11 @@
15901591
responseType: 'json',
15911592
onload: callee.onload
15921593
});
15931594
return this;
15941595
};
1595
-
1596
+
15961597
/**
15971598
Updates P.winfo for certain state and stashes P.winfo, with the
15981599
current content fetched via P.wikiContent().
15991600
16001601
If passed truthy AND the stash already has stashed content for
16011602
--- 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 @@
6060
*/
6161
#define FUZZ_WIKI 0 /* The Fossil-Wiki formatter */
6262
#define FUZZ_MARKDOWN 1 /* The Markdown formatter */
6363
#define FUZZ_ARTIFACT 2 /* Fuzz the artifact parser */
6464
#define FUZZ_WIKI2 3 /* FOSSIL_WIKI and FOSSIL_MARKDOWN */
65
+#define FUZZ_COMFORMAT 4 /* comment_print() */
6566
#endif
6667
6768
/* The type of fuzzing to do */
6869
static int eFuzzType = FUZZ_WIKI;
6970
@@ -93,13 +94,20 @@
9394
blob_reset(&out);
9495
markdown_to_html(&in, &title, &out);
9596
blob_reset(&title);
9697
break;
9798
}
98
- case FUZZ_ARTIFACT:
99
+ case FUZZ_ARTIFACT: {
99100
fossil_fatal("FUZZ_ARTIFACT is not implemented.");
100101
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
+ }
101109
}
102110
blob_reset(&in);
103111
blob_reset(&out);
104112
return 0;
105113
}
@@ -116,10 +124,12 @@
116124
eFuzzType = FUZZ_WIKI;
117125
}else if( fossil_strcmp(zType,"markdown")==0 ){
118126
eFuzzType = FUZZ_MARKDOWN;
119127
}else if( fossil_strcmp(zType,"wiki2")==0 ){
120128
eFuzzType = FUZZ_WIKI2;
129
+ }else if( fossil_strcmp(zType,"comformat")==0 ){
130
+ eFuzzType = FUZZ_COMFORMAT;
121131
}else{
122132
fossil_fatal("unknown fuzz type: \"%s\"", zType);
123133
}
124134
}
125135
@@ -139,10 +149,11 @@
139149
**
140150
** Usage: %fossil test-fuzz [-fuzztype TYPE] INPUTFILE...
141151
**
142152
** Run a fuzz test using INPUTFILE as the test data. TYPE can be one of:
143153
**
154
+** comformat Fuzz the comment_print() routine
144155
** wiki Fuzz the Fossil-wiki translator
145156
** markdown Fuzz the markdown translator
146157
** artifact Fuzz the artifact parser
147158
** wiki2 Fuzz the Fossil-wiki and markdown translator
148159
*/
149160
--- 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 @@
5959
** check-ins and many files. For this reason, we make the identifier
6060
** a 64-bit integer, to dramatically reduce the risk of an overflow.
6161
*/
6262
typedef sqlite3_int64 GraphRowId;
6363
64
-#define GR_MAX_RAIL 40 /* Max number of "rails" to display */
64
+#define GR_MAX_RAIL 64 /* Max number of "rails" to display */
6565
6666
/* The graph appears vertically beside a timeline. Each row in the
6767
** timeline corresponds to a row in the graph. GraphRow.idx is 0 for
6868
** the top-most row and increases moving down. Hence (in the absence of
6969
** time skew) parents have a larger index than their children.
@@ -84,11 +84,11 @@
8484
8585
GraphRow *pNext; /* Next row down in the list of all rows */
8686
GraphRow *pPrev; /* Previous row */
8787
8888
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 */
9090
GraphRow *pChild; /* Child immediately above this node */
9191
u8 isDup; /* True if this is duplicate of a prior entry */
9292
u8 isLeaf; /* True if this is a leaf node */
9393
u8 isStepParent; /* pChild is actually a step-child. The thick
9494
** arrow up to the child is dashed, not solid */
@@ -118,10 +118,11 @@
118118
char **azBranch; /* Names of the branches */
119119
int nRow; /* Number of rows */
120120
int nHash; /* Number of slots in apHash[] */
121121
u8 hasOffsetMergeRiser; /* Merge arrow from leaf goes up on a different
122122
** rail that the node */
123
+ u8 bOverfull; /* Unable to allocate sufficient rails */
123124
u64 mergeRail; /* Rails used for merge lines */
124125
GraphRow **apHash; /* Hash table of GraphRow objects. Key: rid */
125126
u8 aiRailMap[GR_MAX_RAIL]; /* Mapping of rails to actually columns */
126127
};
127128
@@ -336,11 +337,18 @@
336337
iBestDist = dist;
337338
iBest = i;
338339
}
339340
}
340341
}
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
+ }
342350
if( iBest>p->mxRail ) p->mxRail = iBest;
343351
if( bMergeRail ) p->mergeRail |= BIT(iBest);
344352
return iBest;
345353
}
346354
@@ -709,11 +717,11 @@
709717
if( pRow->iRail>=0 ) continue;
710718
if( pRow->isDup ) continue;
711719
if( pRow->nParent<0 ) continue;
712720
if( pRow->nParent==0 || hashFind(p,pRow->aParent[0])==0 ){
713721
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; */
715723
mask = BIT(pRow->iRail);
716724
if( !omitDescenders ){
717725
int n = RISER_MARGIN;
718726
pRow->bDescender = pRow->nParent>0;
719727
for(pLoop=pRow; pLoop && (n--)>0; pLoop=pLoop->pNext){
@@ -744,28 +752,38 @@
744752
assert( pRow->nParent>0 );
745753
parentRid = pRow->aParent[0];
746754
pParent = hashFind(p, parentRid);
747755
if( pParent==0 ){
748756
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
+ }
750761
pRow->railInUse = BIT(pRow->iRail);
751762
continue;
752763
}
753764
if( pParent->idx>pRow->idx ){
754765
/* Common case: Child occurs after parent and is above the
755766
** parent in the timeline */
756767
pRow->iRail = findFreeRail(p, pRow->idxTop, pParent->idx,
757768
pParent->iRail, 0);
758
- if( p->mxRail>=GR_MAX_RAIL ) return;
769
+ /* if( p->mxRail>=GR_MAX_RAIL ) return; */
759770
pParent->aiRiser[pRow->iRail] = pRow->idx;
760771
}else{
761772
/* Timewarp case: Child occurs earlier in time than parent and
762773
** appears below the parent in the timeline. */
763774
int iDownRail = ++p->mxRail;
764775
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
+ }
765780
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
+ }
767785
pRow->railInUse = BIT(pRow->iRail);
768786
pParent->aiRiser[iDownRail] = pRow->idx;
769787
mask = BIT(iDownRail);
770788
for(pLoop=p->pFirst; pLoop; pLoop=pLoop->pNext){
771789
pLoop->railInUse |= mask;
@@ -824,11 +842,11 @@
824842
break;
825843
}
826844
}
827845
if( iMrail==-1 ){
828846
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;*/
830848
mergeRiserFrom[iMrail] = parentRid;
831849
}
832850
iReuseIdx = p->nRow+1;
833851
iReuseRail = iMrail;
834852
mask = BIT(iMrail);
@@ -856,11 +874,11 @@
856874
pDesc->mergeUpto = pDesc->idx;
857875
}
858876
}else{
859877
/* Create a new merge for an on-screen node */
860878
createMergeRiser(p, pDesc, pRow, isCherrypick);
861
- if( p->mxRail>=GR_MAX_RAIL ) return;
879
+ /* if( p->mxRail>=GR_MAX_RAIL ) return; */
862880
if( iReuseIdx<0
863881
&& pDesc->nMergeChild==1
864882
&& (pDesc->iRail!=pDesc->mergeOut || pDesc->isLeaf)
865883
){
866884
iReuseIdx = pDesc->idx;
@@ -872,17 +890,17 @@
872890
}
873891
874892
/*
875893
** Insert merge rails from primaries to duplicates.
876894
*/
877
- if( hasDup ){
895
+ if( hasDup && p->mxRail<GR_MAX_RAIL ){
878896
int dupRail;
879897
int mxRail;
880898
find_max_rail(p);
881899
mxRail = p->mxRail;
882900
dupRail = mxRail+1;
883
- if( p->mxRail>=GR_MAX_RAIL ) return;
901
+ /* if( p->mxRail>=GR_MAX_RAIL ) return; */
884902
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
885903
if( !pRow->isDup ) continue;
886904
pRow->iRail = dupRail;
887905
pDesc = hashFind(p, pRow->rid);
888906
assert( pDesc!=0 && pDesc!=pRow );
@@ -893,11 +911,11 @@
893911
dupRail = mxRail+1;
894912
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
895913
if( pRow->isDup ) pRow->iRail = dupRail;
896914
}
897915
}
898
- if( mxRail>=GR_MAX_RAIL ) return;
916
+ /* if( mxRail>=GR_MAX_RAIL ) return; */
899917
}
900918
901919
/*
902920
** Find the maximum rail number.
903921
*/
904922
--- 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 @@
198198
**
199199
** Usage: %fossil hook COMMAND ...
200200
**
201201
** Commands include:
202202
**
203
-** > fossil hook add --command COMMAND --type TYPE --sequence NUMBER
203
+** > fossil hook add --command COMMAND --type TYPE --sequence NUMBER
204204
**
205205
** Create a new hook. The --command and --type arguments are
206206
** required. --sequence is optional.
207207
**
208
-** > fossil hook delete ID ...
208
+** > fossil hook delete ID ...
209209
**
210210
** Delete one or more hooks by their IDs. ID can be "all"
211211
** to delete all hooks. Caution: There is no "undo" for
212212
** this operation. Deleted hooks are permanently lost.
213213
**
214
-** > fossil hook edit --command COMMAND --type TYPE --sequence NUMBER ID ...
214
+** > fossil hook edit --command COMMAND --type TYPE --sequence NUMBER ID ...
215215
**
216216
** Make changes to one or more existing hooks. The ID argument
217217
** is either a hook-id, or a list of hook-ids, or the keyword
218218
** "all". For example, to disable hook number 2, use:
219219
**
220220
** fossil hook edit --type disabled 2
221221
**
222
-** > fossil hook list
222
+** > fossil hook list
223223
**
224224
** Show all current hooks
225225
**
226
-** > fossil hook status
226
+** > fossil hook status
227227
**
228228
** Print the values of CONFIG table entries that are relevant to
229229
** hook processing. Used for debugging.
230230
**
231
-** > fossil hook test [OPTIONS] ID
231
+** > fossil hook test [OPTIONS] ID
232232
**
233233
** Run the hook script given by ID for testing purposes.
234234
** Options:
235235
**
236236
** --dry-run Print the script on stdout rather than run it
237237
--- 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 @@
512512
transport_send(&g.url, &hdr);
513513
transport_send(&g.url, &payload);
514514
blob_reset(&hdr);
515515
blob_reset(&payload);
516516
transport_flip(&g.url);
517
+ if( mHttpFlags & HTTP_VERBOSE ){
518
+ fossil_print("IP-Address: %s\n", g.zIpAddr);
519
+ }
517520
518521
/*
519522
** Read and interpret the server reply
520523
*/
521524
closeConnection = 1;
@@ -572,10 +575,11 @@
572575
}
573576
}else if( ( rc==301 || rc==302 || rc==307 || rc==308 ) &&
574577
fossil_strnicmp(zLine, "location:", 9)==0 ){
575578
int i, j;
576579
int wasHttps;
580
+ int priorUrlFlags;
577581
578582
if ( --maxRedirect == 0){
579583
fossil_warning("redirect limit exceeded");
580584
goto write_err;
581585
}
@@ -596,10 +600,11 @@
596600
fossil_warning("cannot redirect from %s to %s", g.url.canonical,
597601
&zLine[i]);
598602
goto write_err;
599603
}
600604
wasHttps = g.url.isHttps;
605
+ priorUrlFlags = g.url.flags;
601606
url_parse(&zLine[i], 0);
602607
if( wasHttps && !g.url.isHttps ){
603608
fossil_warning("cannot redirect from HTTPS to HTTP");
604609
goto write_err;
605610
}
@@ -610,11 +615,14 @@
610615
transport_close(&g.url);
611616
transport_global_shutdown(&g.url);
612617
fSeenHttpAuth = 0;
613618
if( g.zHttpAuth ) free(g.zHttpAuth);
614619
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
+ }
616624
return http_exchange(pSend, pReply, mHttpFlags,
617625
maxRedirect, zAltMimetype);
618626
}else if( fossil_strnicmp(zLine, "content-type: ", 14)==0 ){
619627
if( fossil_strnicmp(&zLine[14], "application/x-fossil-debug", -1)==0 ){
620628
isCompressed = 0;
@@ -793,10 +801,11 @@
793801
}
794802
if( find_option("xfer",0,0)!=0 ){
795803
mHttpFlags |= HTTP_USE_LOGIN;
796804
mHttpFlags &= ~HTTP_GENERIC;
797805
}
806
+ if( find_option("ipv4",0,0) ) g.fIPv4 = 1;
798807
verify_all_options();
799808
if( g.argc<3 || g.argc>5 ){
800809
usage("URL ?PAYLOAD? ?OUTPUT?");
801810
}
802811
zInFile = g.argc>=4 ? g.argv[3] : 0;
803812
--- 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 @@
451451
ssl_global_init_client();
452452
if( pUrlData->useProxy ){
453453
int rc;
454454
char *connStr = mprintf("%s:%d", g.url.name, pUrlData->port);
455455
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);
457464
if( BIO_do_connect(sBio)<=0 ){
458465
ssl_set_errmsg("SSL: cannot connect to proxy %s:%d (%s)",
459466
pUrlData->name, pUrlData->port,
460467
ERR_reason_error_string(ERR_get_error()));
461468
ssl_close_client();
@@ -503,11 +510,18 @@
503510
#endif
504511
505512
if( !pUrlData->useProxy ){
506513
char *connStr = mprintf("%s:%d", pUrlData->name, pUrlData->port);
507514
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
+ }
509523
if( BIO_do_connect(iBio)<=0 ){
510524
ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)",
511525
pUrlData->name, pUrlData->port,
512526
ERR_reason_error_string(ERR_get_error()));
513527
ssl_close_client();
@@ -910,31 +924,31 @@
910924
if( file_isdir(zPath, ExtFILE)>0 ) *pzStore = zPath;
911925
}
912926
#endif /* FOSSIL_ENABLE_SSL */
913927
914928
/*
915
-** COMMAND: tls-config*
916
-** COMMAND: ssl-config
929
+** COMMAND: tls-config* abbrv-subcom
930
+** COMMAND: ssl-config abbrv-subcom
917931
**
918932
** Usage: %fossil ssl-config [SUBCOMMAND] [OPTIONS...] [ARGS...]
919933
**
920934
** This command is used to view or modify the TLS (Transport Layer
921935
** Security) configuration for Fossil. TLS (formerly SSL) is the
922936
** encryption technology used for secure HTTPS transport.
923937
**
924938
** Sub-commands:
925939
**
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
936950
*/
937951
void test_tlsconfig_info(void){
938952
const char *zCmd;
939953
size_t nCmd;
940954
int nHit = 0;
941955
--- 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 @@
257257
}
258258
fossil_print("fossil: %z\n", file_fullexename(g.nameOfExe));
259259
fossil_print("version: %s", z);
260260
blob_reset(&vx);
261261
}
262
- }else{
262
+ }else if( g.repositoryOpen ){
263263
int rid;
264264
rid = name_to_rid(g.argv[2]);
265265
if( rid==0 ){
266266
fossil_fatal("no such object: %s", g.argv[2]);
267267
}
268268
show_common_info(rid, "hash:", 1, 1);
269
+ }else{
270
+ fossil_fatal("Could not find or open a Fossil repository");
269271
}
270272
}
271273
272274
/*
273275
** Show the context graph (immediate parents and children) for
@@ -925,12 +927,12 @@
925927
" WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim",
926928
rid
927929
);
928930
isLeaf = !db_exists("SELECT 1 FROM plink WHERE pid=%d", rid);
929931
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"
932934
" FROM blob, event"
933935
" WHERE blob.rid=%d"
934936
" AND event.objid=%d",
935937
rid, rid
936938
);
@@ -1366,72 +1368,10 @@
13661368
webpage_error("Artifact %s is not a check-in.", P(zParam));
13671369
return 0;
13681370
}
13691371
return manifest_get(rid, CFTYPE_MANIFEST, 0);
13701372
}
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
-
14331373
14341374
/*
14351375
** WEBPAGE: vdiff
14361376
** URL: /vdiff?from=TAG&to=TAG
14371377
**
@@ -2682,31 +2622,36 @@
26822622
26832623
/*
26842624
** WEBPAGE: artifact
26852625
** WEBPAGE: file
26862626
** WEBPAGE: whatis
2627
+** WEBPAGE: docfile
26872628
**
26882629
** Typical usage:
26892630
**
26902631
** /artifact/HASH
26912632
** /whatis/HASH
26922633
** /file/NAME
2634
+** /docfile/NAME
26932635
**
26942636
** Additional query parameters:
26952637
**
26962638
** ln - show line numbers
26972639
** ln=N - highlight line number N
26982640
** ln=M-N - highlight lines M through N inclusive
26992641
** ln=M-N+Y-Z - highlight lines M through N and Y through Z (inclusive)
27002642
** 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
27012645
** download - redirect to the download (artifact page only)
27022646
** name=NAME - filename or hash as a query parameter
27032647
** filename=NAME - alternative spelling for "name="
27042648
** fn=NAME - alternative spelling for "name="
27052649
** ci=VERSION - The specific check-in to use with "name=" to
27062650
** identify the file.
27072651
** txt - Force display of unformatted source text
2652
+** hash - Output only the hash of the artifact
27082653
**
27092654
** The /artifact page show the complete content of a file
27102655
** identified by HASH. The /whatis page shows only a description
27112656
** of how the artifact is used. The /file page shows the most recent
27122657
** version of the file or directory called NAME, or a list of the
@@ -2725,18 +2670,21 @@
27252670
void artifact_page(void){
27262671
int rid = 0;
27272672
Blob content;
27282673
const char *zMime;
27292674
Blob downloadName;
2675
+ Blob uuid;
27302676
int renderAsWiki = 0;
27312677
int renderAsHtml = 0;
27322678
int renderAsSvg = 0;
27332679
int objType;
27342680
int asText;
27352681
const char *zUuid = 0;
27362682
u32 objdescFlags = OBJDESC_BASE;
27372683
int descOnly = fossil_strcmp(g.zPath,"whatis")==0;
2684
+ int hashOnly = P("hash")!=0;
2685
+ int docOnly = P("brief")!=0;
27382686
int isFile = fossil_strcmp(g.zPath,"file")==0;
27392687
const char *zLn = P("ln");
27402688
const char *zName = P("name");
27412689
const char *zCI = P("ci");
27422690
HQuery url;
@@ -2747,10 +2695,14 @@
27472695
27482696
login_check_credentials();
27492697
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
27502698
cgi_check_for_malice();
27512699
style_set_current_feature("artifact");
2700
+ if( fossil_strcmp(g.zPath, "docfile")==0 ){
2701
+ isFile = 1;
2702
+ docOnly = 1;
2703
+ }
27522704
27532705
/* Capture and normalize the name= and ci= query parameters */
27542706
if( zName==0 ){
27552707
zName = P("filename");
27562708
if( zName==0 ){
@@ -2849,14 +2801,23 @@
28492801
url_add_parameter(&url, "verbose", "1");
28502802
objdescFlags |= OBJDESC_DETAIL;
28512803
}
28522804
zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
28532805
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
+ }
28542813
28552814
asText = P("txt")!=0;
28562815
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 ){
28582819
zCI = "tip";
28592820
@ <h2>File %z(href("%R/finfo?name=%T&m&ci=tip",zName))%h(zName)</a>
28602821
@ from the %z(href("%R/info/tip"))latest check-in</a></h2>
28612822
}else{
28622823
const char *zPath;
@@ -2874,17 +2835,19 @@
28742835
}else{
28752836
@ part of check-in %z(href("%R/info/%!S",zCIUuid))%S(zCIUuid)</a></h2>
28762837
}
28772838
blob_reset(&path);
28782839
}
2879
- style_submenu_element("Artifact", "%R/artifact/%S", zUuid);
28802840
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
+ }
28862849
blob_init(&downloadName, zName, -1);
28872850
objType = OBJTYPE_CONTENT;
28882851
}else{
28892852
@ <h2>Artifact
28902853
style_copy_button(1, "hash-ar", 0, 2, "%s", zUuid);
@@ -2903,11 +2866,11 @@
29032866
cgi_redirectf("%R/raw/%s?at=%T",
29042867
db_text("x", "SELECT uuid FROM blob WHERE rid=%d", rid),
29052868
file_tail(blob_str(&downloadName)));
29062869
/*NOTREACHED*/
29072870
}
2908
- if( g.perm.Admin ){
2871
+ if( g.perm.Admin && !docOnly ){
29092872
const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
29102873
if( db_exists("SELECT 1 FROM shun WHERE uuid=%Q", zUuid) ){
29112874
style_submenu_element("Unshun", "%R/shun?accept=%s&sub=1#accshun", zUuid);
29122875
}else{
29132876
style_submenu_element("Shun", "%R/shun?shun=%s#addshun",zUuid);
@@ -2948,41 +2911,49 @@
29482911
const char *zIp = db_column_text(&q,2);
29492912
@ <p>Received on %s(zDate) from %h(zUser) at %h(zIp).</p>
29502913
}
29512914
db_finalize(&q);
29522915
}
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
+ }
29562921
}
29572922
if( zMime ){
29582923
if( fossil_strcmp(zMime, "text/html")==0 ){
29592924
if( asText ){
29602925
style_submenu_element("Html", "%s", url_render(&url, "txt", 0, 0, 0));
29612926
}else{
29622927
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
+ }
29642931
}
29652932
}else if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0
29662933
|| fossil_strcmp(zMime, "text/x-markdown")==0
29672934
|| fossil_strcmp(zMime, "text/x-pikchr")==0 ){
29682935
if( asText ){
29692936
style_submenu_element(zMime[7]=='p' ? "Pikchr" : "Wiki",
29702937
"%s", url_render(&url, "txt", 0, 0, 0));
29712938
}else{
29722939
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
+ }
29742943
}
29752944
}else if( fossil_strcmp(zMime, "image/svg+xml")==0 ){
29762945
if( asText ){
29772946
style_submenu_element("Svg", "%s", url_render(&url, "txt", 0, 0, 0));
29782947
}else{
29792948
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
+ }
29812952
}
29822953
}
2983
- if( fileedit_is_editable(zName) ){
2954
+ if( !docOnly && fileedit_is_editable(zName) ){
29842955
style_submenu_element("Edit",
29852956
"%R/fileedit?filename=%T&checkin=%!S",
29862957
zName, zCI);
29872958
}
29882959
}
@@ -2990,11 +2961,13 @@
29902961
style_submenu_element("Parsed", "%R/info/%s", zUuid);
29912962
}
29922963
if( descOnly ){
29932964
style_submenu_element("Content", "%R/artifact/%s", zUuid);
29942965
}else{
2995
- @ <hr>
2966
+ if( !docOnly || !isFile ){
2967
+ @ <hr>
2968
+ }
29962969
content_get(rid, &content);
29972970
if( renderAsWiki ){
29982971
safe_html_context(DOCSRC_FILE);
29992972
wiki_render_by_mimetype(&content, zMime);
30002973
document_emit_js();
@@ -3660,10 +3633,11 @@
36603633
zNewColorFlag = P("newclr") ? " checked" : "";
36613634
zNewTagFlag = P("newtag") ? " checked" : "";
36623635
zNewTag = PDT("tagname","");
36633636
zNewBrFlag = P("newbr") ? " checked" : "";
36643637
zNewBranch = PDT("brname","");
3638
+ zBranchName = branch_of_rid(rid);
36653639
zCloseFlag = P("close") ? " checked" : "";
36663640
zHideFlag = P("hide") ? " checked" : "";
36673641
if( P("apply") && cgi_csrf_safe(2) ){
36683642
Blob ctrl;
36693643
char *zNow;
@@ -3707,17 +3681,25 @@
37073681
zUuid[10] = 0;
37083682
style_header("Edit Check-in [%s]", zUuid);
37093683
if( P("preview") ){
37103684
Blob suffix;
37113685
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
+ }
37123692
@ <b>Preview:</b>
37133693
@ <blockquote>
37143694
@ <table border=0>
37153695
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));">
37173697
}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));">
37193701
}else{
37203702
@ <tr><td>
37213703
}
37223704
@ %!W(blob_str(&comment))
37233705
blob_zero(&suffix);
@@ -3798,13 +3780,10 @@
37983780
@ <tr><th align="right" valign="top">Tags:</th>
37993781
@ <td valign="top">
38003782
@ <label><input type="checkbox" id="newtag" name="newtag"%s(zNewTagFlag)>
38013783
@ Add the following new tag name to this check-in:</label>
38023784
@ <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);
38063785
db_prepare(&q,
38073786
"SELECT tag.tagid, tagname, tagxref.value FROM tagxref, tag"
38083787
" WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid"
38093788
" ORDER BY CASE WHEN tagname GLOB 'sym-*' THEN substr(tagname,5)"
38103789
" ELSE tagname END /*sort*/",
@@ -3941,25 +3920,26 @@
39413920
** Usage: %fossil amend HASH OPTION ?OPTION ...?
39423921
**
39433922
** Amend the tags on check-in HASH to change how it displays in the timeline.
39443923
**
39453924
** 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
39613941
**
39623942
** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in
39633943
** year-month-day form, it may be truncated, the "T" may be replaced by
39643944
** a space, and it may also name a timezone offset from UTC as "-HH:MM"
39653945
** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z"
@@ -3986,19 +3966,22 @@
39863966
int fNewPropagateColor = 0; /* True if color propagates after amend */
39873967
int fHasHidden = 0; /* True if hidden tag already set */
39883968
int fHasClosed = 0; /* True if closed tag already set */
39893969
int fEditComment; /* True if editor to be used for comment */
39903970
int fDryRun; /* Print control artifact, make no changes */
3971
+ int noVerifyCom = 0; /* Allow suspicious check-in comments */
39913972
const char *zChngTime; /* The change time on the control artifact */
39923973
const char *zUserOvrd; /* The user name on the control artifact */
39933974
const char *zUuid;
39943975
Blob ctrl;
39953976
Blob comment;
39963977
char *zNow;
39973978
int nTags, nCancels;
39983979
int i;
39993980
Stmt q;
3981
+ int ckComFlgs; /* Flags passed to verify_comment() */
3982
+
40003983
40013984
fEditComment = find_option("edit-comment","e",0)!=0;
40023985
zNewComment = find_option("comment","m",1);
40033986
zComFile = find_option("message-file","M",1);
40043987
zNewBranch = find_option("branch",0,1);
@@ -4016,10 +3999,11 @@
40163999
fHide = find_option("hide",0,0)!=0;
40174000
fDryRun = find_option("dry-run","n",0)!=0;
40184001
zChngTime = find_option("date-override",0,1);
40194002
if( zChngTime==0 ) zChngTime = find_option("chngtime",0,1);
40204003
zUserOvrd = find_option("user-override",0,1);
4004
+ noVerifyCom = find_option("no-verify-comment",0,0)!=0;
40214005
db_find_and_open_repository(0,0);
40224006
user_select();
40234007
verify_all_options();
40244008
if( g.argc<3 || g.argc>=4 ) usage(AMEND_USAGE_STMT);
40254009
rid = name_to_typed_rid(g.argv[2], "ci");
@@ -4074,21 +4058,69 @@
40744058
);
40754059
}
40764060
if( (zNewColor!=0 && zNewColor[0]==0) && (zColor && zColor[0] ) ){
40774061
cancel_color();
40784062
}
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
+ }
40904122
if( zNewDate && zNewDate[0] && fossil_strcmp(zDate,zNewDate)!=0 ){
40914123
if( is_datetime(zNewDate) ){
40924124
add_date(zNewDate);
40934125
}else{
40944126
fossil_fatal("Unsupported date format, use YYYY-MM-DD HH:MM:SS");
40954127
--- 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 @@
161161
** Usage: %fossil interwiki COMMAND ...
162162
**
163163
** Manage the "intermap" that defines the mapping from interwiki tags
164164
** to complete URLs for interwiki links.
165165
**
166
-** > fossil interwiki delete TAG ...
166
+** > fossil interwiki delete TAG ...
167167
**
168168
** Delete one or more interwiki maps.
169169
**
170
-** > fossil interwiki edit TAG --base URL --hash PATH --wiki PATH
170
+** > fossil interwiki edit TAG --base URL --hash PATH --wiki PATH
171171
**
172172
** Create an interwiki referenced call TAG. The base URL is
173173
** the --base option, which is required. The --hash and --wiki
174174
** paths are optional. The TAG must be lower-case alphanumeric
175175
** and must be unique. A new entry is created if it does not
176176
** already exit.
177177
**
178
-** > fossil interwiki list
178
+** > fossil interwiki list
179179
**
180180
** Show all interwiki mappings.
181181
*/
182182
void interwiki_cmd(void){
183183
const char *zCmd;
184184
--- 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
--- src/json_config.c
+++ src/json_config.c
@@ -86,11 +86,10 @@
8686
{ "logo-image", CONFIGSET_SKIN },
8787
{ "background-mimetype", CONFIGSET_SKIN },
8888
{ "background-image", CONFIGSET_SKIN },
8989
{ "icon-mimetype", CONFIGSET_SKIN },
9090
{ "icon-image", CONFIGSET_SKIN },
91
-{ "timeline-block-markup", CONFIGSET_SKIN },
9291
{ "timeline-date-format", CONFIGSET_SKIN },
9392
{ "timeline-default-style", CONFIGSET_SKIN },
9493
{ "timeline-dwelltime", CONFIGSET_SKIN },
9594
{ "timeline-closetime", CONFIGSET_SKIN },
9695
{ "timeline-hard-newlines", CONFIGSET_SKIN },
9796
--- 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 @@
639639
fossil_warning("%s", blob_str(&msg));
640640
blob_reset(&msg);
641641
}
642642
643643
/*
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.
648649
*/
649650
static void fossil_init_flags_from_options(void){
650651
const char *zValue = find_option("comfmtflags", 0, 1);
651652
if( zValue==0 ){
652653
zValue = find_option("comment-format", 0, 1);
@@ -725,14 +726,14 @@
725726
fossil_limit_memory(1);
726727
727728
/* When updating the minimum SQLite version, change the number here,
728729
** and also MINIMUM_SQLITE_VERSION value set in ../auto.def. Take
729730
** 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
732733
){
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",
734735
sqlite3_libversion());
735736
}
736737
737738
sqlite3_config(SQLITE_CONFIG_MULTITHREAD);
738739
sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0);
@@ -997,15 +998,12 @@
997998
998999
/*
9991000
** Remove n elements from g.argv beginning with the i-th element.
10001001
*/
10011002
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;
10071005
}
10081006
10091007
10101008
/*
10111009
** Look for a command-line option. If present, remove it from the
@@ -1064,10 +1062,19 @@
10641062
break;
10651063
}
10661064
}
10671065
return zReturn;
10681066
}
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
+}
10691076
10701077
/* Return true if zOption exists in the command-line arguments,
10711078
** but do not remove it from the list or otherwise process it.
10721079
*/
10731080
int has_option(const char *zOption){
@@ -1848,10 +1855,15 @@
18481855
** not exist.
18491856
*/
18501857
zCleanRepo = file_cleanup_fullpath(zRepo);
18511858
if( szFile==0 && sqlite3_strglob("*/.fossil",zRepo)!=0 ){
18521859
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
+ }
18531865
if( g.fHttpTrace ){
18541866
sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", szFile);
18551867
@ <!-- file_size(%h(zCleanRepo)) is %s(zBuf) -->
18561868
fprintf(stderr, "# file_size(%s) = %s\n", zCleanRepo, zBuf);
18571869
}
@@ -2155,11 +2167,11 @@
21552167
}
21562168
}
21572169
#endif
21582170
if( (pCmd->eCmdFlags & CMDFLAG_RAWCONTENT)==0 ){
21592171
cgi_decode_post_parameters();
2160
- if( !cgi_same_origin() ){
2172
+ if( !cgi_same_origin(0) ){
21612173
isReadonly = 1;
21622174
db_protect(PROTECT_READONLY);
21632175
}
21642176
}
21652177
if( g.fCgiTrace ){
@@ -2398,11 +2410,11 @@
23982410
** The lines are processed in the order they are read, which is most
23992411
** significant for "errorlog:", which should be set before "repository:"
24002412
** so that any warnings from the database when opening the repository
24012413
** go to that log file.
24022414
**
2403
-** See also: [[http]], [[server]], [[winsrv]]
2415
+** See also: [[http]], [[server]], [[winsrv]] [Windows only]
24042416
*/
24052417
void cmd_cgi(void){
24062418
const char *zNotFound = 0;
24072419
char **azRedirect = 0; /* List of repositories to redirect to */
24082420
int nRedirect = 0; /* Number of entries in azRedirect */
@@ -2530,12 +2542,16 @@
25302542
** setenv: NAME
25312543
**
25322544
** Sets environment variable NAME to VALUE. If VALUE is omitted, then
25332545
** the environment variable is unset.
25342546
*/
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);
25372553
blob_reset(&value);
25382554
blob_reset(&value2);
25392555
continue;
25402556
}
25412557
if( blob_eq(&key, "errorlog:") && blob_token(&line, &value) ){
@@ -2857,11 +2873,11 @@
28572873
** to force use of the current local skin config.
28582874
** --th-trace Trace TH1 execution (for debugging purposes)
28592875
** --usepidkey Use saved encryption key from parent process. This is
28602876
** only necessary when using SEE on Windows or Linux.
28612877
**
2862
-** See also: [[cgi]], [[server]], [[winsrv]]
2878
+** See also: [[cgi]], [[server]], [[winsrv]] [Windows only]
28632879
*/
28642880
void cmd_http(void){
28652881
const char *zIpAddr = 0;
28662882
const char *zNotFound;
28672883
const char *zHost;
@@ -3188,10 +3204,13 @@
31883204
** --chroot DIR Use directory for chroot instead of repository path
31893205
** --ckout-alias NAME Treat URIs of the form /doc/NAME/... as if they were
31903206
** /doc/ckout/...
31913207
** --create Create a new REPOSITORY if it does not already exist
31923208
** --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".
31933212
** --extroot DIR Document root for the /ext extension mechanism
31943213
** --files GLOBLIST Comma-separated list of glob patterns for static files
31953214
** --fossilcmd PATH The pathname of the "fossil" executable on the remote
31963215
** system when REPOSITORY is remote.
31973216
** --from PATH Use PATH as the diff baseline for the /ckout page
@@ -3239,11 +3258,11 @@
32393258
** user and group.
32403259
** --th-trace Trace TH1 execution (for debugging purposes)
32413260
** --usepidkey Use saved encryption key from parent process. This is
32423261
** only necessary when using SEE on Windows or Linux.
32433262
**
3244
-** See also: [[cgi]], [[http]], [[winsrv]]
3263
+** See also: [[cgi]], [[http]], [[winsrv]] [Windows only]
32453264
*/
32463265
void cmd_webserver(void){
32473266
int iPort, mxPort; /* Range of TCP ports allowed */
32483267
const char *zPort; /* Value of the --port option */
32493268
const char *zBrowser; /* Name of web browser program */
@@ -3266,10 +3285,11 @@
32663285
int findServerArg = 2; /* argv index for find_server_repository() */
32673286
char *zRemote = 0; /* Remote host on which to run "fossil ui" */
32683287
const char *zJsMode; /* The --jsmode parameter */
32693288
const char *zFossilCmd =0; /* Name of "fossil" binary on remote system */
32703289
const char *zFrom; /* Value for --from */
3290
+ const char *zExtPage = 0; /* Argument to --extpage */
32713291
32723292
32733293
#if USE_SEE
32743294
db_setup_for_saved_encryption_key();
32753295
#endif
@@ -3307,12 +3327,20 @@
33073327
zFrom = find_option("from", 0, 1);
33083328
if( zFrom && zFrom==file_tail(zFrom) ){
33093329
fossil_fatal("the argument to --from must be a pathname for"
33103330
" the \"ui\" command");
33113331
}
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
+ }
33143342
zFossilCmd = find_option("fossilcmd", 0, 1);
33153343
if( zFrom && zInitPage==0 ){
33163344
zInitPage = mprintf("ckout?exbase=%H", zFrom);
33173345
}
33183346
}
@@ -3481,11 +3509,18 @@
34813509
}
34823510
blob_appendf(&ssh, " ui --nobrowser --localauth --port %d", iPort);
34833511
if( zNotFound ) blob_appendf(&ssh, " --notfound %!$", zNotFound);
34843512
if( zFileGlob ) blob_appendf(&ssh, " --files-urlenc %T", zFileGlob);
34853513
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
+ }
34873522
if( skin_in_use() ) blob_appendf(&ssh, " --skin %s", skin_in_use());
34883523
if( zJsMode ) blob_appendf(&ssh, " --jsmode %s", zJsMode);
34893524
if( fCreate ) blob_appendf(&ssh, " --create");
34903525
blob_appendf(&ssh, " %$", g.argv[2]);
34913526
if( isRetry ){
@@ -3599,15 +3634,14 @@
35993634
g.httpSSLConn = 0;
36003635
}
36013636
#endif /* FOSSIL_ENABLE_SSL */
36023637
36033638
#else /* WIN32 */
3604
- find_server_repository(2, 0);
3639
+ /* Win32 implementation */
36053640
if( fossil_strcmp(g.zRepositoryName,"/")==0 ){
36063641
allowRepoList = 1;
36073642
}
3608
- /* Win32 implementation */
36093643
if( allowRepoList ){
36103644
flags |= HTTP_SERVER_REPOLIST;
36113645
}
36123646
if( win32_http_service(iPort, zAltBase, zNotFound, zFileGlob, flags) ){
36133647
win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile,
36143648
--- 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 @@
21342134
$(XTCC) $(PIKCHR_OPTIONS) -c $(SRCDIR_extsrc)/pikchr.c -o $@
21352135
21362136
$(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c
21372137
$(XTCC) -c $(SRCDIR_extsrc)/cson_amalgamation.c -o $@
21382138
2139
-$(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c
2139
+$(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c $(MAKEFILE_LIST)
21402140
$(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 \
21432143
-sENVIRONMENT=web \
21442144
-sMODULARIZE \
21452145
-sEXPORT_NAME=initPikchrModule \
21462146
--minify 0
2147
+ $(TCLSH) $(TOPDIR)/tools/randomize-js-names.tcl $(SRCDIR_extsrc)
21472148
@chmod -x $(SRCDIR_extsrc)/pikchr.wasm
21482149
wasm: $(SRCDIR_extsrc)/pikchr.js
21492150
21502151
#
21512152
# compile_commands.json support...
21522153
--- 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
--- src/manifest.c
+++ src/manifest.c
@@ -2911,5 +2911,300 @@
29112911
if( g.argc!=3 ) usage("RECORDID");
29122912
rid = name_to_rid(g.argv[2]);
29132913
content_get(rid, &content);
29142914
manifest_crosslink(rid, &content, MC_NONE);
29152915
}
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
+}
29163211
--- 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
--- src/markdown_html.c
+++ src/markdown_html.c
@@ -911,5 +911,62 @@
911911
html_renderer.opaque = &context;
912912
if( output_title ) blob_reset(output_title);
913913
blob_reset(output_body);
914914
markdown(output_body, input_markdown, &html_renderer);
915915
}
916
+
917
+/*
918
+** Undo HTML escapes in Blob p. In other words convert:
919
+**
920
+** &amp; -> &
921
+** &lt; -> <
922
+** &gt; -> >
923
+** &quot; -> "
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],"&lt;",4)==0 ){
952
+ z[k++] = '<';
953
+ j += 3;
954
+ }else if( memcmp(&z[j],"&gt;",4)==0 ){
955
+ z[k++] = '>';
956
+ j += 3;
957
+ }else if( memcmp(&z[j],"&quot;",6)==0 ){
958
+ z[k++] = '"';
959
+ j += 5;
960
+ }else if( memcmp(&z[j],"&amp;",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
+}
916973
--- 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 ** &amp; -> &
921 ** &lt; -> <
922 ** &gt; -> >
923 ** &quot; -> "
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],"&lt;",4)==0 ){
952 z[k++] = '<';
953 j += 3;
954 }else if( memcmp(&z[j],"&gt;",4)==0 ){
955 z[k++] = '>';
956 j += 3;
957 }else if( memcmp(&z[j],"&quot;",6)==0 ){
958 z[k++] = '"';
959 j += 5;
960 }else if( memcmp(&z[j],"&amp;",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 @@
5858
** Check to see if the string might be a compact date/time that omits
5959
** the punctuation. Example: "20190327084549" instead of
6060
** "2019-03-27 08:45:49". If the string is of the appropriate form,
6161
** then return an alternative string (in static space) that is the same
6262
** 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
6376
**
6477
** 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
6679
** bVerifyNotAHash flag is false, then the result is determined by syntax
6780
** of the input string only, without reference to the artifact table.
6881
*/
69
-char *fossil_expand_datetime(const char *zIn, int bVerifyNotAHash){
82
+char *fossil_expand_datetime(const char *zIn,int bVerifyNotAHash,int bRoundUp){
7083
static char zEDate[24];
7184
static const char aPunct[] = { 0, 0, '-', '-', ' ', ':', ':' };
7285
int n = (int)strlen(zIn);
7386
int i, j;
7487
int addZulu = 0;
7588
7689
/* These forms are allowed:
7790
**
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
8295
**
8396
** An optional "Z" zulu timezone designator is allowed at the end.
8497
*/
8598
if( n>0 && (zIn[n-1]=='Z' || zIn[n-1]=='z') ){
8699
n--;
@@ -99,15 +112,23 @@
99112
if( i>=4 && (i%2)==0 ){
100113
zEDate[j++] = aPunct[i/2];
101114
}
102115
zEDate[j++] = zIn[i];
103116
}
104
- if( addZulu ){
117
+ if( bRoundUp ){
105118
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;
108127
}
128
+ }
129
+ if( addZulu ){
109130
zEDate[j++] = 'Z';
110131
}
111132
zEDate[j] = 0;
112133
113134
/* Check for reasonable date values.
@@ -147,27 +168,40 @@
147168
** comparison. So add in missing factional seconds or seconds or time.
148169
**
149170
** The returned string is held in a static buffer that is overwritten
150171
** with each call, or else is just a copy of its input if there are
151172
** no changes.
173
+**
174
+** For reference:
175
+**
176
+** 0123456789 123456789 1234
177
+** YYYY-MM-DD HH:MM:SS.SSSz
152178
*/
153179
const char *fossil_roundup_date(const char *zDate){
154
- static char zUp[24];
180
+ static char zUp[28];
155181
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
+ }
156187
if( n==19 ){ /* YYYY-MM-DD HH:MM:SS */
157188
memcpy(zUp, zDate, 19);
158
- memcpy(zUp+19, ".999", 5);
189
+ memcpy(zUp+19, ".999z", 6);
190
+ if( !addZ ) zUp[23] = 0;
159191
return zUp;
160192
}
161193
if( n==16 ){ /* YYYY-MM-DD HH:MM */
162194
memcpy(zUp, zDate, 16);
163
- memcpy(zUp+16, ":59.999", 8);
195
+ memcpy(zUp+16, ":59.999z", 8);
196
+ if( !addZ ) zUp[23] = 0;
164197
return zUp;
165198
}
166199
if( n==10 ){ /* YYYY-MM-DD */
167200
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;
169203
return zUp;
170204
}
171205
return zDate;
172206
}
173207
@@ -231,11 +265,11 @@
231265
** This is a tricky query to do efficiently.
232266
** If the tag is very common (ex: "trunk") then
233267
** we want to use the query identified below as Q1 - which searches
234268
** the most recent EVENT table entries for the most recent with the tag.
235269
** 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.
237271
*/
238272
static int most_recent_event_with_tag(const char *zTag, const char *zType){
239273
return db_int(0,
240274
"SELECT objid FROM ("
241275
/* Q1: Begin by looking for the tag in the 30 most recent events */
@@ -479,11 +513,11 @@
479513
if( rid ) return rid;
480514
}
481515
482516
/* Date and times */
483517
if( memcmp(zTag, "date:", 5)==0 ){
484
- zDate = fossil_expand_datetime(&zTag[5],0);
518
+ zDate = fossil_expand_datetime(&zTag[5],0,1);
485519
if( zDate==0 ) zDate = &zTag[5];
486520
rid = db_int(0,
487521
"SELECT objid FROM event"
488522
" WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'"
489523
" ORDER BY mtime DESC LIMIT 1",
@@ -545,21 +579,21 @@
545579
546580
/* symbolic-name ":" date-time */
547581
nTag = strlen(zTag);
548582
for(i=0; i<nTag-8 && zTag[i]!=':'; i++){}
549583
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)
551585
){
552586
char *zDate = mprintf("%s", &zTag[i+1]);
553587
char *zTagBase = mprintf("%.*s", i, zTag);
554588
char *zXDate;
555589
int nDate = strlen(zDate);
556590
if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){
557591
zDate[nDate-3] = 'z';
558592
zDate[nDate-2] = 0;
559593
}
560
- zXDate = fossil_expand_datetime(zDate,0);
594
+ zXDate = fossil_expand_datetime(zDate,0,1);
561595
if( zXDate==0 ) zXDate = zDate;
562596
rid = db_int(0,
563597
"SELECT event.objid, max(event.mtime)"
564598
" FROM tag, tagxref, event"
565599
" WHERE tag.tagname='sym-%q' "
@@ -631,11 +665,11 @@
631665
if( startOfBranch ) rid = start_of_branch(rid,1);
632666
return rid;
633667
}
634668
635669
/* Pure numeric date/time */
636
- zDate = fossil_expand_datetime(zTag, 0);
670
+ zDate = fossil_expand_datetime(zTag, 0,1);
637671
if( zDate ){
638672
rid = db_int(0,
639673
"SELECT objid FROM event"
640674
" WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'"
641675
" ORDER BY mtime DESC LIMIT 1",
@@ -684,10 +718,65 @@
684718
rid = symbolic_name_to_rid(zNew,zType);
685719
fossil_free(zNew);
686720
}
687721
return rid;
688722
}
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
+}
689778
690779
/*
691780
** This routine takes a user-entered string and tries to convert it to
692781
** an artifact hash.
693782
**
694783
--- 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 @@
698698
fossil_fatal("cannot change to directory \"%s\"", zDir);
699699
}
700700
fossil_free(zToFree);
701701
return zPatchFile;
702702
}
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
+}
703719
704720
/*
705721
** Create a FILE* that will execute the remote side of a push or pull
706722
** using ssh (probably) or fossil for local pushes and pulls. Return
707723
** a FILE* obtained from popen() into which we write the patch, or from
@@ -729,11 +745,11 @@
729745
if( mFlags & PATCH_DRYRUN ) blob_appendf(&flgs, " -n");
730746
zForce = blob_size(&flgs)>0 ? blob_str(&flgs) : "";
731747
if( g.argc!=4 ){
732748
usage(mprintf("%s [USER@]HOST:DIRECTORY", zThisCmd));
733749
}
734
- zRemote = fossil_strdup(g.argv[3]);
750
+ zRemote = patch_resolve_remote(g.argv[3]);
735751
zDir = (char*)file_skip_userhost(zRemote);
736752
if( zDir==0 ){
737753
if( isRetry ) goto remote_command_error;
738754
zDir = zRemote;
739755
blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
@@ -778,11 +794,11 @@
778794
/*
779795
** Toggle the use-path-for-ssh setting for the remote host defined
780796
** by g.argv[3].
781797
*/
782798
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]);
784800
char *zDir = (char*)file_skip_userhost(zRemote);
785801
if( zDir ){
786802
*(char*)(zDir - 1) = 0;
787803
ssh_needs_path_argument(zRemote, 99);
788804
}
@@ -905,11 +921,10 @@
905921
db_finalize(&q);
906922
diff_end(pCfg, nErr);
907923
if( nErr ) fossil_fatal("abort due to prior errors");
908924
}
909925
910
-
911926
/*
912927
** COMMAND: patch
913928
**
914929
** Usage: %fossil patch SUBCOMMAND ?ARGS ..?
915930
**
@@ -916,10 +931,22 @@
916931
** This command is used to create, view, and apply Fossil binary patches.
917932
** A Fossil binary patch is a single (binary) file that captures all of the
918933
** uncommitted changes of a check-out. Use Fossil binary patches to transfer
919934
** proposed or incomplete changes between machines for testing or analysis.
920935
**
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
+**
921948
** > fossil patch create [DIRECTORY] PATCHFILE
922949
**
923950
** Create a new binary patch in PATCHFILE that captures all uncommitted
924951
** changes in the check-out at DIRECTORY, or the current directory if
925952
** DIRECTORY is omitted. If PATCHFILE is "-" then the binary patch
@@ -996,14 +1023,76 @@
9961023
void patch_cmd(void){
9971024
const char *zCmd;
9981025
size_t n;
9991026
if( g.argc<3 ){
10001027
patch_usage:
1001
- usage("apply|create|diff|gdiff|pull|push|view");
1028
+ usage("alias|apply|create|diff|gdiff|pull|push|view");
10021029
}
10031030
zCmd = g.argv[2];
10041031
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
10051094
if( strncmp(zCmd, "apply", n)==0 ){
10061095
char *zIn;
10071096
unsigned flags = 0;
10081097
if( find_option("dry-run","n",0) ) flags |= PATCH_DRYRUN;
10091098
if( find_option("verbose","v",0) ) flags |= PATCH_VERBOSE;
@@ -1033,14 +1122,18 @@
10331122
db_close(0);
10341123
diff_tk("patch diff", 3);
10351124
return;
10361125
}
10371126
db_find_and_open_repository(0, 0);
1127
+ if( gdiff_using_tk(zCmd[0]=='g') ){
1128
+ diff_tk("patch diff", 3);
1129
+ return;
1130
+ }
10381131
if( find_option("force","f",0) ) flags |= PATCH_FORCE;
10391132
diff_options(&DCfg, zCmd[0]=='g', 0);
10401133
verify_all_options();
1041
- zIn = patch_find_patch_filename("apply");
1134
+ zIn = patch_find_patch_filename("diff");
10421135
patch_attach(zIn, stdin, 0);
10431136
patch_diff(flags, &DCfg);
10441137
fossil_free(zIn);
10451138
}else
10461139
if( strncmp(zCmd, "pull", n)==0 ){
10471140
--- 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 @@
1818
** directed acyclic graph (DAG) of check-ins.
1919
*/
2020
#include "config.h"
2121
#include "path.h"
2222
#include <assert.h>
23
+#include <math.h>
2324
2425
#if INTERFACE
2526
/* Nodes for the paths through the DAG.
2627
*/
2728
struct PathNode {
2829
int rid; /* ID for this node */
2930
u8 fromIsParent; /* True if pFrom is the parent of rid */
3031
u8 isPrim; /* True if primary side of common ancestor */
3132
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 */
3235
PathNode *pFrom; /* Node we came from */
3336
union {
34
- PathNode *pPeer; /* List of nodes of the same generation */
37
+ double rCost; /* Cost of getting to this node from pStart */
3538
PathNode *pTo; /* Next on path from beginning to end */
3639
} u;
37
- PathNode *pAll; /* List of all nodes */
40
+ PathNode *pAll; /* List of all nodes */
3841
};
3942
#endif
4043
4144
/*
4245
** Local variables for this module
4346
*/
4447
static struct {
45
- PathNode *pCurrent; /* Current generation of nodes */
48
+ PQueue pending; /* Nodes pending review for inclusion in the graph */
4649
PathNode *pAll; /* All nodes */
47
- Bag seen; /* Nodes seen before */
4850
int nStep; /* Number of steps from first to last */
4951
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 */
5054
PathNode *pStart; /* Earliest node */
5155
PathNode *pEnd; /* Most recent */
5256
} path;
57
+static int path_debug = 0; /* Flag to enable debugging */
5358
5459
/*
5560
** Return the first (last) element of the computed path.
5661
*/
5762
PathNode *path_first(void){ return path.pStart; }
@@ -66,25 +71,71 @@
6671
** Return the number of non-hidden steps in the computed path.
6772
*/
6873
int path_length_not_hidden(void){ return path.nNotHidden; }
6974
7075
/*
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.
72105
*/
73106
static PathNode *path_new_node(int rid, PathNode *pFrom, int isParent){
74107
PathNode *p;
75108
76109
p = fossil_malloc( sizeof(*p) );
77110
memset(p, 0, sizeof(*p));
111
+ p->pAll = path.pAll;
112
+ path.pAll = p;
78113
p->rid = rid;
79114
p->fromIsParent = isParent;
80115
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);
86137
return p;
87138
}
88139
89140
/*
90141
** Reset memory used by the shortest path algorithm.
@@ -92,13 +143,14 @@
92143
void path_reset(void){
93144
PathNode *p;
94145
while( path.pAll ){
95146
p = path.pAll;
96147
path.pAll = p->pAll;
148
+ fossil_free(p->zBranch);
97149
fossil_free(p);
98150
}
99
- bag_clear(&path.seen);
151
+ pqueuex_clear(&path.pending);
100152
memset(&path, 0, sizeof(path));
101153
}
102154
103155
/*
104156
** Construct the path from path.pStart to path.pEnd in the u.pTo fields.
@@ -128,17 +180,19 @@
128180
PathNode *path_shortest(
129181
int iFrom, /* Path starts here */
130182
int iTo, /* Path ends here */
131183
int directOnly, /* No merge links if true */
132184
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 */
134187
){
135188
Stmt s;
136
- PathNode *pPrev;
189
+ Bag seen;
137190
PathNode *p;
138191
139192
path_reset();
193
+ path.brCost = branchCost;
140194
path.pStart = path_new_node(iFrom, 0, 0);
141195
if( iTo==iFrom ){
142196
path.pEnd = path.pStart;
143197
return path.pStart;
144198
}
@@ -152,44 +206,46 @@
152206
);
153207
}else if( directOnly ){
154208
db_prepare(&s,
155209
"SELECT cid, 1 FROM plink WHERE pid=:pid AND isprim "
156210
"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"
158212
);
159213
}else{
160214
db_prepare(&s,
161215
"SELECT cid, 1 FROM plink WHERE pid=:pid "
162216
"UNION ALL "
163
- "SELECT pid, 0 FROM plink WHERE cid=:pid"
217
+ "SELECT pid, 0 FROM plink WHERE :back AND cid=:pid"
164218
);
165219
}
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);
191247
}
192248
db_finalize(&s);
193249
path_reset();
194250
return 0;
195251
}
@@ -215,10 +271,22 @@
215271
PathNode *p;
216272
p = path.pStart;
217273
if( p ) p = p->u.pTo;
218274
return p;
219275
}
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
+}
220288
221289
/*
222290
** Return an estimate of the number of comparisons remaining in order
223291
** to bisect path. This is based on the log2() of path.nStep.
224292
*/
@@ -238,11 +306,11 @@
238306
int cid /* RID for check-in at the end of the path */
239307
){
240308
PathNode *pPath;
241309
int gen = 0;
242310
Stmt ins;
243
- pPath = path_shortest(cid, origid, 1, 0, 0);
311
+ pPath = path_shortest(cid, origid, 1, 0, 0, 0);
244312
db_multi_exec(
245313
"CREATE TEMP TABLE IF NOT EXISTS ancestor("
246314
" rid INT UNIQUE,"
247315
" generation INTEGER PRIMARY KEY"
248316
");"
@@ -261,58 +329,55 @@
261329
}
262330
263331
/*
264332
** COMMAND: test-shortest-path
265333
**
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:
267337
**
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.
270343
*/
271344
void shortest_path_test_cmd(void){
272345
int iFrom;
273346
int iTo;
274347
PathNode *p;
275348
int n;
276349
int directOnly;
277350
int oneWay;
351
+ const char *zBrCost;
278352
279353
db_find_and_open_repository(0,0);
280354
directOnly = find_option("no-merge",0,0)!=0;
281355
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;
282358
if( g.argc!=4 ) usage("VERSION1 VERSION2");
283359
iFrom = name_to_rid(g.argv[2]);
284360
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);
286363
if( p==0 ){
287364
fossil_fatal("no path from %s to %s", g.argv[1], g.argv[2]);
288365
}
289366
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;
305370
}
306371
307372
/*
308373
** Find the closest common ancestor of two nodes. "Closest" means the
309374
** fewest number of arcs.
310375
*/
311376
int path_common_ancestor(int iMe, int iYou){
312377
Stmt s;
313
- PathNode *pPrev;
378
+ PathNode *pThis;
314379
PathNode *p;
315380
Bag me, you;
316381
317382
if( iMe==iYou ) return iMe;
318383
if( iMe==0 || iYou==0 ) return 0;
@@ -323,45 +388,40 @@
323388
db_prepare(&s, "SELECT pid FROM plink WHERE cid=:cid");
324389
bag_init(&me);
325390
bag_insert(&me, iMe);
326391
bag_init(&you);
327392
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);
363423
}
364424
db_finalize(&s);
365425
path_reset();
366426
return 0;
367427
}
@@ -453,11 +513,11 @@
453513
}else if(0==iTo){
454514
fossil_fatal("Invalid 'to' RID: 0");
455515
}
456516
if( iFrom==iTo ) return;
457517
path_reset();
458
- p = path_shortest(iFrom, iTo, 1, revOK==0, 0);
518
+ p = path_shortest(iFrom, iTo, 1, revOK==0, 0, 0);
459519
if( p==0 ) return;
460520
path_reverse_path();
461521
db_prepare(&q1,
462522
"SELECT pfnid, fnid FROM mlink"
463523
" WHERE mid=:mid AND (pfnid>0 OR fid==0)"
464524
--- 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 @@
1515
**
1616
*******************************************************************************
1717
**
1818
** This file contains code used to implement a priority queue.
1919
** 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).
2231
**
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.
2734
**
2835
** Compatibility note: Some versions of OpenSSL export a symbols
2936
** like "pqueue_insert". This is, technically, a bug in OpenSSL.
3037
** We work around it here by using "pqueuex_" instead of "pqueue_".
3138
*/
@@ -41,11 +48,14 @@
4148
*/
4249
struct PQueue {
4350
int cnt; /* Number of entries in the queue */
4451
int sz; /* Number of slots in a[] */
4552
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;
4757
double value; /* Value of element. Kept in ascending order */
4858
} *a;
4959
};
5060
#endif
5161
@@ -69,44 +79,154 @@
6979
*/
7080
static void pqueuex_resize(PQueue *p, int N){
7181
p->a = fossil_realloc(p->a, sizeof(p->a[0])*N);
7282
p->sz = N;
7383
}
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
+}
74110
75111
/*
76112
** Insert element e into the queue.
77113
*/
78114
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){
79132
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
+ }
94146
}
95147
96148
/*
97149
** Extract the first element from the queue (the element with
98150
** the smallest value) and return its ID. Return 0 if the queue
99151
** is empty.
100152
*/
101153
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;
103164
if( p->cnt==0 ){
104165
return 0;
105166
}
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);
109230
}
110
- p->cnt--;
111
- return e;
231
+ pqueuex_clear(&x);
112232
}
113233
--- 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 @@
240240
while( (N-- != 0) && *(z++)!=0 ){ n++; }
241241
return n;
242242
}
243243
#endif
244244
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
+
245262
/*
246263
** Return an appropriate set of flags for wiki_convert() for displaying
247264
** comments on a timeline. These flag settings are determined by
248265
** configuration parameters.
249266
**
250267
** 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.
254273
*/
255
-static int wiki_convert_flags(int altForm2){
274
+int wiki_convert_flags(int altForm2){
256275
static int wikiFlags = 0;
276
+ (void)altForm2;
257277
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;
263279
if( db_get_boolean("timeline-plaintext", 0) ){
264280
wikiFlags |= WIKI_LINKSONLY;
265281
}
266282
if( db_get_boolean("timeline-hard-newlines", 0) ){
267283
wikiFlags |= WIKI_NEWLINE;
268284
--- 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 @@
14141414
}
14151415
14161416
/*
14171417
** COMMAND: deconstruct*
14181418
**
1419
-** Usage %fossil deconstruct ?OPTIONS? DESTINATION
1419
+** Usage: %fossil deconstruct ?OPTIONS? DESTINATION
14201420
**
14211421
** This command exports all artifacts of a given repository and writes all
14221422
** artifacts to the file system. The DESTINATION directory will be populated
14231423
** with subdirectories AA and files AA/BBBBBBBBB.., where AABBBBBBBBB.. is the
14241424
** 40+ character artifact ID, AA the first 2 characters.
14251425
--- 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 @@
682682
if( j>0 && pRe->zInit[j-1]==0 ) j--;
683683
pRe->nInit = j;
684684
}
685685
return pRe->zErr;
686686
}
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
+}
687726
688727
/*
689728
** Implementation of the regexp() SQL function. This function implements
690729
** the build-in REGEXP operator. The first argument to the function is the
691730
** pattern and the second argument is the string. So, the SQL statements:
692731
--- 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 @@
316316
style_header("Repository List");
317317
@ %s(blob_str(&html))
318318
style_table_sorter();
319319
style_finish_page();
320320
}else{
321
+ const char *zTitle = PD("FOSSIL_REPOLIST_TITLE","Repository List");
321322
/* If no repositories were found that had the "repolist_skin"
322323
** property set, then use a default skin */
323324
@ <html>
324325
@ <head>
325326
@ <base href="%s(g.zBaseURL)/">
326327
@ <meta name="viewport" content="width=device-width, initial-scale=1.0">
327
- @ <title>Repository List</title>
328
+ @ <title>%h(zTitle)</title>
328329
@ </head>
329330
@ <body>
330
- @ <h1 align="center">Fossil Repositories</h1>
331
+ @ <h1 align="center">%h(zTitle)</h1>
331332
@ %s(blob_str(&html))
332333
@ <script>%s(builtin_text("sorttable.js"))</script>
333334
@ </body>
334335
@ </html>
335336
}
336337
--- 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 @@
511511
** Predefined tagid values
512512
*/
513513
#if INTERFACE
514514
# define TAG_BGCOLOR 1 /* Set the background color for display */
515515
# 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 */
517517
# define TAG_DATE 4 /* The date of a check-in */
518518
# define TAG_HIDDEN 5 /* Do not display in timeline */
519519
# define TAG_PRIVATE 6 /* Do not sync */
520520
# define TAG_CLUSTER 7 /* A cluster */
521521
# define TAG_BRANCH 8 /* Value is name of the current branch */
522522
--- 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 @@
564564
/*
565565
** Testing the search function.
566566
**
567567
** COMMAND: search*
568568
**
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.
585591
**
586592
** 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".
590599
** -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
593602
** -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
595605
*/
596606
void search_cmd(void){
597607
Blob pattern;
598608
int i;
599609
Blob sql = empty_blob;
600610
Stmt q;
601611
int iBest;
612
+ int srchFlags = 0;
613
+ int bFts = 1; /* Use FTS search by default now */
602614
char fAll = NULL != find_option("all", "a", 0);
603615
const char *zLimit = find_option("limit","n",1);
616
+ const char *zScope = 0;
604617
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 */
607619
int nLimit = zLimit ? atoi(zLimit) : -1000;
608620
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 */
610635
611636
if( zWidth ){
612637
width = atoi(zWidth);
613638
if( (width!=0) && (width<=20) ){
614639
fossil_fatal("-W|--width value must be >20 or 0");
615640
}
616641
}else{
617642
width = -1;
618643
}
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
+
619671
620672
db_find_and_open_repository(0, 0);
673
+ verify_all_options();
621674
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
+
622692
blob_init(&pattern, g.argv[2], -1);
623693
for(i=3; i<g.argc; i++){
624694
blob_appendf(&pattern, " %s", g.argv[i]);
625695
}
626696
if( bFts ){
627697
/* Search using FTS */
628698
Blob com;
629699
Blob snip;
630700
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
- }
649701
search_sql_setup(g.db);
650702
add_content_sql_commands(g.db);
651703
db_multi_exec(
652704
"CREATE TEMP TABLE x(label,url,score,id,date,snip);"
653705
);
@@ -654,30 +706,32 @@
654706
if( !search_index_exists() ){
655707
search_fullscan(zPattern, srchFlags); /* Full-scan search */
656708
}else{
657709
search_update_index(srchFlags); /* Update the index */
658710
search_indexed(zPattern, srchFlags); /* Indexed search */
711
+ if( srchFlags & SRCH_HELP ){
712
+ search_fullscan(zPattern, SRCH_HELP);
713
+ }
659714
}
660715
db_prepare(&q, "SELECT snip, label, score, id, date"
661716
" FROM x"
662717
" ORDER BY score DESC, date DESC;");
663718
blob_init(&com, 0, 0);
664719
blob_init(&snip, 0, 0);
665
- if( width<0 ) width = 80;
720
+ if( width<0 ) width = terminal_get_width(80);
666721
while( db_step(&q)==SQLITE_ROW ){
667722
const char *zSnippet = db_column_text(&q, 0);
668723
const char *zLabel = db_column_text(&q, 1);
669724
const char *zDate = db_column_text(&q, 4);
670725
const char *zScore = db_column_text(&q, 2);
671726
const char *zId = db_column_text(&q, 3);
727
+ char *zOrig;
672728
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);
679733
blob_appendf(&com, "%s\n%s\n%s", zLabel, blob_str(&snip), zDate);
680734
if( bDebug ){
681735
blob_appendf(&com," score: %s id: %s", zScore, zId);
682736
}
683737
comment_print(blob_str(&com), 0, 5, width,
@@ -731,28 +785,33 @@
731785
#define SRCH_DOC 0x0002 /* Search over embedded documents */
732786
#define SRCH_TKT 0x0004 /* Search over tickets */
733787
#define SRCH_WIKI 0x0008 /* Search over wiki */
734788
#define SRCH_TECHNOTE 0x0010 /* Search over tech notes */
735789
#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 */
737792
#endif
738793
739794
/*
740795
** Remove bits from srchFlags which are disallowed by either the
741796
** current server configuration or by user permissions. Return
742797
** 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.
743801
*/
744802
unsigned int search_restrict(unsigned int srchFlags){
745803
static unsigned int knownGood = 0;
746804
static unsigned int knownBad = 0;
747805
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" },
752810
{ SRCH_TECHNOTE, "search-technote" },
753
- { SRCH_FORUM, "search-forum" },
811
+ { SRCH_FORUM, "search-forum" },
812
+ { SRCH_HELP, "search-help" },
754813
};
755814
int i;
756815
if( g.perm.Read==0 ) srchFlags &= ~(SRCH_CKIN|SRCH_DOC|SRCH_TECHNOTE);
757816
if( g.perm.RdTkt==0 ) srchFlags &= ~(SRCH_TKT);
758817
if( g.perm.RdWiki==0 ) srchFlags &= ~(SRCH_WIKI);
@@ -912,10 +971,31 @@
912971
" search_snippet()"
913972
" FROM event JOIN blob on event.objid=blob.rid"
914973
" WHERE search_match('',body('f',rid,NULL));"
915974
);
916975
}
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
+ }
917997
}
918998
919999
/*
9201000
** Number of significant bits in a u32
9211001
*/
@@ -1068,10 +1148,11 @@
10681148
{ SRCH_DOC, 'd' },
10691149
{ SRCH_TKT, 't' },
10701150
{ SRCH_WIKI, 'w' },
10711151
{ SRCH_TECHNOTE, 'e' },
10721152
{ SRCH_FORUM, 'f' },
1153
+ { SRCH_HELP, 'h' },
10731154
};
10741155
int i;
10751156
for(i=0; i<count(aMask); i++){
10761157
if( srchFlags & aMask[i].m ){
10771158
blob_appendf(&sql, "%sftsdocs.type='%c'", zSep, aMask[i].c);
@@ -1157,11 +1238,11 @@
11571238
int nLimit = db_get_int("search-limit", 100);
11581239
11591240
if( P("searchlimit")!=0 ){
11601241
nLimit = atoi(P("searchlimit"));
11611242
}
1162
- srchFlags = search_restrict(srchFlags);
1243
+ srchFlags = search_restrict(srchFlags) | (srchFlags & SRCH_HELP);
11631244
if( srchFlags==0 ) return 0;
11641245
search_sql_setup(g.db);
11651246
add_content_sql_commands(g.db);
11661247
db_multi_exec(
11671248
"CREATE TEMP TABLE x(label,url,score,id,date,snip);"
@@ -1169,10 +1250,13 @@
11691250
if( !search_index_exists() ){
11701251
search_fullscan(zPattern, srchFlags); /* Full-scan search */
11711252
}else{
11721253
search_update_index(srchFlags); /* Update the index, if necessary */
11731254
search_indexed(zPattern, srchFlags); /* Indexed search */
1255
+ if( srchFlags & SRCH_HELP ){
1256
+ search_fullscan(zPattern, SRCH_HELP);
1257
+ }
11741258
}
11751259
db_prepare(&q, "SELECT url, snip, label, score, id, substr(date,1,10)"
11761260
" FROM x"
11771261
" ORDER BY score DESC, date DESC;");
11781262
while( db_step(&q)==SQLITE_ROW ){
@@ -1223,28 +1307,35 @@
12231307
**
12241308
** 0x02 Show nothing if search is disabled.
12251309
**
12261310
** Return true if there are search results.
12271311
*/
1228
-int search_screen(unsigned srchFlags, int mFlags){
1312
+int search_screen(unsigned srchAllowed, int mFlags){
12291313
const char *zType = 0;
12301314
const char *zClass = 0;
12311315
const char *zDisable1;
12321316
const char *zDisable2;
12331317
const char *zPattern;
12341318
int fDebug = PB("debug");
12351319
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 ){
12381328
case SRCH_CKIN: zType = " Check-ins"; zClass = "Ckin"; break;
12391329
case SRCH_DOC: zType = " Docs"; zClass = "Doc"; break;
12401330
case SRCH_TKT: zType = " Tickets"; zClass = "Tkt"; break;
12411331
case SRCH_WIKI: zType = " Wiki"; zClass = "Wiki"; break;
12421332
case SRCH_TECHNOTE: zType = " Tech Notes"; zClass = "Note"; break;
12431333
case SRCH_FORUM: zType = " Forum"; zClass = "Frm"; break;
1334
+ case SRCH_HELP: zType = " Help"; zClass = "Hlp"; break;
12441335
}
1245
- if( srchFlags==0 ){
1336
+ if( srchAllowed==0 ){
12461337
if( mFlags & 0x02 ) return 0;
12471338
zDisable1 = " disabled";
12481339
zDisable2 = " disabled";
12491340
zPattern = "";
12501341
}else{
@@ -1257,41 +1348,46 @@
12571348
@ <div class='searchForm searchForm%s(zClass)'>
12581349
}else{
12591350
@ <div class='searchForm'>
12601351
}
12611352
@ <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[] = {
12641360
{ "all", "All", SRCH_ALL },
12651361
{ "c", "Check-ins", SRCH_CKIN },
12661362
{ "d", "Docs", SRCH_DOC },
12671363
{ "t", "Tickets", SRCH_TKT },
12681364
{ "w", "Wiki", SRCH_WIKI },
12691365
{ "e", "Tech Notes", SRCH_TECHNOTE },
12701366
{ "f", "Forum", SRCH_FORUM },
1367
+ { "h", "Help", SRCH_HELP },
12711368
};
1272
- const char *zY = PD("y","all");
1273
- unsigned newFlags = srchFlags;
12741369
int i;
12751370
@ <select size='1' name='y'>
12761371
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;
12781375
cgi_printf("<option value='%s'", aY[i].z);
12791376
if( fossil_strcmp(zY,aY[i].z)==0 ){
1280
- newFlags &= aY[i].m;
1377
+ srchThisTime &= aY[i].m;
12811378
cgi_printf(" selected");
12821379
}
12831380
cgi_printf(">%s</option>\n", aY[i].zNm);
12841381
}
12851382
@ </select>
1286
- srchFlags = newFlags;
12871383
}
12881384
if( fDebug ){
12891385
@ <input type="hidden" name="debug" value="1">
12901386
}
12911387
@ <input type="submit" value="Search%s(zType)"%s(zDisable2)>
1292
- if( srchFlags==0 ){
1388
+ if( srchAllowed==0 && srchThisTime==0 ){
12931389
@ <p class="generalError">Search is disabled</p>
12941390
}
12951391
@ </div></form>
12961392
while( fossil_isspace(zPattern[0]) ) zPattern++;
12971393
if( zPattern[0] ){
@@ -1298,11 +1394,11 @@
12981394
if( zClass ){
12991395
@ <div class='searchResult searchResult%s(zClass)'>
13001396
}else{
13011397
@ <div class='searchResult'>
13021398
}
1303
- if( search_run_and_output(zPattern, srchFlags, fDebug)==0 ){
1399
+ if( search_run_and_output(zPattern, srchThisTime, fDebug)==0 ){
13041400
@ <p class='searchEmpty'>No matches for: <span>%h(zPattern)</span></p>
13051401
}
13061402
@ </div>
13071403
haveResult = 1;
13081404
}
@@ -1315,17 +1411,18 @@
13151411
** Search for check-in comments, documents, tickets, or wiki that
13161412
** match a user-supplied pattern.
13171413
**
13181414
** s=PATTERN Specify the full-text pattern to search for
13191415
** 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.
13271424
*/
13281425
void search_page(void){
13291426
const int isSearch = P("s")!=0;
13301427
login_check_credentials();
13311428
style_header("Search%s", isSearch ? " Results" : "");
@@ -1374,20 +1471,20 @@
13741471
}else{
13751472
blob_append(pOut, "\n", 1);
13761473
wiki_convert(pIn, &html, 0);
13771474
}
13781475
}
1379
- html_to_plaintext(blob_str(&html), pOut);
1476
+ html_to_plaintext(blob_str(&html), pOut, 0);
13801477
}else if( fossil_strcmp(zMimetype,"text/x-markdown")==0 ){
13811478
markdown_to_html(pIn, blob_size(&title) ? NULL : &title, &html);
13821479
}else if( fossil_strcmp(zMimetype,"text/html")==0 ){
13831480
if( blob_size(&title)==0 ) doc_is_embedded_html(pIn, &title);
13841481
pHtml = pIn;
13851482
}
13861483
blob_appendf(pOut, "%s\n", blob_str(&title));
13871484
if( blob_size(pHtml) ){
1388
- html_to_plaintext(blob_str(pHtml), pOut);
1485
+ html_to_plaintext(blob_str(pHtml), pOut, 0);
13891486
}else{
13901487
blob_append(pOut, blob_buffer(pIn), blob_size(pIn));
13911488
}
13921489
blob_reset(&html);
13931490
blob_reset(&title);
@@ -2108,32 +2205,32 @@
21082205
}
21092206
fossil_print(" done\n");
21102207
}
21112208
21122209
/*
2113
-** COMMAND: fts-config*
2210
+** COMMAND: fts-config* abbrv-subcom
21142211
**
21152212
** Usage: fossil fts-config ?SUBCOMMAND? ?ARGUMENT?
21162213
**
21172214
** The "fossil fts-config" command configures the full-text search capabilities
21182215
** of the repository. Subcommands:
21192216
**
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.
21352232
**
21362233
** The current search settings are displayed after any changes are applied.
21372234
** Run this command with no arguments to simply see the settings.
21382235
*/
21392236
void fts_config_cmd(void){
@@ -2150,16 +2247,17 @@
21502247
static const struct {
21512248
const char *zSetting;
21522249
const char *zName;
21532250
const char *zSw;
21542251
} 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" },
21612259
};
21622260
char *zSubCmd = 0;
21632261
int i, j, n;
21642262
int iCmd = 0;
21652263
int iAction = 0;
@@ -2192,16 +2290,46 @@
21922290
}
21932291
db_begin_transaction();
21942292
21952293
/* Adjust search settings */
21962294
if( iCmd==3 || iCmd==4 ){
2295
+ int k;
21972296
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
+ }
22032331
}
22042332
}
22052333
}else if( iCmd==5 ){
22062334
int iOldTokenizer, iNewTokenizer;
22072335
if( g.argc<4 ) usage("tokenizer porter|on|off|trigram|unicode61");
@@ -2224,30 +2352,30 @@
22242352
search_rebuild_index();
22252353
}
22262354
22272355
/* Always show the status before ending */
22282356
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,
22302358
db_get_boolean(aSetng[i].zSetting,0) ? "on" : "off");
22312359
}
2232
- fossil_print("%-17s %s\n", "tokenizer:",
2360
+ fossil_print("%-21s %s\n", "tokenizer:",
22332361
search_tokenizer_for_string(0));
22342362
if( search_index_exists() ){
22352363
int pgsz = db_int64(0, "PRAGMA repository.page_size;");
22362364
i64 nTotal = db_int64(0, "PRAGMA repository.page_count;")*pgsz;
22372365
i64 nFts = db_int64(0, "SELECT count(*) FROM dbstat"
22382366
" WHERE schema='repository'"
22392367
" AND name LIKE 'fts%%'")*pgsz;
22402368
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:",
22432371
db_int(0, "SELECT count(*) FROM ftsdocs"));
22442372
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",
22462374
zSize, 100.0*((double)nFts/(double)nTotal));
22472375
}else{
2248
- fossil_print("%-17s disabled\n", "full-text index:");
2376
+ fossil_print("%-21s disabled\n", "full-text index:");
22492377
}
22502378
db_end_transaction(0);
22512379
}
22522380
22532381
/*
22542382
--- 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 @@
553553
@ checkbox on the <a href="setup_access">Access Control</a> page.
554554
}
555555
556556
/* Logging should be turned on
557557
*/
558
- if( db_get_boolean("access-log",0)==0 ){
558
+ if( db_get_boolean("access-log",1)==0 ){
559559
@ <li><p>
560560
@ The <a href="access_log">User Log</a> is disabled. The user log
561561
@ keeps a record of successful and unsuccessful login attempts and is
562562
@ useful for security monitoring.
563563
}
564
- if( db_get_boolean("admin-log",0)==0 ){
564
+ if( db_get_boolean("admin-log",1)==0 ){
565565
@ <li><p>
566566
@ The <a href="admin_log">Administrative Log</a> is disabled.
567567
@ The administrative log provides a record of configuration changes
568568
@ and is useful for security monitoring.
569569
}
@@ -804,37 +804,59 @@
804804
@ </pre></blockquote>
805805
blob_reset(&fullname);
806806
}
807807
}
808808
809
-/*
810
-** The maximum number of bytes of the error log to show by default.
811
-*/
812
-#define MXSHOWLOG 500000
813
-
814809
/*
815810
** WEBPAGE: errorlog
816811
**
817812
** Show the content of the error log. Only the administrator can view
818813
** 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.
819823
*/
820824
void errorlog_page(void){
821825
i64 szFile;
822826
FILE *in;
823827
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;
824838
char z[10000];
839
+ char zTime[10000];
840
+
825841
login_check_credentials();
826842
if( !g.perm.Admin ){
827843
login_needed(0);
828844
return;
829845
}
846
+ if( zType ){
847
+ eType = strtol(zType,0,0) & eAllTypes;
848
+ }
830849
style_header("Server Error Log");
831850
style_submenu_element("Test", "%R/test-warning");
832851
style_submenu_element("Refresh", "%R/errorlog");
852
+ style_submenu_element("Download", "%R/errorlog?download");
853
+ style_submenu_element("Truncate", "%R/errorlog?truncate");
833854
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
+ }
836858
837859
if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){
838860
no_error_log_available();
839861
style_finish_page();
840862
return;
@@ -861,163 +883,117 @@
861883
return;
862884
}
863885
zLog = file_canonical_name_dup(g.zErrlog);
864886
@ <p>The server error log at "%h(zLog)" is %,lld(szFile) bytes in size.
865887
fossil_free(zLog);
866
- style_submenu_element("Download", "%R/errorlog?download");
867
- style_submenu_element("Truncate", "%R/errorlog?truncate");
868888
in = fossil_fopen(g.zErrlog, "rb");
869889
if( in==0 ){
870890
@ <p class='generalError'>Unable to open that file for reading!</p>
871891
style_finish_page();
872892
return;
873893
}
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
+ }
939945
}
940946
if( strncmp(z, "--------", 8)==0 ){
941947
size_t n = strlen(z);
942948
memcpy(zTime, z, n+1);
943949
prevWasTime = 1;
944950
bOutput = 0;
945951
}else{
946952
prevWasTime = 0;
947953
}
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
+ }
1022998
style_finish_page();
1023999
}
10241000
--- 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 @@
183183
184184
185185
/*
186186
** WEBPAGE: setup-logmenu
187187
**
188
-** Show a menu of available log renderings accessible to an administrator,
188
+** Show a menu of available log renderings accessible to an administrator,
189189
** together with a succinct explanation of each.
190190
**
191191
** This page is only accessible by administrators.
192192
*/
193193
void setup_logmenu_page(void){
@@ -202,11 +202,11 @@
202202
return;
203203
}
204204
style_header("Log Menu");
205205
@ <table border="0" cellspacing="3">
206206
207
- if( db_get_boolean("admin-log",0)==0 ){
207
+ if( db_get_boolean("admin-log",1)==0 ){
208208
blob_appendf(&desc,
209209
"The admin log records configuration changes to the repository.\n"
210210
"<b>Disabled</b>: Turn on the "
211211
" <a href='%R/setup_settings'>admin-log setting</a> to enable."
212212
);
@@ -220,11 +220,11 @@
220220
}
221221
setup_menu_entry("Artifact Log", "rcvfromlist",
222222
"The artifact log records when new content is added in the\n"
223223
"\"rcvfrom\" table.\n"
224224
);
225
- if( db_get_boolean("access-log",0) ){
225
+ if( db_get_boolean("access-log",1) ){
226226
setup_menu_entry("User Log", "user_log",
227227
"Login attempts recorded in the \"accesslog\" table."
228228
);
229229
}else{
230230
blob_appendf(&desc,
@@ -264,27 +264,10 @@
264264
bErrLog = 1;
265265
}
266266
setup_menu_entry("Error Log", bErrLog ? "errorlog" : 0, blob_str(&desc));
267267
blob_reset(&desc);
268268
269
- @ <tr><td><td><td>
270
- @ &mdash;&mdash;
271
- @ <i>The remaining links are subsets of the Error Log</i>
272
- @ &mdash;&mdash;
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
-
286269
@ </table>
287270
style_finish_page();
288271
}
289272
290273
/*
@@ -952,11 +935,11 @@
952935
@ of project-name. This description can be edited in the second entry
953936
@ box on the <a href="./setup_config">Setup/Configuration page</a>.
954937
@
955938
@ <li><p><b>project-name</b> &rarr;
956939
@ 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
958941
@ <a href="./setup_config">Setup/Configuration page</a>.
959942
@
960943
@ <li><p><b>peer-repo-<i>CODE</i></b> &rarr;
961944
@ <i>CODE</i> is 16-character prefix of the project-code for another
962945
@ repository that is part of the same login-group. The value is the
@@ -998,17 +981,10 @@
998981
db_begin_transaction();
999982
@ <form action="%R/setup_timeline" method="post"><div>
1000983
login_insert_csrf_secret();
1001984
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
1002985
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
-
1010986
@ <hr>
1011987
onoff_attribute("Plaintext comments on timelines",
1012988
"timeline-plaintext", "tpt", 0, 0);
1013989
@ <p>In timeline displays, check-in comments are displayed literally,
1014990
@ without any wiki or HTML interpretation. Use CSS to change
@@ -1126,10 +1102,11 @@
11261102
*/
11271103
void setup_settings(void){
11281104
int nSetting;
11291105
int i;
11301106
Setting const *pSet;
1107
+ int bIfChng = P("all")==0;
11311108
const Setting *aSetting = setting_info(&nSetting);
11321109
11331110
login_check_credentials();
11341111
if( !g.perm.Setup ){
11351112
login_needed(0);
@@ -1142,23 +1119,36 @@
11421119
/* Provide read-only access to versioned settings,
11431120
but only if no repo file was explicitly provided. */
11441121
db_open_local(0);
11451122
}
11461123
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
+ }
11471128
@ <p>Settings marked with (v) are "versionable" and will be overridden
11481129
@ by the contents of managed files named
11491130
@ "<tt>.fossil-settings/</tt><i>SETTING-NAME</i>".
11501131
@ If the file for a versionable setting exists, the value cannot be
11511132
@ changed on this screen.</p><hr><p>
11521133
@
11531134
@ <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
+ }
11541141
@ <table border="0"><tr><td valign="top">
11551142
login_insert_csrf_secret();
11561143
for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
11571144
if( pSet->width==0 ){
11581145
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
+ }
11601150
onoff_attribute("", pSet->name,
11611151
pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/,
11621152
is_truth(pSet->def), hasVersionableValue);
11631153
@ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a>
11641154
if( pSet->versionable ){
@@ -1172,11 +1162,14 @@
11721162
@ </td><td style="width:50px;"></td><td valign="top">
11731163
@ <table>
11741164
for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
11751165
if( pSet->width>0 && !pSet->forceTextArea ){
11761166
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
+ }
11781171
@ <tr><td>
11791172
@ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a>
11801173
if( pSet->versionable ){
11811174
@ (v)
11821175
} else {
@@ -1191,11 +1184,14 @@
11911184
}
11921185
@</table>
11931186
@ </td><td style="width:50px;"></td><td valign="top">
11941187
for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
11951188
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
+ }
11971193
@ <a href='%R/help?cmd=%s(pSet->name)'>%s(pSet->name)</a>
11981194
if( pSet->versionable ){
11991195
@ (v)<br>
12001196
} else {
12011197
@ <br>
@@ -1428,21 +1424,22 @@
14281424
db_begin_transaction();
14291425
@ <form action="%R/setup_wiki" method="post"><div>
14301426
login_insert_csrf_secret();
14311427
@ <input type="submit" name="submit" value="Apply Changes"></p>
14321428
@ <hr>
1433
- onoff_attribute("Associate Wiki Pages With Branches, Tags, or Checkins",
1429
+ onoff_attribute("Associate Wiki Pages With Branches, Tags, Tickets, or Checkins",
14341430
"wiki-about", "wiki-about", 1, 0);
14351431
@ <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.
14401436
@ <ul>
14411437
@ <li> <b>branch/</b><i>branch-name</i>
14421438
@ <li> <b>checkin/</b><i>full-check-in-hash</i>
14431439
@ <li> <b>tag/</b><i>tag-name</i>
1440
+ @ <li> <b>ticket/</b><i>full-ticket-hash</i>
14441441
@ </ul>
14451442
@ (Property: "wiki-about")</p>
14461443
@ <hr>
14471444
entry_attribute("Allow Unsafe HTML In Markdown", 6,
14481445
"safe-html", "safe-html", "", 0);
@@ -2137,11 +2134,11 @@
21372134
style_header("Admin Log");
21382135
style_submenu_element("Log-Menu", "setup-logmenu");
21392136
create_admin_log_table();
21402137
limit = atoi(PD("n","200"));
21412138
ofst = atoi(PD("x","0"));
2142
- fLogEnabled = db_get_boolean("admin-log", 0);
2139
+ fLogEnabled = db_get_boolean("admin-log", 1);
21432140
@ <div>Admin logging is %s(fLogEnabled?"on":"off").
21442141
@ (Change this on the <a href="setup_settings">settings</a> page.)</div>
21452142
21462143
if( ofst>0 ){
21472144
int prevx = ofst - limit;
@@ -2251,10 +2248,12 @@
22512248
onoff_attribute("Search Wiki", "search-wiki", "sw", 0, 0);
22522249
@ <br>
22532250
onoff_attribute("Search Tech Notes", "search-technote", "se", 0, 0);
22542251
@ <br>
22552252
onoff_attribute("Search Forum", "search-forum", "sf", 0, 0);
2253
+ @ <br>
2254
+ onoff_attribute("Search Built-in Help Text", "search-help", "sh", 0, 0);
22562255
@ <hr>
22572256
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
22582257
@ <hr>
22592258
if( P("fts0") ){
22602259
search_drop_index();
22612260
--- 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 @ &mdash;&mdash;
271 @ <i>The remaining links are subsets of the Error Log</i>
272 @ &mdash;&mdash;
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> &rarr;
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> &rarr;
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> &rarr;
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> &rarr;
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 @@
302302
if( zPw==0 ) return 0;
303303
if( zPw[0]==0 ) return 1;
304304
while( zPw[0]=='*' ){ zPw++; }
305305
return zPw[0]!=0;
306306
}
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
+}
307398
308399
/*
309400
** WEBPAGE: setup_uedit
310401
**
311402
** Edit information about a user or create a new user.
@@ -314,10 +405,11 @@
314405
void user_edit(void){
315406
const char *zId, *zLogin, *zInfo, *zCap, *zPw;
316407
const char *zGroup;
317408
const char *zOldLogin;
318409
int uid, i;
410
+ char *zOldCaps = 0; /* Capabilities before edit */
319411
char *zDeleteVerify = 0; /* Delete user verification text */
320412
int higherUser = 0; /* True if user being edited is SETUP and the */
321413
/* user doing the editing is ADMIN. Disallow editing */
322414
const char *inherit[128];
323415
int a[128];
@@ -331,14 +423,15 @@
331423
/* Check to see if an ADMIN user is trying to edit a SETUP account.
332424
** Don't allow that.
333425
*/
334426
zId = PD("id", "0");
335427
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
+ }
340433
}
341434
342435
if( P("can") ){
343436
/* User pressed the cancel button */
344437
cgi_redirect(cgi_referer("setup_ulist"));
@@ -393,30 +486,33 @@
393486
}else if( !cgi_csrf_safe(2) ){
394487
/* This might be a cross-site request forgery, so ignore it */
395488
}else{
396489
/* We have all the information we need to make the change to the user */
397490
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];
399494
zNm[0] = 'a';
400495
zNm[2] = 0;
401496
for(i=0, c='a'; c<='z'; c++){
402497
zNm[1] = c;
403498
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;
405500
}
406501
for(c='0'; c<='9'; c++){
407502
zNm[1] = c;
408503
a[c&0x7f] = P(zNm)!=0;
409
- if( a[c&0x7f] ) zCap[i++] = c;
504
+ if( a[c&0x7f] ) aCap[i++] = c;
410505
}
411506
for(c='A'; c<='Z'; c++){
412507
zNm[1] = c;
413508
a[c&0x7f] = P(zNm)!=0;
414
- if( a[c&0x7f] ) zCap[i++] = c;
509
+ if( a[c&0x7f] ) aCap[i++] = c;
415510
}
416511
417
- zCap[i] = 0;
512
+ aCap[i] = 0;
513
+ bHasNewCaps = bIsNew || userHasNewCaps(zOldCaps, &aCap[0]);
418514
zPw = P("pw");
419515
zLogin = P("login");
420516
if( strlen(zLogin)==0 ){
421517
const char *zRef = cgi_referer("setup_ulist");
422518
style_header("User Creation Error");
@@ -444,15 +540,16 @@
444540
style_finish_page();
445541
return;
446542
}
447543
cgi_csrf_verify();
448544
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 );
454551
if( zOldLogin && fossil_strcmp(zLogin, zOldLogin)!=0 ){
455552
if( alert_tables_exist() ){
456553
/* Rename matching subscriber entry, else the user cannot
457554
re-subscribe with their same email address. */
458555
db_multi_exec("UPDATE subscriber SET suname=%Q WHERE suname=%Q",
@@ -460,12 +557,13 @@
460557
}
461558
admin_log( "Renamed user [%q] to [%q].", zOldLogin, zLogin );
462559
}
463560
db_protect_pop();
464561
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] );
467565
if( atoi(PD("all","0"))>0 ){
468566
Blob sql;
469567
char *zErr = 0;
470568
blob_zero(&sql);
471569
if( zOldLogin==0 ){
@@ -496,49 +594,55 @@
496594
"(SELECT value FROM config WHERE name='project-code')),pw),"
497595
" info=%Q,"
498596
" cap=%Q,"
499597
" mtime=now()"
500598
" WHERE login=%Q;",
501
- zLogin, P("pw"), zLogin, P("info"), zCap,
599
+ zLogin, P("pw"), zLogin, P("info"), &aCap[0],
502600
zOldLogin
503601
);
504602
db_unprotect(PROTECT_USER);
505603
login_group_sql(blob_str(&sql), "<li> ", " </li>\n", &zErr);
506604
db_protect_pop();
507605
blob_reset(&sql);
508606
admin_log( "Updated user [%q] in all login groups "
509607
"with capabilities [%q].",
510
- zLogin, zCap );
608
+ zLogin, &aCap[0] );
511609
if( zErr ){
512610
const char *zRef = cgi_referer("setup_ulist");
513611
style_header("User Change Error");
514612
admin_log( "Error updating user '%q': %s'.", zLogin, zErr );
515613
@ <span class="loginError">%h(zErr)</span>
516614
@
517615
@ <p><a href="setup_uedit?id=%d(uid)&referer=%T(zRef)">
518616
@ [Bummer]</a></p>
519617
style_finish_page();
618
+ if( bHasNewCaps ){
619
+ alert_user_elevation(zLogin, uid, bIsNew, zOldCaps, &aCap[0]);
620
+ }
520621
return;
521622
}
522623
}
624
+ if( bHasNewCaps ){
625
+ alert_user_elevation(zLogin, uid, bIsNew, zOldCaps, &aCap[0]);
626
+ }
523627
cgi_redirect(cgi_referer("setup_ulist"));
524628
return;
525629
}
526630
527631
/* Load the existing information about the user, if any
528632
*/
529633
zLogin = "";
530634
zInfo = "";
531
- zCap = "";
635
+ zCap = zOldCaps;
532636
zPw = "";
533637
for(i='a'; i<='z'; i++) oa[i] = "";
534638
for(i='0'; i<='9'; i++) oa[i] = "";
535639
for(i='A'; i<='Z'; i++) oa[i] = "";
536640
if( uid ){
641
+ assert( zCap );
537642
zLogin = db_text("", "SELECT login FROM user WHERE uid=%d", uid);
538643
zInfo = db_text("", "SELECT info FROM user WHERE uid=%d", uid);
539
- zCap = db_text("", "SELECT cap FROM user WHERE uid=%d", uid);
540644
zPw = db_text("", "SELECT pw FROM user WHERE uid=%d", uid);
541645
for(i=0; zCap[i]; i++){
542646
char c = zCap[i];
543647
if( (c>='a' && c<='z') || (c>='0' && c<='9') || (c>='A' && c<='Z') ){
544648
oa[c&0x7f] = " checked=\"checked\"";
545649
--- 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
--- src/sitemap.c
+++ src/sitemap.c
@@ -293,10 +293,11 @@
293293
@ <li>%z(href("%R/test-rename-list"))List of file renames</a></li>
294294
}
295295
@ <li>%z(href("%R/test-builtin-files"))List of built-in files</a></li>
296296
@ <li>%z(href("%R/mimetype_list"))List of MIME types</a></li>
297297
@ <li>%z(href("%R/hash-color-test"))Hash color test</a>
298
+ @ <li>%z(href("%R/test-bgcolor"))Background color test</a>
298299
if( g.perm.Admin ){
299300
@ <li>%z(href("%R/test-backlinks"))List of backlinks</a></li>
300301
@ <li>%z(href("%R/test-backlink-timeline"))Backlink timeline</a></li>
301302
@ <li>%z(href("%R/phantoms"))List of phantom artifacts</a></li>
302303
@ <li>%z(href("%R/test-warning"))Error Log test page</a></li>
303304
--- 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
--- src/sqlcmd.c
+++ src/sqlcmd.c
@@ -235,10 +235,11 @@
235235
char *zSql = sqlite3_mprintf("ATTACH %Q AS 'configdb' KEY ''",
236236
g.zConfigDbName);
237237
sqlite3_exec(db, zSql, 0, 0, 0);
238238
sqlite3_free(zSql);
239239
}
240
+ (void)timeline_query_for_tty(); /* Registers wiki_to_text() as side-effect */
240241
/* Arrange to trace close operations so that static prepared statements
241242
** will get cleaned up when the shell closes the database connection */
242243
if( g.fSqlTrace ) mTrace |= SQLITE_TRACE_PROFILE;
243244
sqlite3_trace_v2(db, mTrace, db_sql_trace, 0);
244245
db_protect_only(PROTECT_NONE);
245246
--- 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 @@
756756
DiffConfig DCfg;
757757
758758
if( strstr(zCmd,"show")!=0 || strstr(zCmd,"cat")!=0 ){
759759
fBaseline = 1;
760760
}
761
- if( find_option("tk",0,0)!=0 ){
761
+ if( find_option("tk",0,0)!=0 || gdiff_using_tk(zCmd[0]=='g') ){
762762
db_close(0);
763763
diff_tk(fBaseline ? "stash show" : "stash diff", 3);
764764
return;
765765
}
766766
diff_options(&DCfg, zCmd[0]=='g', 0);
767767
--- 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 @@
318318
&& nMaxEvents>0
319319
){
320320
/* If the timespan covered by this row contains "now", then project
321321
** the number of changes until the completion of the timespan and
322322
** show a dashed box of that projection. */
323
+ int nProj = (int)(((double)nCount)/rNowFraction);
323324
int nExtra = (int)(((double)nCount)/rNowFraction) - nCount;
324325
int nXSize = (100 * nExtra)/nMaxEvents;
325326
@ <span class='statistics-report-graph-line' \
326327
@ style='display:inline-block;min-width:%d(nSize)%%;'>&nbsp;</span>\
327
- @ <span class='statistics-report-graph-extra' \
328
+ @ <span class='statistics-report-graph-extra' title='%d(nProj)' \
328329
@ style='display:inline-block;min-width:%d(nXSize)%%;'>&nbsp;</span>\
329330
}else{
330331
@ <div class='statistics-report-graph-line' \
331332
@ style='width:%d(nSize)%%;'>&nbsp;</div> \
332333
}
@@ -747,18 +748,19 @@
747748
if( zCurrentWeek!=0
748749
&& strcmp(zWeek, zCurrentWeek)==0
749750
&& rNowFraction>0.05
750751
&& nMaxEvents>0
751752
){
752
- /* If the covered covered by this row contains "now", then project
753
+ /* If the timespan covered by this row contains "now", then project
753754
** the number of changes until the completion of the week and
754755
** show a dashed box of that projection. */
756
+ int nProj = (int)(((double)nCount)/rNowFraction);
755757
int nExtra = (int)(((double)nCount)/rNowFraction) - nCount;
756758
int nXSize = (100 * nExtra)/nMaxEvents;
757759
@ <span class='statistics-report-graph-line' \
758760
@ style='display:inline-block;min-width:%d(nSize)%%;'>&nbsp;</span>\
759
- @ <span class='statistics-report-graph-extra' \
761
+ @ <span class='statistics-report-graph-extra' title='%d(nProj)' \
760762
@ style='display:inline-block;min-width:%d(nXSize)%%;'>&nbsp;</span>\
761763
}else{
762764
@ <div class='statistics-report-graph-line' \
763765
@ style='width:%d(nSize)%%;'>&nbsp;</div> \
764766
}
765767
--- 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)%%;'>&nbsp;</span>\
327 @ <span class='statistics-report-graph-extra' \
328 @ style='display:inline-block;min-width:%d(nXSize)%%;'>&nbsp;</span>\
329 }else{
330 @ <div class='statistics-report-graph-line' \
331 @ style='width:%d(nSize)%%;'>&nbsp;</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)%%;'>&nbsp;</span>\
759 @ <span class='statistics-report-graph-extra' \
760 @ style='display:inline-block;min-width:%d(nXSize)%%;'>&nbsp;</span>\
761 }else{
762 @ <div class='statistics-report-graph-line' \
763 @ style='width:%d(nSize)%%;'>&nbsp;</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)%%;'>&nbsp;</span>\
328 @ <span class='statistics-report-graph-extra' title='%d(nProj)' \
329 @ style='display:inline-block;min-width:%d(nXSize)%%;'>&nbsp;</span>\
330 }else{
331 @ <div class='statistics-report-graph-line' \
332 @ style='width:%d(nSize)%%;'>&nbsp;</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)%%;'>&nbsp;</span>\
761 @ <span class='statistics-report-graph-extra' title='%d(nProj)' \
762 @ style='display:inline-block;min-width:%d(nXSize)%%;'>&nbsp;</span>\
763 }else{
764 @ <div class='statistics-report-graph-line' \
765 @ style='width:%d(nSize)%%;'>&nbsp;</div> \
766 }
767
+33
--- src/style.c
+++ src/style.c
@@ -1340,10 +1340,43 @@
13401340
&& !login_has_capability(&c, 1, 0) ) zCap[i++] = c;
13411341
}
13421342
zCap[i] = 0;
13431343
return zCap;
13441344
}
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
+}
13451378
13461379
/*
13471380
** WEBPAGE: test_env
13481381
**
13491382
** Display CGI-variables and other aspects of the run-time
13501383
--- 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 @@
499499
pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0);
500500
if( pManifest ){
501501
int flg, eflg = 0;
502502
mTime = (unsigned)((pManifest->rDate - 2440587.5)*86400.0);
503503
if( pTar ) tar_begin(mTime);
504
- flg = db_get_manifest_setting();
504
+ flg = db_get_manifest_setting(blob_str(&hash));
505505
if( flg ){
506506
/* eflg is the effective flags, taking include/exclude into account */
507507
if( (pInclude==0 || glob_match(pInclude, "manifest"))
508508
&& !glob_match(pExclude, "manifest")
509509
&& (flg & MFESTFLG_RAW) ){
510510
--- 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 @@
6060
memset(t, 0, sizeof(*t));
6161
6262
#if defined(TIOCGSIZE)
6363
{
6464
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
+ ){
6669
t->nColumns = ts.ts_cols;
6770
t->nLines = ts.ts_lines;
6871
return 1;
6972
}
7073
return 0;
7174
}
7275
#elif defined(TIOCGWINSZ)
7376
{
7477
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
+ ){
7682
t->nColumns = ws.ws_col;
7783
t->nLines = ws.ws_row;
7884
return 1;
7985
}
8086
return 0;
8187
}
8288
#elif defined(_WIN32)
8389
{
8490
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
+ ){
8695
t->nColumns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
8796
t->nLines = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
8897
return 1;
8998
}
9099
return 0;
@@ -129,5 +138,57 @@
129138
void test_terminal_size_cmd(void){
130139
TerminalSize ts;
131140
terminal_get_size(&ts);
132141
fossil_print("%d %d\n", ts.nColumns, ts.nLines);
133142
}
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 */
134195
--- 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
--- src/th_main.c
+++ src/th_main.c
@@ -696,10 +696,31 @@
696696
wiki_convert(&src, 0, flags);
697697
blob_reset(&src);
698698
}
699699
return TH_OK;
700700
}
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
+}
701722
702723
/*
703724
** TH1 command: htmlize STRING
704725
**
705726
** Escape all characters of STRING which have special meaning in HTML.
@@ -2374,10 +2395,11 @@
23742395
{"unversioned", unversionedCmd, 0},
23752396
{"utime", utimeCmd, 0},
23762397
{"verifyCsrf", verifyCsrfCmd, 0},
23772398
{"verifyLogin", verifyLoginCmd, 0},
23782399
{"wiki", wikiCmd, (void*)&aFlags[0]},
2400
+ {"wiki_assoc", wikiAssocCmd, 0},
23792401
{0, 0, 0}
23802402
};
23812403
if( g.thTrace ){
23822404
Th_Trace("th1-init 0x%x => 0x%x<br>\n", g.th1Flags, flags);
23832405
}
23842406
--- 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 @@
205205
char zPrevDate[20];
206206
GraphContext *pGraph = 0;
207207
int prevWasDivider = 0; /* True if previous output row was <hr> */
208208
int fchngQueryInit = 0; /* True if fchngQuery is initialized */
209209
Stmt fchngQuery; /* Query for file changes on check-ins */
210
- static Stmt qbranch;
211210
int pendingEndTr = 0; /* True if a </td></tr> is needed */
212211
int vid = 0; /* Current check-out version */
213212
int dateFormat = 0; /* 0: HH:MM (default) */
214213
int bCommentGitStyle = 0; /* Only show comments through first blank line */
215214
const char *zStyle; /* Sub-name for classes for the style */
@@ -222,11 +221,28 @@
222221
vid = db_lget_int("checkout", 0);
223222
}
224223
zPrevDate[0] = 0;
225224
mxWikiLen = db_get_int("timeline-max-comment", 0);
226225
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
+ */
227234
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
+ */
228244
bTimestampLinksToInfo = db_get_boolean("timeline-tslink-info", 0);
229245
if( (tmFlags & TIMELINE_VIEWS)==0 ){
230246
tmFlags |= timeline_ss_cookie();
231247
}
232248
if( tmFlags & TIMELINE_COLUMNAR ){
@@ -243,14 +259,10 @@
243259
zDateFmt = P("datefmt");
244260
if( zDateFmt ) dateFormat = atoi(zDateFmt);
245261
if( tmFlags & TIMELINE_GRAPH ){
246262
pGraph = graph_init();
247263
}
248
- db_static_prepare(&qbranch,
249
- "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid",
250
- TAG_BRANCH
251
- );
252264
if( (tmFlags & TIMELINE_CHPICK)!=0
253265
&& !db_table_exists("repository","cherrypick")
254266
){
255267
tmFlags &= ~TIMELINE_CHPICK;
256268
}
@@ -266,11 +278,11 @@
266278
const char *zType = db_column_text(pQuery, 7);
267279
const char *zUser = db_column_text(pQuery, 4);
268280
const char *zTagList = db_column_text(pQuery, 8);
269281
int tagid = db_column_int(pQuery, 9);
270282
const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
271
- const char *zBr = 0; /* Branch */
283
+ char *zBr = 0; /* Branch */
272284
int commentColumn = 3; /* Column containing comment text */
273285
int modPending; /* Pending moderation */
274286
char *zDateLink; /* URL for the link on the timestamp */
275287
int drawDetailEllipsis; /* True to show ellipsis in place of detail */
276288
int gidx = 0; /* Graph row identifier */
@@ -390,10 +402,12 @@
390402
zDateLink = mprintf("<a>");
391403
}
392404
@ <td class="timelineTime">%z(zDateLink)%s(zTime)</a></td>
393405
@ <td class="timelineGraph">
394406
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. */
395409
if( tmFlags & TIMELINE_UCOLOR ){
396410
zBgClr = zUser ? user_color(zUser) : 0;
397411
}else if( tmFlags & TIMELINE_NOCOLOR ){
398412
zBgClr = 0;
399413
}else if( zType[0]=='c' ){
@@ -408,22 +422,21 @@
408422
}else{
409423
zBgClr = hash_color("f"); /* delta manifest */
410424
}
411425
db_reset(&qdelta);
412426
}
427
+ }else{
428
+ /* Make sure the user-specified background color is reasonable */
429
+ zBgClr = reasonable_bg_color(zBgClr, 0);
413430
}
414431
if( zType[0]=='c'
415432
&& (pGraph || zBgClr==0 || (tmFlags & (TIMELINE_BRCOLOR|TIMELINE_DELTA))!=0)
416433
){
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);
424435
if( zBgClr==0 || (tmFlags & TIMELINE_BRCOLOR)!=0 ){
436
+ /* If no background color is specified, use a color based on the
437
+ ** branch name */
425438
if( tmFlags & (TIMELINE_DELTA|TIMELINE_NOCOLOR) ){
426439
}else if( zBr==0 || strcmp(zBr,"trunk")==0 ){
427440
zBgClr = 0;
428441
}else{
429442
zBgClr = hash_color(zBr);
@@ -459,19 +472,19 @@
459472
db_reset(&qcherrypick);
460473
}
461474
gidx = graph_add_row(pGraph, rid, nParent, nCherrypick, aParent,
462475
zBr, zBgClr, zUuid,
463476
isLeaf ? isLeaf + 2 * has_closed_tag(rid) : 0);
464
- db_reset(&qbranch);
465477
@ <div id="m%d(gidx)" class="tl-nodemark"></div>
466478
}else if( zType[0]=='e' && pGraph && zBgClr && zBgClr[0] ){
467479
/* For technotes, make a graph node with nParent==(-1). This will
468480
** not actually draw anything on the graph, but it will set the
469481
** background color of the timeline entry */
470482
gidx = graph_add_row(pGraph, rid, -1, 0, 0, zBr, zBgClr, zUuid, 0);
471483
@ <div id="m%d(gidx)" class="tl-nodemark"></div>
472484
}
485
+ fossil_free(zBr);
473486
@</td>
474487
if( !isSelectedOrCurrent ){
475488
@ <td class="timeline%s(zStyle)Cell%s(zExtraClass)" id='mc%d(gidx)'>
476489
}else{
477490
@ <td class="timeline%s(zStyle)Cell%s(zExtraClass)">
@@ -1100,47 +1113,10 @@
11001113
@ WHERE blob.rid=event.objid
11011114
;
11021115
return zBase;
11031116
}
11041117
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
-
11421118
/*
11431119
** zDate is a localtime date. Insert records into the
11441120
** "timeline" table to cause <hr> to be inserted on zDate.
11451121
*/
11461122
static int timeline_add_divider(double rDate){
@@ -1392,11 +1368,11 @@
13921368
** return 0.
13931369
*/
13941370
static int timeline_endpoint(
13951371
int iFrom, /* Starting point */
13961372
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 */
13981374
){
13991375
int tagId;
14001376
int endId = 0;
14011377
Stmt q;
14021378
int ans = 0;
@@ -1419,11 +1395,12 @@
14191395
" AND plink.mtime<=(SELECT max(event.mtime) FROM tagxref, event"
14201396
" WHERE tagxref.tagid=%d AND tagxref.tagtype>0"
14211397
" AND event.objid=tagxref.rid)"
14221398
" ORDER BY plink.mtime)"
14231399
"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",
14251402
iFrom, iFrom, tagId, tagId
14261403
);
14271404
}else{
14281405
db_prepare(&q,
14291406
"WITH RECURSIVE dx(id,mtime) AS ("
@@ -1450,11 +1427,12 @@
14501427
" AND event.mtime>=(SELECT min(event.mtime) FROM tagxref, event"
14511428
" WHERE tagxref.tagid=%d AND tagxref.tagtype>0"
14521429
" AND event.objid=tagxref.rid)"
14531430
" ORDER BY event.mtime DESC)"
14541431
"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",
14561434
iFrom, iFrom, tagId, tagId
14571435
);
14581436
}else{
14591437
db_prepare(&q,
14601438
"WITH RECURSIVE dx(id,mtime) AS ("
@@ -1522,17 +1500,17 @@
15221500
/*
15231501
** COMMAND: test-endpoint
15241502
**
15251503
** Usage: fossil test-endpoint BASE TAG ?OPTIONS?
15261504
**
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
15291507
** the --backto to see the first ancestor checkin.
15301508
**
15311509
** Options:
15321510
**
1533
-** --backto Show ancestor. Others defaults to descendents.
1511
+** --backto Show ancestor. Others defaults to descendants.
15341512
*/
15351513
void timeline_test_endpoint(void){
15361514
int bForward = find_option("backto",0,0)==0;
15371515
int from_rid;
15381516
int ans;
@@ -1583,14 +1561,14 @@
15831561
** dp=CHECKIN Same as 'd=CHECKIN&p=CHECKIN'
15841562
** dp2=CKIN2 Same as 'd2=CKIN2&p2=CKIN2'
15851563
** df=CHECKIN Same as 'd=CHECKIN&n1=all&nd'. Mnemonic: "Derived From"
15861564
** bt=CHECKIN "Back To". Show ancenstors going back to CHECKIN
15871565
** 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
15891567
** ft=CHECKIN "Forward To": Show decendents forward to CHECKIN
15901568
** 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
15921570
** t=TAG Show only check-ins with the given TAG
15931571
** r=TAG Same as 't=TAG&rel'. Mnemonic: "Related"
15941572
** tl=TAGLIST Same as 't=TAGLIST&ms=brlist'. Mnemonic: "Tag List"
15951573
** rl=TAGLIST Same as 'r=TAGLIST&ms=brlist'. Mnemonic: "Related List"
15961574
** ml=TAGLIST Same as 'tl=TAGLIST&mionly'. Mnemonic: "Merge-in List"
@@ -1611,20 +1589,21 @@
16111589
** nsm Omit the submenu
16121590
** nc Omit all graph colors other than highlights
16131591
** v Show details of files changed
16141592
** vfx Show complete text of forum messages
16151593
** 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
16261605
** uf=FILE_HASH Show only check-ins that contain the given file version
16271606
** All qualifying check-ins are shown unless there is
16281607
** also an n= or n1= query parameter.
16291608
** chng=GLOBLIST Show only check-ins that involve changes to a file whose
16301609
** name matches one of the comma-separate GLOBLIST
@@ -1707,15 +1686,15 @@
17071686
const char *zThisUser = 0; /* Suppress links to this user */
17081687
HQuery url; /* URL for various branch links */
17091688
int from_rid = name_to_typed_rid(P("from"),"ci"); /* from= for paths */
17101689
const char *zTo2 = 0;
17111690
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 */
17131692
int me_rid = name_to_typed_rid(P("me"),"ci"); /* me= for common ancestory */
17141693
int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */
17151694
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 */
17171696
double rBefore, rAfter, rCirca; /* Boundary times */
17181697
const char *z;
17191698
char *zOlderButton = 0; /* URL for Older button at the bottom */
17201699
char *zOlderButtonLabel = 0; /* Label for the Older Button */
17211700
char *zNewerButton = 0; /* URL for Newer button at the top */
@@ -1728,10 +1707,11 @@
17281707
int showCherrypicks = 1; /* True to show cherrypick merges */
17291708
int haveParameterN; /* True if n= query parameter present */
17301709
int from_to_mode = 0; /* 0: from,to. 1: from,ft 2: from,bt */
17311710
int showSql = PB("showsql"); /* True to show the SQL */
17321711
Blob allSql; /* Copy of all SQL text */
1712
+ int bMin = P("min")!=0; /* True if "min" query parameter used */
17331713
17341714
login_check_credentials();
17351715
url_initialize(&url, "timeline");
17361716
cgi_query_parameters_to_url(&url);
17371717
blob_init(&allSql, 0, 0);
@@ -1777,20 +1757,20 @@
17771757
}else{
17781758
nEntry = 50;
17791759
}
17801760
17811761
/* 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);
17841764
z = P("f");
17851765
f_rid = z ? name_to_typed_rid(z,"ci") : 0;
17861766
z = P("df");
17871767
if( z && (d_rid = name_to_typed_rid(z,"ci"))!=0 ){
17881768
nEntry = 0;
17891769
useDividers = 0;
17901770
cgi_replace_query_parameter("d",fossil_strdup(z));
1791
- zDPName = z;
1771
+ zDPNameD = zDPNameP = z;
17921772
}
17931773
17941774
/* Undocumented query parameter to set JS mode */
17951775
builtin_set_js_delivery_mode(P("jsmode"),1);
17961776
@@ -1810,13 +1790,14 @@
18101790
showCherrypicks = 0;
18111791
}
18121792
18131793
/* To view the timeline, must have permission to read project data.
18141794
*/
1815
- pd_rid = name_choice("dp","dp2",&zDPName);
1795
+ pd_rid = name_choice("dp","dp2",&zDPNameP);
18161796
if( pd_rid ){
18171797
p_rid = d_rid = pd_rid;
1798
+ zDPNameD = zDPNameP;
18181799
}
18191800
if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum)
18201801
|| (bisectLocal && !g.perm.Setup)
18211802
){
18221803
login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
@@ -2083,20 +2064,22 @@
20832064
const char *zTo = 0;
20842065
Blob ins;
20852066
int nNodeOnPath = 0;
20862067
int commonAncs = 0; /* Common ancestors of me_rid and you_rid. */
20872068
int earlierRid = 0, laterRid = 0;
2069
+ int cost = bShort ? 0 : 1;
2070
+ int nSkip = 0;
20882071
20892072
if( from_rid && to_rid ){
20902073
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);
20922075
}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);
20942077
earlierRid = commonAncs = from_rid;
20952078
laterRid = to_rid;
20962079
}else{
2097
- p = path_shortest(to_rid, from_rid, 0, 1, 0);
2080
+ p = path_shortest(to_rid, from_rid, 0, 1, 0, cost);
20982081
earlierRid = commonAncs = to_rid;
20992082
laterRid = from_rid;
21002083
}
21012084
zFrom = P("from");
21022085
zTo = zTo2 ? zTo2 : P("to");
@@ -2123,20 +2106,25 @@
21232106
);
21242107
if( p ){
21252108
int cnt = 4;
21262109
blob_init(&ins, 0, 0);
21272110
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 ){
21312120
blob_append_sql(&ins, ",\n (%d)", p->rid);
21322121
cnt = 0;
21332122
}else{
21342123
cnt++;
21352124
blob_append_sql(&ins, ",(%d)", p->rid);
21362125
}
2137
- p = p->u.pTo;
21382126
}
21392127
}
21402128
path_reset();
21412129
db_multi_exec("%s", blob_str(&ins)/*safe-for-%s*/);
21422130
blob_reset(&ins);
@@ -2196,12 +2184,13 @@
21962184
style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0);
21972185
}
21982186
nNodeOnPath = db_int(0, "SELECT count(*) FROM temp.pathnode");
21992187
if( nNodeOnPath==1 && from_to_mode>0 ){
22002188
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);
22032192
}else{
22042193
blob_appendf(&desc, "%d check-ins going from ", nNodeOnPath);
22052194
}
22062195
if( from_rid==selectedRid ){
22072196
blob_appendf(&desc, "<span class='timelineSelected'>");
@@ -2225,49 +2214,60 @@
22252214
}
22262215
}
22272216
}
22282217
addFileGlobDescription(zChng, &desc);
22292218
}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;
22332222
int np = 0, nd;
22342223
const char *zBackTo = 0;
22352224
const char *zFwdTo = 0;
22362225
int ridBackTo = 0;
22372226
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 */
22382230
22392231
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;
22432239
}
22442240
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)"
22462242
);
22472243
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;
22522244
blob_append_sql(&sql, " AND event.objid IN ok");
22532245
nd = 0;
22542246
if( d_rid ){
22552247
double rStopTime = 9e99;
22562248
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
+ }
22572256
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);
22602258
ridFwdTo = first_checkin_with_tag_after_date(zFwdTo, rStartDate);
22612259
if( ridFwdTo==0 ){
22622260
ridFwdTo = name_to_typed_rid(zBackTo,"ci");
22632261
}
22642262
if( ridFwdTo ){
22652263
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);
22682265
}
2266
+ }else if( bSeparateDandP ){
2267
+ rStopTime = mtime_of_rid(p_rid, 9e99);
2268
+ nEntry = 0;
22692269
}
22702270
if( rStopTime<9e99 ){
22712271
rStopTime += 5.8e-6; /* Round up by 1/2 second */
22722272
}
22732273
db_multi_exec(
@@ -2280,72 +2280,111 @@
22802280
" ORDER BY 2\n"
22812281
")\n"
22822282
"INSERT OR IGNORE INTO ok SELECT rid FROM dx LIMIT %d",
22832283
d_rid, rStopTime<8e99 ? 17 : 2, rStopTime, nEntry<=0 ? -1 : nEntry+1
22842284
);
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;
22892288
}
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
+ }
22922303
}
22932304
if( p_rid ){
22942305
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
+ }
22952313
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);
22982315
ridBackTo = last_checkin_with_tag_before_date(zBackTo, rDateLimit);
22992316
if( ridBackTo==0 ){
23002317
ridBackTo = name_to_typed_rid(zBackTo,"ci");
23012318
}
23022319
if( ridBackTo && !haveParameterN ) nEntry = 0;
2320
+ }else if( bSeparateDandP ){
2321
+ ridBackTo = d_rid;
2322
+ nEntry = 0;
23032323
}
23042324
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;");
23092331
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;
23102340
}
2311
- if( useDividers && !selectedRid ) selectedRid = p_rid;
23122341
}
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
+ }
23162358
if( ridBackTo ){
23172359
if( np==0 ){
23182360
blob_reset(&desc);
23192361
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,
23222364
href("%R/info?name=%h",zBackTo), zBackTo);
23232365
}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)" : "");
23262369
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)" : "");
23292373
}
23302374
}
23312375
}else if( ridFwdTo ){
23322376
if( nd==0 ){
23332377
blob_reset(&desc);
23342378
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,
23372381
href("%R/info?name=%h",zFwdTo), zFwdTo);
23382382
}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)":"");
23472386
}
23482387
}
23492388
if( advancedMenu ){
23502389
style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0);
23512390
}
@@ -2815,13 +2854,13 @@
28152854
blob_append_sql(&cond,
28162855
" AND (event.comment LIKE '%%%q%%' OR event.brief LIKE '%%%q%%')",
28172856
zSearch, zSearch);
28182857
}
28192858
}
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);
28232862
blob_append_sql(&sql, "%s", blob_sql_text(&cond));
28242863
if( rAfter>0.0 ){
28252864
if( rBefore>0.0 ){
28262865
blob_append_sql(&sql,
28272866
" AND event.mtime>=%.17g AND event.mtime<=%.17g\n"
@@ -2958,11 +2997,11 @@
29582997
zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/");
29592998
if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){
29602999
zDate = mprintf("%s", (zAfter ? zAfter : zBefore));
29613000
}
29623001
if( zDate ){
2963
- rDate = symbolic_name_to_mtime(zDate, 0);
3002
+ rDate = symbolic_name_to_mtime(zDate, 0, 0);
29643003
if( db_int(0,
29653004
"SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
29663005
" WHERE blob.rid=event.objid AND mtime<=%.17g%s)",
29673006
rDate-ONE_SECOND, blob_sql_text(&cond))
29683007
){
@@ -2974,11 +3013,11 @@
29743013
zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/");
29753014
if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){
29763015
zDate = mprintf("%s", (zBefore ? zBefore : zAfter));
29773016
}
29783017
if( zDate ){
2979
- rDate = symbolic_name_to_mtime(zDate, 0);
3018
+ rDate = symbolic_name_to_mtime(zDate, 0, 0);
29803019
if( db_int(0,
29813020
"SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
29823021
" WHERE blob.rid=event.objid AND mtime>=%.17g%s)",
29833022
rDate+ONE_SECOND, blob_sql_text(&cond))
29843023
){
@@ -3017,11 +3056,11 @@
30173056
style_submenu_element("Advanced", "%s",
30183057
url_render(&url, "advm", "1", "udc", "1"));
30193058
}
30203059
if( PB("showid") ) tmFlags |= TIMELINE_SHOWRID;
30213060
if( useDividers && zMark && zMark[0] ){
3022
- double r = symbolic_name_to_mtime(zMark, 0);
3061
+ double r = symbolic_name_to_mtime(zMark, 0, 0);
30233062
if( r>0.0 && !selectedRid ) selectedRid = timeline_add_divider(r);
30243063
}
30253064
blob_zero(&sql);
30263065
if( PB("oldestfirst") ){
30273066
db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby ASC /*scan*/");
@@ -3383,22 +3422,56 @@
33833422
fossil_print("+++ no more data (%d) +++\n", nEntry);
33843423
}
33853424
}
33863425
if( fchngQueryInit ) db_finalize(&fchngQuery);
33873426
}
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
+}
33883460
33893461
/*
33903462
** Return a pointer to a static string that forms the basis for
33913463
** a timeline query for display on a TTY.
33923464
*/
33933465
const char *timeline_query_for_tty(void){
3466
+ static int once = 0;
33943467
static const char zBaseSql[] =
33953468
@ SELECT
33963469
@ blob.rid AS rid,
33973470
@ uuid,
33983471
@ datetime(event.mtime,toLocal()) AS mDateTime,
3399
- @ coalesce(ecomment,comment)
3472
+ @ wiki_to_text(coalesce(ecomment,comment))
34003473
@ || ' (user: ' || coalesce(euser,user,'?')
34013474
@ || (SELECT case when length(x)>0 then ' tags: ' || x else '' end
34023475
@ FROM (SELECT group_concat(substr(tagname,5), ', ') AS x
34033476
@ FROM tag, tagxref
34043477
@ WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
@@ -3422,10 +3495,15 @@
34223495
@ AND tagxref.tagtype>0
34233496
@ AND tagxref.rid=blob.rid
34243497
@ WHERE blob.rid=event.objid
34253498
@ AND tag.tagname='branch'
34263499
;
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
+ }
34273505
return zBaseSql;
34283506
}
34293507
34303508
/*
34313509
** Return true if the input string is a date in the ISO 8601 format:
34323510
--- 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 @@
765765
}
766766
}
767767
if( !showTimeline && g.perm.Hyperlink ){
768768
style_submenu_element("Timeline", "%R/info/%T", zUuid);
769769
}
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
+ }
770776
if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br>\n", -1);
771777
ticket_init();
772778
initializeVariablesFromCGI();
773779
getAllTicketFields();
774780
initializeVariablesFromDb();
@@ -777,13 +783,10 @@
777783
if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW_SCRIPT<br>\n", -1);
778784
safe_html_context(DOCSRC_TICKET);
779785
Th_Render(zScript);
780786
if( g.thTrace ) Th_Trace("END_TKTVIEW<br>\n", -1);
781787
782
- zFullName = db_text(0,
783
- "SELECT tkt_uuid FROM ticket"
784
- " WHERE tkt_uuid GLOB '%q*'", zUuid);
785788
if( zFullName ){
786789
attachment_list(zFullName, "<h2>Attachments:</h2>", 1);
787790
}
788791
789792
style_finish_page();
790793
--- 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 @@
505505
@ </tr>
506506
@ <tr><td class="tktDspLabel">Version&nbsp;Found&nbsp;In:</td>
507507
@ <td colspan="3" valign="top" class="tktDspValue">
508508
@ $<foundin>
509509
@ </td></tr>
510
+@ </table>
511
+@
512
+@ <th1>
513
+@ wiki_assoc "ticket" $tkt_uuid
514
+@ </th1>
510515
@
516
+@ <table cellpadding="5" style="min-width:100%">
511517
@ <th1>
512518
@ if {[info exists comment]} {
513519
@ if {[string length $comment]>10} {
514520
@ html {
515521
@ <tr><td class="tktDspLabel">Description:</td></tr>
@@ -531,11 +537,11 @@
531537
@ FROM ticketchng
532538
@ WHERE tkt_id=$tkt_id AND length(icomment)>0} {
533539
@ if {$seenRow} {
534540
@ html "<hr>\n"
535541
@ } 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"
537543
@ html "<tr><td colspan='5' class='tktDspValue'>\n"
538544
@ set seenRow 1
539545
@ }
540546
@ html "<span class='tktDspCommenter'>"
541547
@ html "[htmlize $xlogin]"
542548
--- src/tktsetup.c
+++ src/tktsetup.c
@@ -505,11 +505,17 @@
505 @ </tr>
506 @ <tr><td class="tktDspLabel">Version&nbsp;Found&nbsp;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&nbsp;Found&nbsp;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
--- src/unversioned.c
+++ src/unversioned.c
@@ -218,12 +218,12 @@
218218
}
219219
return 0;
220220
}
221221
222222
/*
223
-** COMMAND: uv#
224
-** COMMAND: unversioned
223
+** COMMAND: uv# abbrv-subcom
224
+** COMMAND: unversioned abbrv-subcom
225225
**
226226
** Usage: %fossil unversioned SUBCOMMAND ARGS...
227227
** or: %fossil uv SUBCOMMAND ARGS..
228228
**
229229
** Unversioned files (UV-files) are artifacts that are synced and are available
@@ -266,10 +266,11 @@
266266
** URL.
267267
**
268268
** Options:
269269
** -v|--verbose Extra diagnostic output
270270
** -n|--dry-run Show what would have happened
271
+** --proxy PROXY Use the specified HTTP proxy
271272
**
272273
** remove|rm|delete FILE ...
273274
** Remove unversioned files from the local repository.
274275
** Changes are not pushed to other repositories until
275276
** the next sync.
@@ -285,10 +286,11 @@
285286
** The remote account requires the 'y' capability.
286287
**
287288
** Options:
288289
** -v|--verbose Extra diagnostic output
289290
** -n|--dry-run Show what would have happened
291
+** --proxy PROXY Use the specified HTTP proxy
290292
**
291293
** touch FILE ... Update the TIMESTAMP on all of the listed files
292294
**
293295
** Options:
294296
** --mtime TIMESTAMP Use TIMESTAMP instead of "now" for the "add",
295297
--- 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 @@
101101
** used for merging, named *-baseline, *-original,
102102
** and *-merge.
103103
** --latest Acceptable in place of VERSION, update to
104104
** latest version
105105
** --nosync Do not auto-sync prior to update
106
+** --proxy PROXY Use PROXY as http proxy during sync operation
106107
** --setmtime Set timestamps of all files to match their
107108
** SCM-side times (the timestamp of the last
108109
** check-in which modified them).
109110
** -v|--verbose Print status information about all files
110111
** -W|--width WIDTH Width of lines (default is to auto-detect).
@@ -198,11 +199,11 @@
198199
}
199200
}
200201
}
201202
202203
/* 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,
204205
** look for one from the same branch as the current version. If there
205206
** are still multiple descendants, show them all and refuse to update
206207
** until the user selects one.
207208
*/
208209
if( tid==0 ){
209210
--- 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 @@
298298
** Prompt the user to enter a single line of text.
299299
*/
300300
void prompt_user(const char *zPrompt, Blob *pIn){
301301
char *z;
302302
char zLine[1000];
303
- blob_zero(pIn);
303
+ blob_init(pIn, 0, 0);
304304
fossil_force_newline();
305305
fossil_print("%s", zPrompt);
306306
fflush(stdout);
307307
z = fgets(zLine, sizeof(zLine), stdin);
308308
if( z ){
@@ -331,12 +331,11 @@
331331
** > fossil user default ?USERNAME?
332332
**
333333
** Query or set the default user. The default user is the
334334
** user for command-line interaction.
335335
**
336
-** > fossil user list
337
-** > fossil user ls
336
+** > fossil user list | ls
338337
**
339338
** List all users known to the repository
340339
**
341340
** > fossil user new ?USERNAME? ?CONTACT-INFO? ?PASSWORD?
342341
**
343342
--- 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 @@
2525
#ifdef _WIN32
2626
# include <windows.h>
2727
#endif
2828
#include "cygsup.h"
2929
30
-#if defined(_WIN32) || defined(__CYGWIN__)
30
+#if defined(_WIN32)
3131
/*
3232
** Translate MBCS to UTF-8. Return a pointer to the translated text.
3333
** Call fossil_mbcs_free() to deallocate any memory used to store the
3434
** returned pointer when done.
3535
*/
3636
--- 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 @@
670670
** Search algorithm:
671671
** (1) The local "editor" setting
672672
** (2) The global "editor" setting
673673
** (3) The VISUAL environment variable
674674
** (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,
676677
*/
677678
const char *fossil_text_editor(void){
678679
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;
679684
if( zEditor==0 ){
680685
zEditor = fossil_getenv("VISUAL");
681686
}
682687
if( zEditor==0 ){
683688
zEditor = fossil_getenv("EDITOR");
684689
}
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;
693698
return zEditor;
694699
}
695700
696701
/*
697702
** Construct a temporary filename.
@@ -895,35 +900,76 @@
895900
return n< 10 ? 1 : n< 100 ? 2 : n< 1000 ? 3
896901
: n< 10000 ? 4 : n< 100000 ? 5 : n< 1000000 ? 6
897902
: n<10000000 ? 7 : n<100000000 ? 8 : n<1000000000 ? 9 : 10;
898903
}
899904
900
-#if !defined(_WIN32)
901
-#if !defined(__DARWIN__) && !defined(__APPLE__) && !defined(__HAIKU__)
902905
/*
903906
** Search for an executable on the PATH environment variable.
904907
** 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.
905911
*/
906
-static int binaryOnPath(const char *zBinary){
912
+int fossil_app_on_path(const char *zBinary, int ePrint){
907913
const char *zPath = fossil_getenv("PATH");
908914
char *zFull;
909915
int i;
910916
int bExists;
917
+ int bFound = 0;
911918
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
912930
while( zPath[0]==':' ) zPath++;
913931
for(i=0; zPath[i] && zPath[i]!=':'; i++){}
914932
zFull = mprintf("%.*s/%s", i, zPath, zBinary);
915933
bExists = file_access(zFull, X_OK);
934
+#endif
935
+ if( bExists==0 && ePrint ){
936
+ fossil_print("%s\n", zFull);
937
+ }
916938
fossil_free(zFull);
917
- if( bExists==0 ) return 1;
939
+ if( bExists==0 ){
940
+ if( ePrint<2 ) return 1;
941
+ bFound = 1;
942
+ }
918943
zPath += i;
919944
}
920
- return 0;
945
+ return bFound;
921946
}
922
-#endif
923
-#endif
924947
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
+}
925971
926972
/*
927973
** Return the name of a command that will launch a web-browser.
928974
*/
929975
const char *fossil_web_browser(void){
@@ -938,11 +984,11 @@
938984
static const char *const azBrowserProg[] =
939985
{ "xdg-open", "gnome-open", "firefox", "google-chrome" };
940986
int i;
941987
zBrowser = "echo";
942988
for(i=0; i<count(azBrowserProg); i++){
943
- if( binaryOnPath(azBrowserProg[i]) ){
989
+ if( fossil_app_on_path(azBrowserProg[i],0) ){
944990
zBrowser = azBrowserProg[i];
945991
break;
946992
}
947993
}
948994
zBrowser = mprintf("%s 2>/dev/null", zBrowser);
949995
--- 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 @@
2121
#include "config.h"
2222
#include <assert.h>
2323
#include <ctype.h>
2424
#include "wiki.h"
2525
26
+#define has_prefix(literal_prfx, zStr) \
27
+ (fossil_strncmp((zStr), "" literal_prfx, (sizeof literal_prfx)-1)==0)
28
+
2629
/*
2730
** Return true if the input string is a well-formed wiki page name.
2831
**
2932
** Well-formed wiki page names do not begin or end with whitespace,
3033
** and do not contain tabs or other control characters and do not
@@ -410,29 +413,33 @@
410413
# define WIKITYPE_UNKNOWN (-1)
411414
# define WIKITYPE_NORMAL 0
412415
# define WIKITYPE_BRANCH 1
413416
# define WIKITYPE_CHECKIN 2
414417
# define WIKITYPE_TAG 3
418
+# define WIKITYPE_TICKET 4
415419
#endif
416420
417421
/*
418422
** Figure out what type of wiki page we are dealing with.
419423
*/
420424
int wiki_page_type(const char *zPageName){
421425
if( db_get_boolean("wiki-about",1)==0 ){
422426
return WIKITYPE_NORMAL;
423427
}else
424
- if( sqlite3_strglob("checkin/*", zPageName)==0
428
+ if( has_prefix("checkin/", zPageName)
425429
&& db_exists("SELECT 1 FROM blob WHERE uuid=%Q",zPageName+8)
426430
){
427431
return WIKITYPE_CHECKIN;
428432
}else
429
- if( sqlite3_strglob("branch/*", zPageName)==0 ){
433
+ if( has_prefix("branch/", zPageName) ){
430434
return WIKITYPE_BRANCH;
431435
}else
432
- if( sqlite3_strglob("tag/*", zPageName)==0 ){
436
+ if( has_prefix("tag/", zPageName) ){
433437
return WIKITYPE_TAG;
438
+ }else
439
+ if( has_prefix("ticket/", zPageName) ){
440
+ return WIKITYPE_TICKET;
434441
}
435442
return WIKITYPE_NORMAL;
436443
}
437444
438445
/*
@@ -442,10 +449,11 @@
442449
const char * wiki_page_type_name(const char *zPageName){
443450
switch(wiki_page_type(zPageName)){
444451
case WIKITYPE_CHECKIN: return "checkin";
445452
case WIKITYPE_BRANCH: return "branch";
446453
case WIKITYPE_TAG: return "tag";
454
+ case WIKITYPE_TICKET: return "ticket";
447455
case WIKITYPE_NORMAL:
448456
default: return "normal";
449457
}
450458
}
451459
@@ -501,10 +509,20 @@
501509
style_header("Notes About Tag %h", zPageName);
502510
style_submenu_element("Tag Timeline","%R/timeline?t=%t",zPageName);
503511
}
504512
break;
505513
}
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
+ }
506524
}
507525
return eType;
508526
}
509527
510528
/*
@@ -516,15 +534,19 @@
516534
*/
517535
static int wiki_special_permission(const char *zPageName){
518536
if( strncmp(zPageName,"branch/",7)!=0
519537
&& strncmp(zPageName,"checkin/",8)!=0
520538
&& strncmp(zPageName,"tag/",4)!=0
539
+ && strncmp(zPageName,"ticket/",7)!=0
521540
){
522541
return 1;
523542
}
524543
if( db_get_boolean("wiki-about",1)==0 ){
525544
return 1;
545
+ }
546
+ if( strncmp(zPageName,"ticket/",7)==0 ){
547
+ return g.perm.WrTkt;
526548
}
527549
return g.perm.Write;
528550
}
529551
530552
/*
@@ -1968,11 +1990,12 @@
19681990
style_submenu_element("All", "%R/wcontent?all=1");
19691991
}
19701992
cgi_check_for_malice();
19711993
showCkBr = db_exists(
19721994
"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/*' ) "
19741997
" AND TYPEOF(tagxref.value+0)='integer'" );
19751998
if( showCkBr ){
19761999
showCkBr = P("showckbr")!=0;
19772000
style_submenu_checkbox("showckbr", "Show associated wikis", 0, 0);
19782001
}
@@ -1998,18 +2021,20 @@
19982021
char *zAge;
19992022
int wcnt = db_column_int(&q, 4);
20002023
char *zWDisplayName;
20012024
20022025
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) )){
20052030
continue;
20062031
}
2007
- if( sqlite3_strglob("checkin/*", zWName)==0 ){
2032
+ if( has_prefix("checkin/",zWName) || has_prefix("ticket/",zWName) ){
20082033
zWDisplayName = mprintf("%.25s...", zWName);
20092034
}else{
2010
- zWDisplayName = mprintf("%s", zWName);
2035
+ zWDisplayName = fossil_strdup(zWName);
20112036
}
20122037
if( wrid==0 ){
20132038
if( !showAll ) continue;
20142039
@ <tr><td data-sortkey="%h(zSort)">\
20152040
@ %z(href("%R/whistory?name=%T",zWName))<s>%h(zWDisplayName)</s></a></td>
@@ -2501,12 +2526,14 @@
25012526
const int wrid = db_column_int(&q, 2);
25022527
if(!showAll && !wrid){
25032528
continue;
25042529
}
25052530
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) ) ){
25082535
continue;
25092536
}
25102537
if( showIds ){
25112538
const char *zUuid = db_column_text(&q, 1);
25122539
fossil_print("%s ",zUuid);
@@ -2551,17 +2578,30 @@
25512578
}
25522579
25532580
/*
25542581
** Add an "Wiki" button in a submenu that links to the read-wiki page.
25552582
*/
2556
-static void wiki_submenu_to_edit_wiki(
2583
+static void wiki_submenu_to_read_wiki(
25572584
const char *zPrefix, /* "branch", "tag", or "checkin" */
25582585
const char *zName, /* Name of the object */
25592586
unsigned int mFlags /* Zero or more WIKIASSOC_* flags */
25602587
){
25612588
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);
25632603
}
25642604
}
25652605
25662606
/*
25672607
** Check to see if there exists a wiki page with a name zPrefix/zName.
@@ -2569,11 +2609,11 @@
25692609
** return true.
25702610
**
25712611
** If there is no such wiki page, return false.
25722612
*/
25732613
int wiki_render_associated(
2574
- const char *zPrefix, /* "branch", "tag", or "checkin" */
2614
+ const char *zPrefix, /* "branch", "tag", "ticket", or "checkin" */
25752615
const char *zName, /* Name of the object */
25762616
unsigned int mFlags /* Zero or more WIKIASSOC_* flags */
25772617
){
25782618
int rid;
25792619
Manifest *pWiki;
@@ -2601,10 +2641,11 @@
26012641
if( blob_size(&title) ){
26022642
@ <div class="section accordion">%h(blob_str(&title))</div>
26032643
}else{
26042644
wiki_section_label(zPrefix, zName, mFlags);
26052645
}
2646
+ wiki_submenu_to_read_wiki(zPrefix, zName, mFlags);
26062647
wiki_submenu_to_edit_wiki(zPrefix, zName, mFlags);
26072648
@ <div class="accordion_panel">
26082649
safe_html_context(DOCSRC_WIKI);
26092650
safe_html(&tail);
26102651
convert_href_and_output(&tail);
26112652
--- 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 @@
2323
2424
#if INTERFACE
2525
/*
2626
** Allowed wiki transformation operations
2727
*/
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: &lt;) */
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 */
4061
4162
4263
/*
4364
** These are the only markup attributes allowed.
4465
*/
@@ -444,20 +465,20 @@
444465
#define AT_NEWLINE 0x0010000 /* At start of a line */
445466
#define AT_PARAGRAPH 0x0020000 /* At start of a paragraph */
446467
#define ALLOW_WIKI 0x0040000 /* Allow wiki markup */
447468
#define ALLOW_LINKS 0x0080000 /* Allow [...] hyperlinks */
448469
#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> */
451471
452472
/*
453473
** Current state of the rendering engine
454474
*/
455475
typedef struct Renderer Renderer;
456476
struct Renderer {
457477
Blob *pOut; /* Output appended to this blob */
458478
int state; /* Flag that govern rendering */
479
+ int mRender; /* Mask of RENDER_* values to return */
459480
unsigned renderFlags; /* Flags from the client */
460481
int wikiList; /* Current wiki list type */
461482
int inVerbatim; /* True in <verbatim> mode */
462483
int preVerbState; /* Value of state prior to verbatim */
463484
int wantAutoParagraph; /* True if a <p> is desired */
@@ -679,21 +700,26 @@
679700
static int nextWikiToken(const char *z, Renderer *p, int *pTokenType){
680701
int n;
681702
if( z[0]=='<' ){
682703
n = html_tag_length(z);
683704
if( n>0 ){
705
+ p->mRender |= RENDER_TAG;
684706
*pTokenType = TOKEN_MARKUP;
685707
return n;
686708
}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)) ){
687717
*pTokenType = TOKEN_CHARACTER;
688718
return 1;
689719
}
690720
}
691
- if( z[0]=='&' && (p->inVerbatim || !isElement(z)) ){
692
- *pTokenType = TOKEN_CHARACTER;
693
- return 1;
694
- }
695721
if( (p->state & ALLOW_WIKI)!=0 ){
696722
if( z[0]=='\n' ){
697723
n = paragraphBreakLength(z);
698724
if( n>0 ){
699725
*pTokenType = TOKEN_PARAGRAPH;
@@ -725,17 +751,31 @@
725751
if( n>0 ){
726752
*pTokenType = TOKEN_INDENT;
727753
return n;
728754
}
729755
}
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 ){
731769
*pTokenType = TOKEN_LINK;
732770
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;
733776
}
734
- }else if( (p->state & ALLOW_LINKS)!=0 && z[0]=='[' && (n = linkLength(z))>0 ){
735
- *pTokenType = TOKEN_LINK;
736
- return n;
737777
}
738778
*pTokenType = TOKEN_TEXT;
739779
return 1 + textLength(z+1, p->state);
740780
}
741781
@@ -745,13 +785,20 @@
745785
** z points to the start of a token. Return the number of
746786
** characters in that token. Write the token type into *pTokenType.
747787
*/
748788
static int nextRawToken(const char *z, Renderer *p, int *pTokenType){
749789
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
+ }
753800
}
754801
*pTokenType = TOKEN_RAW;
755802
return 1 + textLength(z+1, p->state);
756803
}
757804
@@ -1248,12 +1295,16 @@
12481295
** [wiki:WikiPageName]
12491296
**
12501297
** [2010-02-27 07:13]
12511298
**
12521299
** [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.
12531304
*/
1254
-void wiki_resolve_hyperlink(
1305
+int wiki_resolve_hyperlink(
12551306
Blob *pOut, /* Write the HTML output here */
12561307
int mFlags, /* Rendering option flags */
12571308
const char *zTarget, /* Hyperlink target; text within [...] */
12581309
char *zClose, /* Write hyperlink closing text here */
12591310
int nClose, /* Bytes available in zClose[] */
@@ -1263,10 +1314,11 @@
12631314
const char *zTerm = "</a>";
12641315
const char *z;
12651316
char *zExtra = 0;
12661317
const char *zExtraNS = 0;
12671318
char *zRemote = 0;
1319
+ int rc = 0;
12681320
12691321
if( zTitle ){
12701322
zExtra = mprintf(" title='%h'", zTitle);
12711323
zExtraNS = zExtra+1;
12721324
}else if( mFlags & WIKI_TARGET_BLANK ){
@@ -1316,15 +1368,20 @@
13161368
}
13171369
}
13181370
}else if( !in_this_repo(zTarget) ){
13191371
if( (mFlags & (WIKI_LINKSONLY|WIKI_NOBADLINKS))!=0 ){
13201372
zTerm = "";
1373
+ }else if( (mFlags & WIKI_MARK)!=0 ){
1374
+ blob_appendf(pOut, "<mark>%s", zLB);
1375
+ zTerm = "]</mark>";
1376
+ rc |= RENDER_MARK;
13211377
}else{
13221378
blob_appendf(pOut, "<span class=\"brokenlink\">%s", zLB);
13231379
zTerm = "]</span>";
13241380
}
1325
- }else if( g.perm.Hyperlink ){
1381
+ rc |= RENDER_BADTARGET;
1382
+ }else if( g.perm.Hyperlink || (mFlags & WIKI_ADMIN)!=0 ){
13261383
blob_appendf(pOut, "%z%s",xhref(zExtraNS, "%R/info/%s", zTarget), zLB);
13271384
zTerm = "]</a>";
13281385
}else{
13291386
zTerm = "";
13301387
}
@@ -1340,33 +1397,54 @@
13401397
}else{
13411398
blob_appendf(pOut, "<a href=\"%R/wiki?name=%T\"%s>", z, zExtra);
13421399
}
13431400
}else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-'
13441401
&& 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
13461403
** timeline for that date */
13471404
blob_appendf(pOut, "<a href=\"%R/timeline?c=%T\"%s>", zTarget, zExtra);
13481405
}else if( mFlags & WIKI_MARKDOWNLINKS ){
13491406
/* If none of the above, and if rendering links for markdown, then
13501407
** create a link to the literal text of the target */
13511408
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;
13521413
}else if( zOrig && zTarget>=&zOrig[2]
13531414
&& zTarget[-1]=='[' && !fossil_isspace(zTarget[-2]) ){
13541415
/* If the hyperlink markup is not preceded by whitespace, then it
13551416
** is probably a C-language subscript or similar, not really a
13561417
** hyperlink. Just ignore it. */
13571418
zTerm = "";
13581419
}else if( (mFlags & (WIKI_NOBADLINKS|WIKI_LINKSONLY))!=0 ){
13591420
/* Also ignore the link if various flags are set */
13601421
zTerm = "";
1422
+ rc |= RENDER_BADTARGET;
13611423
}else{
13621424
blob_appendf(pOut, "<span class=\"brokenlink\">[%h]", zTarget);
13631425
zTerm = "</span>";
1426
+ rc |= RENDER_BADTARGET;
13641427
}
13651428
if( zExtra ) fossil_free(zExtra);
13661429
assert( (int)strlen(zTerm)<nClose );
13671430
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(&notUsed, 0, 0);
1442
+ wiki_resolve_hyperlink(&notUsed, WIKI_NOBADLINKS|WIKI_ADMIN,
1443
+ zTarget, zClose, sizeof(zClose)-1, 0, 0);
1444
+ blob_reset(&notUsed);
1445
+ return zClose[0]!=0;
13681446
}
13691447
13701448
/*
13711449
** Check to see if the given parsed markup is the correct
13721450
** </verbatim> tag.
@@ -1450,11 +1528,10 @@
14501528
*/
14511529
static void wiki_render(Renderer *p, char *z){
14521530
int tokenType;
14531531
ParsedMarkup markup;
14541532
int n;
1455
- int inlineOnly = (p->state & INLINE_MARKUP_ONLY)!=0;
14561533
int wikiHtmlOnly = (p->state & (WIKI_HTMLONLY | WIKI_LINKSONLY))!=0;
14571534
int linksOnly = (p->state & WIKI_LINKSONLY)!=0;
14581535
char *zOrig = z;
14591536
14601537
/* Make sure the attribute constants and names still align
@@ -1468,22 +1545,17 @@
14681545
n = nextWikiToken(z, p, &tokenType);
14691546
}
14701547
p->state &= ~(AT_NEWLINE|AT_PARAGRAPH);
14711548
switch( tokenType ){
14721549
case TOKEN_PARAGRAPH: {
1473
- if( inlineOnly ){
1474
- /* blob_append_string(p->pOut, " &para; "); */
1475
- blob_append_string(p->pOut, " &nbsp;&nbsp; ");
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;
14851557
p->state |= AT_PARAGRAPH|AT_NEWLINE;
14861558
break;
14871559
}
14881560
case TOKEN_NEWLINE: {
14891561
if( p->renderFlags & WIKI_NEWLINE ){
@@ -1493,86 +1565,91 @@
14931565
}
14941566
p->state |= AT_NEWLINE;
14951567
break;
14961568
}
14971569
case TOKEN_BUL_LI: {
1498
- if( inlineOnly ){
1499
- blob_append_string(p->pOut, " &bull; ");
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>");
15151584
break;
15161585
}
15171586
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>");
15351601
break;
15361602
}
15371603
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));
15551618
break;
15561619
}
15571620
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;
15651627
break;
15661628
}
15671629
case TOKEN_CHARACTER: {
15681630
startAutoParagraph(p);
1631
+ if( p->state & WIKI_MARK ){
1632
+ blob_append_string(p->pOut, "<mark>");
1633
+ p->mRender |= RENDER_MARK;
1634
+ }
15691635
if( z[0]=='<' ){
1636
+ p->mRender |= RENDER_BADTAG;
15701637
blob_append_string(p->pOut, "&lt;");
15711638
}else if( z[0]=='&' ){
1639
+ p->mRender |= RENDER_BADENTITY;
15721640
blob_append_string(p->pOut, "&amp;");
15731641
}
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
+ }
15741651
break;
15751652
}
15761653
case TOKEN_LINK: {
15771654
char *zTarget;
15781655
char *zDisplay = 0;
@@ -1581,10 +1658,11 @@
15811658
char zClose[20];
15821659
char cS1 = 0;
15831660
int iS1 = 0;
15841661
15851662
startAutoParagraph(p);
1663
+ p->mRender |= RENDER_LINK;
15861664
zTarget = &z[1];
15871665
for(i=1; z[i] && z[i]!=']'; i++){
15881666
if( z[i]=='|' && zDisplay==0 ){
15891667
zDisplay = &z[i+1];
15901668
for(j=i; j>0 && fossil_isspace(z[j-1]); j--){}
@@ -1597,11 +1675,11 @@
15971675
if( zDisplay==0 ){
15981676
zDisplay = zTarget + interwiki_removable_prefix(zTarget);
15991677
}else{
16001678
while( fossil_isspace(*zDisplay) ) zDisplay++;
16011679
}
1602
- wiki_resolve_hyperlink(p->pOut, p->state,
1680
+ p->mRender |= wiki_resolve_hyperlink(p->pOut, p->state,
16031681
zTarget, zClose, sizeof(zClose), zOrig, 0);
16041682
if( linksOnly || zClose[0]==0 || p->inVerbatim ){
16051683
if( cS1 ) z[iS1] = cS1;
16061684
if( zClose[0]!=']' ){
16071685
blob_appendf(p->pOut, "[%h]%s", zTarget, zClose);
@@ -1687,14 +1765,22 @@
16871765
16881766
/* Render invalid markup literally. The markup appears in the
16891767
** final output as plain text.
16901768
*/
16911769
if( markup.iCode==MARKUP_INVALID ){
1770
+ p->mRender |= RENDER_BADTAG;
16921771
unparseMarkup(&markup);
16931772
startAutoParagraph(p);
1694
- blob_append_string(p->pOut, "&lt;");
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, "&lt;");
1780
+ htmlize_to_blob(p->pOut, z+1, n-1);
1781
+ }
16961782
}else
16971783
16981784
/* If the markup is not font-change markup ignore it if the
16991785
** font-change-only flag is set.
17001786
*/
@@ -1708,16 +1794,10 @@
17081794
}else{
17091795
p->state &= ~ALLOW_WIKI;
17101796
}
17111797
}else
17121798
1713
- /* Ignore block markup for in-line rendering.
1714
- */
1715
- if( inlineOnly && (markup.iType&MUTYPE_INLINE)==0 ){
1716
- /* Do nothing */
1717
- }else
1718
-
17191799
/* Generate end-tags */
17201800
if( markup.endTag ){
17211801
popStackToTag(p, markup.iCode);
17221802
}else
17231803
@@ -1799,10 +1879,11 @@
17991879
}else
18001880
{
18011881
if( markup.iType==MUTYPE_FONT ){
18021882
startAutoParagraph(p);
18031883
}else if( markup.iType==MUTYPE_BLOCK || markup.iType==MUTYPE_LIST ){
1884
+ p->mRender |= RENDER_BLOCKTAG;
18041885
p->wantAutoParagraph = 0;
18051886
}
18061887
if( markup.iCode==MARKUP_HR
18071888
|| markup.iCode==MARKUP_H1
18081889
|| markup.iCode==MARKUP_H2
@@ -1829,12 +1910,14 @@
18291910
** Transform the text in the pIn blob. Write the results
18301911
** into the pOut blob. The pOut blob should already be
18311912
** initialized. The output is merely appended to pOut.
18321913
** If pOut is NULL, then the output is appended to the CGI
18331914
** reply.
1915
+**
1916
+** Return a mask of RENDER_ flags indicating what happened.
18341917
*/
1835
-void wiki_convert(Blob *pIn, Blob *pOut, int flags){
1918
+int wiki_convert(Blob *pIn, Blob *pOut, int flags){
18361919
Renderer renderer;
18371920
18381921
memset(&renderer, 0, sizeof(renderer));
18391922
renderer.renderFlags = flags;
18401923
renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH|flags;
@@ -1858,10 +1941,11 @@
18581941
while( renderer.nStack ){
18591942
popStack(&renderer);
18601943
}
18611944
blob_append_char(renderer.pOut, '\n');
18621945
free(renderer.aStack);
1946
+ return renderer.mRender;
18631947
}
18641948
18651949
/*
18661950
** COMMAND: test-wiki-render
18671951
**
@@ -1870,36 +1954,85 @@
18701954
** Translate the input FILE from Fossil-wiki into HTML and write
18711955
** the resulting HTML on standard output.
18721956
**
18731957
** Options:
18741958
** --buttons Set the WIKI_BUTTONS flag
1959
+** --dark-pikchr Render pikchrs in dark mode
1960
+** --flow Render as text using comment_format
18751961
** --htmlonly Set the WIKI_HTMLONLY flag
1876
-** --linksonly Set the WIKI_LINKSONLY flag
1877
-** --nobadlinks Set the WIKI_NOBADLINKS flag
18781962
** --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()
18811969
*/
18821970
void test_wiki_render(void){
18831971
Blob in, out;
18841972
int flags = 0;
1973
+ int bText;
1974
+ int bFlow = 0;
1975
+ int showType = 0;
1976
+ int mType;
1977
+ const char *zIn;
18851978
if( find_option("buttons",0,0)!=0 ) flags |= WIKI_BUTTONS;
18861979
if( find_option("htmlonly",0,0)!=0 ) flags |= WIKI_HTMLONLY;
18871980
if( find_option("linksonly",0,0)!=0 ) flags |= WIKI_LINKSONLY;
18881981
if( find_option("nobadlinks",0,0)!=0 ) flags |= WIKI_NOBADLINKS;
18891982
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;
18911984
if( find_option("dark-pikchr",0,0)!=0 ){
18921985
pikchr_to_html_add_flags( PIKCHR_PROCESS_DARK_MODE );
18931986
}
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);
18941991
db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
18951992
verify_all_options();
1896
- if( g.argc!=3 ) usage("FILE");
1993
+ if( (zIn==0 && g.argc!=3) || (zIn!=0 && g.argc!=2) ) usage("FILE");
18971994
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
+ }
19012034
}
19022035
19032036
/*
19042037
** COMMAND: test-markdown-render
19052038
**
@@ -1906,24 +2039,26 @@
19062039
** Usage: %fossil test-markdown-render FILE ...
19072040
**
19082041
** Render markdown in FILE as HTML on stdout.
19092042
** Options:
19102043
**
1911
-** --safe Restrict the output to use only "safe" HTML
2044
+** --dark-pikchr Render pikchrs in dark mode
19122045
** --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().
19142048
*/
19152049
void test_markdown_render(void){
19162050
Blob in, out;
19172051
int i;
1918
- int bSafe = 0, bFnLint = 0;
2052
+ int bSafe = 0, bFnLint = 0, bText = 0;
19192053
db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
19202054
bSafe = find_option("safe",0,0)!=0;
19212055
bFnLint = find_option("lint-footnotes",0,0)!=0;
19222056
if( find_option("dark-pikchr",0,0)!=0 ){
19232057
pikchr_to_html_add_flags( PIKCHR_PROCESS_DARK_MODE );
19242058
}
2059
+ bText = find_option("text",0,0)!=0;
19252060
verify_all_options();
19262061
for(i=2; i<g.argc; i++){
19272062
blob_zero(&out);
19282063
blob_read_from_file(&in, g.argv[i], ExtFILE);
19292064
if( g.argc>3 ){
@@ -1930,10 +2065,17 @@
19302065
fossil_print("<!------ %h ------->\n", g.argv[i]);
19312066
}
19322067
markdown_to_html(&in, 0, &out);
19332068
safe_html_context( bSafe ? DOCSRC_UNTRUSTED : DOCSRC_TRUSTED );
19342069
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
+ }
19352077
blob_write_to_file(&out, "-");
19362078
blob_reset(&in);
19372079
blob_reset(&out);
19382080
}
19392081
if( bFnLint && (g.ftntsIssues[0] || g.ftntsIssues[1]
@@ -1990,33 +2132,30 @@
19902132
** [target|...]
19912133
**
19922134
** Where "target" can be either an artifact ID prefix or a wiki page
19932135
** name. For each such hyperlink found, add an entry to the
19942136
** backlink table.
2137
+**
2138
+** The return value is a mask of RENDER_ flags.
19952139
*/
1996
-void wiki_extract_links(
2140
+int wiki_extract_links(
19972141
char *z, /* The wiki text from which to extract links */
19982142
Backlink *pBklnk, /* Backlink extraction context */
19992143
int flags /* wiki parsing flags */
20002144
){
20012145
Renderer renderer;
20022146
int tokenType;
20032147
ParsedMarkup markup;
20042148
int n;
2005
- int inlineOnly;
20062149
int wikiHtmlOnly = 0;
20072150
20082151
memset(&renderer, 0, sizeof(renderer));
20092152
renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH;
2010
- if( flags & WIKI_NOBLOCK ){
2011
- renderer.state |= INLINE_MARKUP_ONLY;
2012
- }
20132153
if( wikiUsesHtml() ){
20142154
renderer.state |= WIKI_HTMLONLY;
20152155
wikiHtmlOnly = 1;
20162156
}
2017
- inlineOnly = (renderer.state & INLINE_MARKUP_ONLY)!=0;
20182157
20192158
while( z[0] ){
20202159
if( wikiHtmlOnly ){
20212160
n = nextRawToken(z, &renderer, &tokenType);
20222161
}else{
@@ -2093,16 +2232,10 @@
20932232
}else{
20942233
renderer.state &= ~ALLOW_WIKI;
20952234
}
20962235
}else
20972236
2098
- /* Ignore block markup for in-line rendering.
2099
- */
2100
- if( inlineOnly && (markup.iType&MUTYPE_INLINE)==0 ){
2101
- /* Do nothing */
2102
- }else
2103
-
21042237
/* Generate end-tags */
21052238
if( markup.endTag ){
21062239
popStackToTag(&renderer, markup.iCode);
21072240
}else
21082241
@@ -2141,10 +2274,11 @@
21412274
}
21422275
}
21432276
z += n;
21442277
}
21452278
free(renderer.aStack);
2279
+ return renderer.mRender;
21462280
}
21472281
21482282
/*
21492283
** Return the length, in bytes, of the HTML token that z is pointing to.
21502284
*/
@@ -2393,33 +2527,61 @@
23932527
blob_reset(&in);
23942528
fossil_puts(blob_buffer(&out), 0, blob_size(&out));
23952529
blob_reset(&out);
23962530
}
23972531
}
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
+}
23982554
23992555
/*
24002556
** Remove all HTML markup from the input text. The output written into
24012557
** pOut is pure text.
24022558
**
24032559
** Put the title on the first line, if there is any <title> markup.
24042560
** If there is no <title>, then create a blank first line.
24052561
*/
2406
-void html_to_plaintext(const char *zIn, Blob *pOut){
2562
+void html_to_plaintext(const char *zIn, Blob *pOut, int mFlags){
24072563
int n;
24082564
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;
24142575
while( zIn[0] ){
24152576
n = html_token_length(zIn);
24162577
if( zIn[0]=='<' && n>1 ){
24172578
int isCloseTag;
24182579
int eTag;
24192580
int eType;
24202581
char zTag[32];
2582
+ prevWS = 0;
24212583
isCloseTag = zIn[1]=='/';
24222584
for(i=0, j=1+isCloseTag; i<30 && fossil_isalnum(zIn[j]); i++, j++){
24232585
zTag[i] = fossil_tolower(zIn[j]);
24242586
}
24252587
zTag[i] = 0;
@@ -2433,92 +2595,116 @@
24332595
zIn += n;
24342596
}
24352597
if( zIn[0]=='<' ) zIn += n;
24362598
continue;
24372599
}
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[] = {
24662639
{ 5, '&', "&amp;" },
24672640
{ 4, '<', "&lt;" },
24682641
{ 4, '>', "&gt;" },
24692642
{ 6, ' ', "&nbsp;" },
2643
+ { 6, '"', "&quot;" },
24702644
};
24712645
int jj;
24722646
for(jj=0; jj<count(aEntity); jj++){
24732647
if( aEntity[jj].n==n && strncmp(aEntity[jj].z,zIn,n)==0 ){
24742648
c = aEntity[jj].c;
24752649
break;
24762650
}
24772651
}
24782652
}
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));
24832662
}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));
24882667
}
24892668
}else{
2490
- if( !seenText && !inTitle ) blob_append_char(pOut, '\n');
2491
- seenText = 1;
2492
- nNL = nWS = 0;
2669
+ prevWS = 0;
24932670
blob_append(pOut, zIn, n);
24942671
}
24952672
zIn += n;
24962673
}
2497
- if( nNL==0 ) blob_append_char(pOut, '\n');
2674
+ if( nMark ){
2675
+ addMark(pOut, mFlags, 1);
2676
+ }
24982677
}
24992678
25002679
/*
25012680
** COMMAND: test-html-to-text
25022681
**
2503
-** Usage: %fossil test-html-to-text FILE ...
2682
+** Usage: %fossil test-html-to-text [OPTIONS] FILE ...
25042683
**
25052684
** Read all files named on the command-line. Convert the file
25062685
** content from HTML to text and write the results on standard
25072686
** output.
25082687
**
25092688
** This command is intended as a test and debug interface for
25102689
** 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.
25112695
*/
25122696
void test_html_to_text(void){
25132697
Blob in, out;
25142698
int i;
2699
+ int mFlags = 0;
2700
+ if( find_option("vt100",0,0)!=0 ) mFlags |= HTOT_VT100;
25152701
25162702
for(i=2; i<g.argc; i++){
25172703
blob_read_from_file(&in, g.argv[i], ExtFILE);
25182704
blob_zero(&out);
2519
- html_to_plaintext(blob_str(&in), &out);
2705
+ html_to_plaintext(blob_str(&in), &out, mFlags);
25202706
blob_reset(&in);
25212707
fossil_puts(blob_buffer(&out), 0, blob_size(&out));
25222708
blob_reset(&out);
25232709
}
25242710
}
25252711
--- 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, " &para; "); */
1475 blob_append_string(p->pOut, " &nbsp;&nbsp; ");
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, " &bull; ");
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, "&lt;");
1571 }else if( z[0]=='&' ){
 
1572 blob_append_string(p->pOut, "&amp;");
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, "&lt;");
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, '&', "&amp;" },
2467 { 4, '<', "&lt;" },
2468 { 4, '>', "&gt;" },
2469 { 6, ' ', "&nbsp;" },
 
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: &lt;) */
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(&notUsed, 0, 0);
1442 wiki_resolve_hyperlink(&notUsed, WIKI_NOBADLINKS|WIKI_ADMIN,
1443 zTarget, zClose, sizeof(zClose)-1, 0, 0);
1444 blob_reset(&notUsed);
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, "&lt;");
1638 }else if( z[0]=='&' ){
1639 p->mRender |= RENDER_BADENTITY;
1640 blob_append_string(p->pOut, "&amp;");
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, "&lt;");
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, '&', "&amp;" },
2640 { 4, '<', "&lt;" },
2641 { 4, '>', "&gt;" },
2642 { 6, ' ', "&nbsp;" },
2643 { 6, '"', "&quot;" },
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 @@
996996
**
997997
** -D|--display DISPLAY-NAME
998998
**
999999
** Sets the display name of the service. This name is shown
10001000
** by graphical interface programs. By default, the display name
1001
-** equals to the service name.
1001
+** is equal to the service name.
10021002
**
10031003
** -S|--start TYPE
10041004
**
10051005
** Sets the start type of the service. TYPE can be "manual",
10061006
** which means you need to start the service yourself with the
@@ -1019,11 +1019,11 @@
10191019
** -W|--password PASSWORD
10201020
**
10211021
** Password for the user account.
10221022
**
10231023
** 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:
10251025
**
10261026
** --baseurl URL
10271027
**
10281028
** Use URL as the base (useful for reverse proxies)
10291029
**
10301030
--- 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 @@
652652
pManifest = manifest_get(rid, CFTYPE_MANIFEST, 0);
653653
if( pManifest ){
654654
int flg, eflg = 0;
655655
char *zName = 0;
656656
zip_set_timedate(pManifest->rDate);
657
- flg = db_get_manifest_setting();
657
+ flg = db_get_manifest_setting(blob_str(&hash));
658658
if( flg ){
659659
/* eflg is the effective flags, taking include/exclude into account */
660660
if( (pInclude==0 || glob_match(pInclude, "manifest"))
661661
&& !glob_match(pExclude, "manifest")
662662
&& (flg & MFESTFLG_RAW) ){
663663
664664
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 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- tools/makemake.tcl
+++ tools/makemake.tcl
@@ -567,18 +567,19 @@
567567
$(XTCC) $(PIKCHR_OPTIONS) -c $(SRCDIR_extsrc)/pikchr.c -o $@
568568
569569
$(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c
570570
$(XTCC) -c $(SRCDIR_extsrc)/cson_amalgamation.c -o $@
571571
572
-$(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c
572
+$(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c $(MAKEFILE_LIST)
573573
$(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>>>
576576
-sENVIRONMENT=web <<<NEXT_LINE>>>
577577
-sMODULARIZE <<<NEXT_LINE>>>
578578
-sEXPORT_NAME=initPikchrModule <<<NEXT_LINE>>>
579579
--minify 0
580
+ $(TCLSH) $(TOPDIR)/tools/randomize-js-names.tcl $(SRCDIR_extsrc)
580581
@chmod -x $(SRCDIR_extsrc)/pikchr.wasm
581582
wasm: $(SRCDIR_extsrc)/pikchr.js
582583
583584
#
584585
# compile_commands.json support...
585586
--- 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 @@
8585
8686
/***************************************************************************
8787
** These macros must match similar macros in dispatch.c.
8888
**
8989
** 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 */
105106
/**************************************************************************/
106107
107108
/*
108109
** Each entry looks like this:
109110
*/
@@ -280,10 +281,13 @@
280281
aEntry[nUsed].zVar = string_dup(&zLine[i+9], j-9);
281282
}else if( j==6 && strncmp(&zLine[i], "hidden", 6)==0 ){
282283
aEntry[nUsed].eType |= CMDFLAG_HIDDEN;
283284
}else if( j==14 && strncmp(&zLine[i], "loadavg-exempt", 14)==0 ){
284285
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;
285289
}else{
286290
fprintf(stderr, "%s:%d: unknown option: '%.*s'\n",
287291
zFile, nLine, j, &zLine[i]);
288292
nErr++;
289293
}
290294
291295
ADDED tools/randomize-js-names.tcl
292296
ADDED win/build32.bat
293297
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\""
--- 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
--- 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
--- www/alerts.md
+++ www/alerts.md
@@ -7,10 +7,11 @@
77
88
* New [checkins](/help?cmd=ci)
99
* [Ticket](./tickets.wiki) changes
1010
* [Wiki](./wikitheory.wiki) page changes
1111
* New and edited [forum](./forum.wiki) posts
12
+ * Users receiving [new permissions](./caps/index.md) (admins only)
1213
* Announcements
1314
1415
Subscribers can elect to receive emails as soon as these events happen,
1516
or they can receive a daily digest of the events instead.
1617
@@ -515,10 +516,14 @@
515516
email addresses until the user clicks the link in the verification
516517
email. This checkbox lets the Fossil Admin user manually verify the
517518
user, such as in the case where the verification email message got
518519
lost. Unchecking this box does not cause another verification email
519520
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.
520525
521526
This screen also allows a Fossil Admin user to perform other activities
522527
on behalf of a subscriber which they could do themselves, such as to
523528
[unsubscribe](#unsub) them.
524529
525530
--- 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 @@
340340
found in the help text for the [/help?cmd=test-fuzz|test-fuzz
341341
command].
342342
343343
Fuzzing requires:
344344
345
- * Customizing the build of fossil a small bit.
346345
* The clang C compiler.
347346
* libfuzzer. On Ubuntu-derived systems, it can be installed with
348347
<tt>apt install libfuzzer-XYZ</tt>, where XYZ is a version number
349348
(several versions may be available on any given system)
350349
351350
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
364355
</code></pre>
365356
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
370358
one primarily in that it runs the <tt>test-fuzz</tt> command by
371359
default. It needs to be told what to fuzz and needs to be given a
372360
directory of input files to seed the fuzzer with:
373361
374362
375
-<pre></code>$ mkdir cases
363
+<pre><code>$ mkdir cases
376364
# Copy input files into ./cases. e.g. when fuzzing the markdown
377365
# processor, copy any to-be-tested .md files into that directory.
378366
# Then start the fuzzer:
379367
$ ./fossil-fuzz --fuzztype markdown cases
380368
</code></pre>
381369
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"
383371
directory, each one named in the form of a hash. When it finds a
384372
problem it will produce a stack trace for the offending code, will
385373
output the name of the file which triggered the crash (named
386374
<tt>cases/SOME_HASH</tt>) and may, depending on the nature of the
387375
problem, produce a file named <tt>crash-SOMETHING</tt>. In theory the
@@ -503,11 +491,13 @@
503491
When a new version of <tt>extsrc/pikchr.c</tt> is installed, the
504492
files <tt>pikchr.{js,wasm}</tt> will need to be recompiled to account
505493
for that. Running <tt>make wasm</tt> will, if the build is set up for
506494
the emsdk, recompile those:
507495
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
509499
./tools/emcc.sh -o extsrc/pikchr.js ...
510500
$ ls -la extsrc/pikchr.{js,wasm}
511501
-rw-rw-r-- 1 stephan stephan 17263 Jun 8 03:59 extsrc/pikchr.js
512502
-rw-rw-r-- 1 stephan stephan 97578 Jun 8 03:59 extsrc/pikchr.wasm
513503
</code></pre>
514504
--- 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 @@
7373
of available Fossil repositories.
7474
7575
The "skin" of the reply is determined by the first
7676
repository in the list that has a non-zero
7777
[/help?cmd=repolist-skin|repolist-skin] setting.
78
+
7879
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.
8084
8185
The repolist-generated page recurses into subdirectories and will list
8286
all <tt>*.fossil</tt> files found, with the following exceptions:
8387
8488
* Filenames starting with a period are treated as "hidden" and skipped.
8589
--- 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 @@
11
<title>Change Log</title>
22
33
<h2 id='v2_26'>Changes for version 2.26 (pending)</h2>
44
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>
817
* Added the [/help?cmd=/ckout|/ckout web page] to provide information
918
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".
2459
* Enhancements to the [/help?cmd=/timeline|/timeline page]:
2560
<ol type="a">
2661
<li> Added the "ml=" ("Merge-in List") query parameter that works
2762
like "rl=" ("Related List") but adds "mionly" style related
2863
check-ins instead of the full "rel" style.
@@ -43,20 +78,59 @@
4378
<li> Enhance the "ymd" query parameter so that when used like
4479
"ymd=YYYYMMDD-YYYYMMDD" it shows all events in the range of
4580
dates specified.
4681
<li> Accept the "Z" (Zulu-time) suffix on date arguments for the
4782
"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.
4895
</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.
5299
* Added the [/help?cmd=/clusterlist|/clusterlist page] for analysis
53100
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
+
58132
59133
<h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</h2>
60134
61135
* The "[/help?cmd=ui|fossil ui /]" command now works even for repositories
62136
that have non-ASCII filenames
63137
--- 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 @@
3939
and certain other text outputs are formatted for display. The flags are
4040
individual bits in `NUMBER`, which must be specified in base 10:
4141
4242
* _0_ &mdash; Uses the revised algorithm with no special handling.
4343
44
- * _1_ &mdash; Uses the legacy algorithm, other flags are ignored.
44
+ * _1_ &mdash; Uses the canonical algorithm, other flags are ignored.
4545
4646
* _2_ &mdash; Trims leading and trailing carriage-returns and line-feeds
4747
where they do not materially impact pre-existing formatting
4848
(i.e. at the start of the comment string _and_ right before
4949
line indentation).
@@ -60,10 +60,15 @@
6060
preserving more of the pre-existing formatting.
6161
6262
6363
`--comment-format NUMBER`: Alias for `--comfmtflags NUMBER`.
6464
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
+
6570
6671
`--errorlog ERRLOG`: Name a file to which fossil will log panics,
6772
errors, and warnings.
6873
6974
@@ -143,10 +148,15 @@
143148
144149
145150
`FOSSIL_HOME`: Location of [configuration database][configdb].
146151
See the [configuration database location][configloc] description
147152
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].
148158
149159
`FOSSIL_USE_SEE_TEXTKEY`: If set, treat the encryption key string for
150160
SEE as text to be hashed into the actual encryption key. This has no
151161
effect if Fossil was not compiled with SEE support enabled.
152162
@@ -210,10 +220,14 @@
210220
used as the location of the `~/.fossil` file.
211221
212222
`LOGNAME`: Name of the logged in user on many Unix-like platforms.
213223
Used as the fossil user name if `FOSSIL_USER` is not specified. See
214224
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).
215229
216230
`PATH`: Used by most platforms to locate programs invoked without a
217231
fully qualified name. Explicitly used by `fossil ui` on certain platforms
218232
to choose the browser to launch.
219233
@@ -464,12 +478,12 @@
464478
465479
### Web browser
466480
467481
Occasionally, fossil wants to launch a web browser for the user, most
468482
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.
471485
472486
On all platforms, if the local or global settings `web-browser` is
473487
set, that is the command used to open a URL.
474488
475489
Otherwise, the specific actions vary by platform.
@@ -484,5 +498,6 @@
484498
On Windows platforms, it assumes that `start` is the command to open
485499
a URL in the user's configured default browser.
486500
487501
[configdb]: ./tech_overview.wiki#configdb
488502
[configloc]: ./tech_overview.wiki#configloc
503
+[cgictlfile]: ./cgi.wiki
489504
--- 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_ &mdash; Uses the revised algorithm with no special handling.
43
44 * _1_ &mdash; Uses the legacy algorithm, other flags are ignored.
45
46 * _2_ &mdash; 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_ &mdash; Uses the revised algorithm with no special handling.
43
44 * _1_ &mdash; Uses the canonical algorithm, other flags are ignored.
45
46 * _2_ &mdash; 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
--- www/fileformat.wiki
+++ www/fileformat.wiki
@@ -61,11 +61,11 @@
6161
artifacts:
6262
6363
<ul>
6464
<li> [#manifest | Manifests] </li>
6565
<li> [#cluster | Clusters] </li>
66
-<li> [#ctrl | Control Artifacts] </li>
66
+<li> [#ctrl | Control (a.k.a. Tag) Artifacts] </li>
6767
<li> [#wikichng | Wiki Pages] </li>
6868
<li> [#tktchng | Ticket Changes] </li>
6969
<li> [#attachment | Attachments] </li>
7070
<li> [#event | TechNotes] </li>
7171
<li> [#forum | Forum Posts] </li>
@@ -173,11 +173,14 @@
173173
same file as it existed in the parent check-in. If the name of the
174174
file is unchanged from its parent, then the 4th argument is omitted.
175175
176176
A manifest has zero or one <b>N</b> cards. The <b>N</b> card specifies the mimetype for the
177177
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.
179182
180183
A manifest has zero or one <b>P</b> cards. Most manifests have one <b>P</b> card.
181184
The <b>P</b> card has a varying number of arguments that
182185
define other manifests from which the current manifest
183186
is derived. Each argument is a lowercase
@@ -273,22 +276,26 @@
273276
prior cards in the cluster. The <b>Z</b> card is required.
274277
275278
An example cluster from Fossil can be seen
276279
[/artifact/d03dbdd73a2a8 | here].
277280
278
-<h3 id="ctrl">2.3 Control Artifacts</h3>
281
+<h3 id="ctrl">2.3 Control (a.k.a. Tag) Artifacts</h3>
279282
280283
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:
283286
284287
<div class="indent">
285288
<b>D</b> <i>time-and-date-stamp</i><br />
286289
<b>T</b> (<b>+</b>|<b>-</b>|<b>*</b>)<i>tag-name</i> <i>artifact-id</i> ?<i>value</i>?<br />
287290
<b>U</b> <i>user-name</i><br />
288291
<b>Z</b> <i>checksum</i><br />
289292
</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].
290297
291298
A control artifact must have one <b>D</b> card, one <b>U</b> card, one <b>Z</b> card and
292299
one or more <b>T</b> cards. No other cards or other text is
293300
allowed in a control artifact. Control artifacts might be PGP
294301
clearsigned.
295302
--- 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
--- www/serverext.wiki
+++ www/serverext.wiki
@@ -32,11 +32,11 @@
3232
If the Fossil server is itself run as
3333
[./server/any/cgi.md|CGI], then add a line to the
3434
[./cgi.wiki#extroot|CGI script file] that says:
3535
3636
<pre>
37
-extroot: <i>DIRECTORY</i>
37
+ extroot: <i>DIRECTORY</i>
3838
</pre>
3939
4040
Or, if the Fossil server is being run using the
4141
"[./server/any/none.md|fossil server]" or
4242
"[./server/any/none.md|fossil ui]" or
@@ -45,18 +45,21 @@
4545
4646
The <i>DIRECTORY</i> is the DOCUMENT_ROOT for the CGI.
4747
Files in the DOCUMENT_ROOT are accessed via URLs like this:
4848
4949
<pre>
50
-https://example-project.org/ext/<i>FILENAME</i>
50
+ https://example-project.org/ext/<i>FILENAME</i>
5151
</pre>
5252
5353
In other words, access files in DOCUMENT_ROOT by appending the filename
5454
relative to DOCUMENT_ROOT to the [/help?cmd=/ext|/ext]
5555
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.
5861
5962
<h3>2.1 Example #1</h3>
6063
6164
The source code repository for SQLite is a Fossil server that is run
6265
as CGI. The URL for the source code repository is [https://sqlite.org/src].
@@ -117,16 +120,41 @@
117120
of [https://www.tcl.tk|Tcl/Tk] and so he tends to gravitate toward Tcl-based
118121
technologies like Wapp.) The fileup1 script is a demo program that lets
119122
the user upload a file using a form, and then displays that file in the reply.
120123
There is a link on the page that causes the fileup1 script to return a copy
121124
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.
122150
123151
<h2 id="cgi-inputs">3.0 CGI Inputs</h2>
124152
125153
The /ext extension mechanism is an ordinary CGI interface. Parameters
126154
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:
128156
129157
* AUTH_TYPE
130158
* AUTH_CONTENT
131159
* CONTENT_LENGTH
132160
* CONTENT_TYPE
133161
--- 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
--- www/settings.wiki
+++ www/settings.wiki
@@ -1,18 +1,18 @@
11
<title>Fossil Settings</title>
22
3
-<h2>Using Fossil Settings</h2>
3
+<h1>Using Fossil Settings</h1>
44
55
Settings control the behaviour of fossil. They are set with the
66
<tt>fossil settings</tt> command, or through the web interface in
77
the Settings page in the Admin section.
88
99
For a list of all settings, view the Settings page, or type
1010
<tt>fossil help settings</tt> from the command line.
1111
1212
13
-<h3 id="repo">Repository settings</h3>
13
+<h2 id="repo">1.0 Repository settings</h2>
1414
1515
Settings are set on a per-repository basis. When you clone a repository,
1616
a subset of settings are copied to your local repository.
1717
1818
If you make a change to a setting on your local repository, it is not
@@ -24,11 +24,11 @@
2424
will be used for all repositories cloned to your machine, unless
2525
overridden explicitly in a particular repository. Global settings can be
2626
set by using the <tt>-global</tt> option on the <tt>fossil settings</tt>
2727
command.
2828
29
-<h3 id="versionable">"Versionable" settings</h3>
29
+<h2 id="versionable">2.0 "Versionable" settings</h2>
3030
3131
Most of the settings control the behaviour of fossil on your local
3232
machine, largely acting to reflect your preference on how you want to
3333
use Fossil, how you communicate with the server, or options for hosting
3434
a repository on the web.
3535
--- 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 @@
227227
* [unversioned list](#unversioned_list)
228228
* [utime](#utime)
229229
* [verifyCsrf](#verifyCsrf)
230230
* [verifyLogin](#verifyLogin)
231231
* [wiki](#wiki)
232
+ * [wiki_assoc](#wiki_assoc)
232233
233234
Each of the commands above is documented by a block comment above their
234235
implementation in the th\_main.c or th\_tcl.c source files.
235236
236237
All commands starting with "tcl", with the exception of "tclReady",
@@ -271,11 +272,11 @@
271272
<a id="bireqjs"></a>TH1 builtin_request_js Command
272273
--------------------------------------------------
273274
274275
* builtin_request_js NAME
275276
276
-NAME must be the name of one of the
277
+NAME must be the name of one of the
277278
[built-in javascript source files](/dir?ci=trunk&type=flat&name=src&re=js$).
278279
This command causes that javascript file to be appended to the delivered
279280
document.
280281
281282
@@ -284,11 +285,11 @@
284285
-----------------------------------------------------
285286
286287
* capexpr CAPABILITY-EXPR
287288
288289
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).
290291
The overall expression is true if any
291292
one term is true. A single term is true if all letters within that
292293
term are true. Or, if the term begins with "!", then the term is true
293294
if none of the terms are true. Or, if the term begins with "@" then
294295
the term is true if all of the capability letters in that term are
@@ -861,10 +862,19 @@
861862
-----------------------------------
862863
863864
* wiki STRING
864865
865866
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.
866876
867877
Tcl Integration Commands
868878
------------------------
869879
870880
When the Tcl integration subsystem is enabled, several commands are added
871881
872882
ADDED www/title-test.md
873883
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
--- 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)
--- 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]

Keyboard Shortcuts

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