Fossil SCM

Merge trunk

jan.nijtmans 2018-07-18 20:21 UTC multi-thread merge
Commit 02bc919bc0f63be563105359e7c0625c1d0007d1da2ba1fb357c14ecbc242477
163 files changed +3 +3 -1 +67 +1 -1 +23 -6 +15 +77 +4 +12 +53 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 +1 -1 +142 -71 +1 -1 +18 -6 +1 -1 +214 +44 -6 +5 -1 +3 -3 +34 -21 +3 -3 +182 -145 +16 -11 +1 +12 -5 +124 -46 +10 +97 -47 +1 -1 +91 -87 +81 -81 +186 -38 +186 -38 +41 +24 -10 +1 +4 -2 +18 -11 +2 +89 -46 +71 +1 -1 +107 -9 +88 -7 +355 +12 -8 +19 -13 +18 -7 +3 -3 +4 -4 +10 -3 +10 -10 +2 -2 +4 -4 +2 +1 -1 +13 -12 +8 -8 +125 -66 +272 -63 +272 -63 +108 -3 +1 +34 -10 +11 -4 +2 -2 +7 -4 +2 -2 +43 -10 +6 -5 +1 -1 +65 -69 +1 -1 +6 -2 +95 -5 +1 -1 +107 +171 -85 +8689 -798 +97 -85 +12 +15 -6 +15 -6 +15496 -5951 +950 -130 +71 +11 -3 +19 -4 +2 -2 +47 -5 +6 +7 +114 -43 +4 -4 +49 -40 +1 +6 -1 +54 -12 +1 -1 +14 +2 -2 +4 -4 +1 -1 +350 -112 +2 -1 +13 -2 +163 +51 +28 +53 +3 -3 +47 -11 +123 -4 +58 -3 +98 -5 +5 +12 -11 +1 -1 +57 -3 +1 -1 +232 -3 +21 -20 +5 -6 -125 +8 +1 -1 +46 -46 +29 -6 +1
~ Makefile.classic ~ Makefile.in ~ Makefile.osx-jaguar ~ VERSION ~ auto.def ~ skins/ardoise/README.md ~ skins/ardoise/css.txt ~ skins/ardoise/details.txt ~ skins/ardoise/footer.txt ~ skins/ardoise/header.txt ~ skins/ardoise/images/active.svg ~ skins/ardoise/images/addindent.svg ~ skins/ardoise/images/addindent_h.svg ~ skins/ardoise/images/blist.svg ~ skins/ardoise/images/blist_h.svg ~ skins/ardoise/images/bold.svg ~ skins/ardoise/images/bold_h.svg ~ skins/ardoise/images/calign.svg ~ skins/ardoise/images/calign_h.svg ~ skins/ardoise/images/clrfmt.svg ~ skins/ardoise/images/clrfmt_h.svg ~ skins/ardoise/images/delindent.svg ~ skins/ardoise/images/delindent_h.svg ~ skins/ardoise/images/dir.svg ~ skins/ardoise/images/file.svg ~ skins/ardoise/images/italic.svg ~ skins/ardoise/images/italic_h.svg ~ skins/ardoise/images/lalign.svg ~ skins/ardoise/images/lalign_h.svg ~ skins/ardoise/images/link.svg ~ skins/ardoise/images/link_h.svg ~ skins/ardoise/images/nlist.svg ~ skins/ardoise/images/nlist_h.svg ~ skins/ardoise/images/quote.svg ~ skins/ardoise/images/quote_h.svg ~ skins/ardoise/images/ralign.svg ~ skins/ardoise/images/ralign_h.svg ~ skins/ardoise/images/redo.svg ~ skins/ardoise/images/redo_h.svg ~ skins/ardoise/images/underline.svg ~ skins/ardoise/images/underline_h.svg ~ skins/ardoise/images/undo.svg ~ skins/ardoise/images/undo_h.svg ~ skins/default/css.txt ~ skins/xekri/css.txt ~ skins/xekri/details.txt ~ src/add.c ~ src/attach.c ~ src/backoffice.c ~ src/blob.c ~ src/browse.c ~ src/bundle.c ~ src/cache.c ~ src/captcha.c ~ src/cgi.c ~ src/checkin.c ~ src/checkout.c ~ src/clone.c ~ src/codecheck1.c ~ src/config.h ~ src/configure.c ~ src/cookies.c ~ src/cson_amalgamation.c ~ src/cson_amalgamation.h ~ src/db.c ~ src/db.c ~ src/default_css.txt ~ src/diff.c ~ src/diffcmd.c ~ src/dispatch.c ~ src/doc.c ~ src/email.c ~ src/encode.c ~ src/etag.c ~ src/event.c ~ src/export.c ~ src/file.c ~ src/forum.c ~ src/graph.c ~ src/graph.js ~ src/http_socket.c ~ src/http_ssl.c ~ src/http_transport.c ~ src/import.c ~ src/info.c ~ src/json.c ~ src/json_branch.c ~ src/json_dir.c ~ src/json_status.c ~ src/json_timeline.c ~ src/linenoise.c ~ src/login.c ~ src/main.c ~ src/main.c ~ src/main.mk ~ src/makeheaders.c ~ src/makemake.tcl ~ src/manifest.c ~ src/markdown.c ~ src/merge.c ~ src/moderate.c ~ src/path.c ~ src/pivot.c ~ src/popen.c ~ src/printf.c ~ src/purge.c ~ src/rebuild.c ~ src/regexp.c ~ src/search.c ~ src/security_audit.c ~ src/setup.c ~ src/shell.c ~ src/skins.c ~ src/smtp.c ~ src/sqlcmd.c ~ src/sqlcmd.c ~ src/sqlite3.c ~ src/sqlite3.h ~ src/stat.c ~ src/statrep.c ~ src/style.c ~ src/tag.c ~ src/tar.c ~ src/th_main.c ~ src/th_tcl.c ~ src/timeline.c ~ src/tkt.c ~ src/unversioned.c ~ src/url.c ~ src/user.c ~ src/util.c ~ src/verify.c ~ src/webmail.c ~ src/wiki.c ~ src/wikiformat.c ~ src/winfile.c ~ src/winhttp.c ~ src/xfer.c ~ src/zip.c ~ tools/decode-email.c ~ tools/email-monitor.tcl ~ tools/email-sender.tcl ~ tools/fossil-stress.tcl ~ win/Makefile.PellesCGMake ~ win/Makefile.dmc ~ win/Makefile.mingw ~ win/Makefile.mingw.mistachkin ~ win/Makefile.msc ~ win/fossil.rc ~ win/include/dirent.h ~ www/build.wiki ~ www/changes.wiki ~ www/customskin.md ~ www/emaildesign.md ~ www/env-opts.md ~ www/fossil-v-git.wiki ~ www/index.wiki - www/mkdownload.tcl ~ www/quotes.wiki ~ www/server.wiki ~ www/stats.wiki ~ www/sync.wiki ~ www/th1.md
--- Makefile.classic
+++ Makefile.classic
@@ -42,10 +42,13 @@
4242
# TCC += -DFOSSIL_ENABLE_MINIZ
4343
4444
# To add support for HTTPS
4545
TCC += -DFOSSIL_ENABLE_SSL
4646
47
+# To enable legacy mv/rm support
48
+TCC += -DFOSSIL_ENABLE_LEGACY_MV_RM=1
49
+
4750
#### We sometimes add the -static option here so that we can build a
4851
# static executable that will run in a chroot jail.
4952
#LIB = -static
5053
TCC += -DFOSSIL_DYNAMIC_BUILD=1
5154
5255
--- Makefile.classic
+++ Makefile.classic
@@ -42,10 +42,13 @@
42 # TCC += -DFOSSIL_ENABLE_MINIZ
43
44 # To add support for HTTPS
45 TCC += -DFOSSIL_ENABLE_SSL
46
 
 
 
47 #### We sometimes add the -static option here so that we can build a
48 # static executable that will run in a chroot jail.
49 #LIB = -static
50 TCC += -DFOSSIL_DYNAMIC_BUILD=1
51
52
--- Makefile.classic
+++ Makefile.classic
@@ -42,10 +42,13 @@
42 # TCC += -DFOSSIL_ENABLE_MINIZ
43
44 # To add support for HTTPS
45 TCC += -DFOSSIL_ENABLE_SSL
46
47 # To enable legacy mv/rm support
48 TCC += -DFOSSIL_ENABLE_LEGACY_MV_RM=1
49
50 #### We sometimes add the -static option here so that we can build a
51 # static executable that will run in a chroot jail.
52 #LIB = -static
53 TCC += -DFOSSIL_DYNAMIC_BUILD=1
54
55
+3 -1
--- Makefile.in
+++ Makefile.in
@@ -36,16 +36,18 @@
3636
#### Tcl shell for use in running the fossil testsuite. If you do not
3737
# care about testing the end result, this can be blank.
3838
#
3939
TCLSH = tclsh
4040
41
+CFLAGS = @CFLAGS@
4142
LIB = @LDFLAGS@ @EXTRA_LDFLAGS@ @LIBS@
4243
BCCFLAGS = @CPPFLAGS@ @CFLAGS@
43
-TCCFLAGS = @EXTRA_CFLAGS@ @CPPFLAGS@ @CFLAGS@ -DHAVE_AUTOCONFIG_H -D_HAVE_SQLITE_CONFIG_H
44
+TCCFLAGS = @EXTRA_CFLAGS@ @CPPFLAGS@ @CFLAGS@ -DHAVE_AUTOCONFIG_H -D_HAVE_SQLITE_CONFIG_H -DFOSSIL_ENABLE_LEGACY_MV_RM=1
4445
INSTALLDIR = $(DESTDIR)@prefix@/bin
4546
USE_SYSTEM_SQLITE = @USE_SYSTEM_SQLITE@
4647
USE_LINENOISE = @USE_LINENOISE@
48
+USE_MMAN_H = @USE_MMAN_H@
4749
USE_SEE = @USE_SEE@
4850
FOSSIL_ENABLE_MINIZ = @FOSSIL_ENABLE_MINIZ@
4951
5052
include $(SRCDIR)/main.mk
5153
5254
5355
ADDED Makefile.osx-jaguar
--- Makefile.in
+++ Makefile.in
@@ -36,16 +36,18 @@
36 #### Tcl shell for use in running the fossil testsuite. If you do not
37 # care about testing the end result, this can be blank.
38 #
39 TCLSH = tclsh
40
 
41 LIB = @LDFLAGS@ @EXTRA_LDFLAGS@ @LIBS@
42 BCCFLAGS = @CPPFLAGS@ @CFLAGS@
43 TCCFLAGS = @EXTRA_CFLAGS@ @CPPFLAGS@ @CFLAGS@ -DHAVE_AUTOCONFIG_H -D_HAVE_SQLITE_CONFIG_H
44 INSTALLDIR = $(DESTDIR)@prefix@/bin
45 USE_SYSTEM_SQLITE = @USE_SYSTEM_SQLITE@
46 USE_LINENOISE = @USE_LINENOISE@
 
47 USE_SEE = @USE_SEE@
48 FOSSIL_ENABLE_MINIZ = @FOSSIL_ENABLE_MINIZ@
49
50 include $(SRCDIR)/main.mk
51
52
53 DDED Makefile.osx-jaguar
--- Makefile.in
+++ Makefile.in
@@ -36,16 +36,18 @@
36 #### Tcl shell for use in running the fossil testsuite. If you do not
37 # care about testing the end result, this can be blank.
38 #
39 TCLSH = tclsh
40
41 CFLAGS = @CFLAGS@
42 LIB = @LDFLAGS@ @EXTRA_LDFLAGS@ @LIBS@
43 BCCFLAGS = @CPPFLAGS@ @CFLAGS@
44 TCCFLAGS = @EXTRA_CFLAGS@ @CPPFLAGS@ @CFLAGS@ -DHAVE_AUTOCONFIG_H -D_HAVE_SQLITE_CONFIG_H -DFOSSIL_ENABLE_LEGACY_MV_RM=1
45 INSTALLDIR = $(DESTDIR)@prefix@/bin
46 USE_SYSTEM_SQLITE = @USE_SYSTEM_SQLITE@
47 USE_LINENOISE = @USE_LINENOISE@
48 USE_MMAN_H = @USE_MMAN_H@
49 USE_SEE = @USE_SEE@
50 FOSSIL_ENABLE_MINIZ = @FOSSIL_ENABLE_MINIZ@
51
52 include $(SRCDIR)/main.mk
53
54
55 DDED Makefile.osx-jaguar
--- a/Makefile.osx-jaguar
+++ b/Makefile.osx-jaguar
@@ -0,0 +1,67 @@
1
+#!/usr/bin/make
2
+#
3
+# This is a specially modified version of the Makefile that will build
4
+# Fossil on Mac OSX Jaguar (10.2) circa 2002. This Makefile is used for
5
+# testing on an old PPC iBook. The use of this old platform helps to verify
6
+# Fossil and SQLite running on big-endian hardware.
7
+#
8
+# To build with this Makefile, run:
9
+#
10
+# make -f Makefile.osx-jaguar
11
+#
12
+#
13
+# This is the top-level makefile for Fossil when the build is occurring
14
+# on a unix platform. This works out-of-the-box on most unix platforms.
15
+# But you are free to vary some of the definitions if desired.
16
+#
17
+#### The toplevel directory of the source tree. Fossil can be built
18
+# in a directory that is separate from the source tree. Just change
19
+# the following to point from the build directory to the src/ folder.
20
+#
21
+SRCDIR = ./src
22
+
23
+#### The directory into which object code files should be written.
24
+# Having a "./" prefix in the value of this variable breaks our use of the
25
+# "makeheaders" tool when running make on the MinGW platform, apparently
26
+# due to some command line argument manipulation performed automatically
27
+# by the shell.
28
+#
29
+#
30
+OBJDIR = bld
31
+
32
+#### C Compiler and options for use in building executables that
33
+# will run on the platform that is doing the build. This is used
34
+# to compile code-generator programs as part of the build process.
35
+# See TCC below for the C compiler for building the finished binary.
36
+#
37
+BCC = cc
38
+ = cc
39
+BCCFLAGS = $(CFLAGS)
40
+
41
+#### The suffix to add to final executable file. When cross-compiling
42
+# to windows, make this ".exe". Othe rwise leave
43
+#### Tcl shell for use in running the fossil testsuite. If you do not
44
+# care about testing the end result, this can be blank.
45
+#
46
+TCLSH = tclsh
47
+
48
+# LIB = -lz
49
+LIB = compat/zlib/libz.a
50
+TCC += -g -O0 -DHAVE_AUTOCONFIG_H
51
+TCC += -Icompat/zlib
52
+TCC += -DSQLITE_WITHOUT_ZONEMALLOC
53
+TCC += -D_BSD_SOURCE=1
54
+TCC += -DWITHOUT_ICONV
55
+TCC += -Dsocklen_t=int
56
+TCC += -DSQLITE_MAX_MMAP_SIZE=0
57
+TCC += -DF
58
+INSTALLDIR = $(DESTDIR)/usr/lo
59
+USE_LINENOISE = 1
60
+# FOSSIL_ENABLE_TCL = @FOSSIL_ENABLE_TCL@
61
+FOSSIL_ENABLE_TCL = 0
62
+FOSSIL_ENABLE_MINIZ = 0
63
+
64
+include $(SRCDIR)/main.mk
65
+
66
+distclean: clean
67
+ rm -f autoconfig.h config.log Makefile
--- a/Makefile.osx-jaguar
+++ b/Makefile.osx-jaguar
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/Makefile.osx-jaguar
+++ b/Makefile.osx-jaguar
@@ -0,0 +1,67 @@
1 #!/usr/bin/make
2 #
3 # This is a specially modified version of the Makefile that will build
4 # Fossil on Mac OSX Jaguar (10.2) circa 2002. This Makefile is used for
5 # testing on an old PPC iBook. The use of this old platform helps to verify
6 # Fossil and SQLite running on big-endian hardware.
7 #
8 # To build with this Makefile, run:
9 #
10 # make -f Makefile.osx-jaguar
11 #
12 #
13 # This is the top-level makefile for Fossil when the build is occurring
14 # on a unix platform. This works out-of-the-box on most unix platforms.
15 # But you are free to vary some of the definitions if desired.
16 #
17 #### The toplevel directory of the source tree. Fossil can be built
18 # in a directory that is separate from the source tree. Just change
19 # the following to point from the build directory to the src/ folder.
20 #
21 SRCDIR = ./src
22
23 #### The directory into which object code files should be written.
24 # Having a "./" prefix in the value of this variable breaks our use of the
25 # "makeheaders" tool when running make on the MinGW platform, apparently
26 # due to some command line argument manipulation performed automatically
27 # by the shell.
28 #
29 #
30 OBJDIR = bld
31
32 #### C Compiler and options for use in building executables that
33 # will run on the platform that is doing the build. This is used
34 # to compile code-generator programs as part of the build process.
35 # See TCC below for the C compiler for building the finished binary.
36 #
37 BCC = cc
38 = cc
39 BCCFLAGS = $(CFLAGS)
40
41 #### The suffix to add to final executable file. When cross-compiling
42 # to windows, make this ".exe". Othe rwise leave
43 #### Tcl shell for use in running the fossil testsuite. If you do not
44 # care about testing the end result, this can be blank.
45 #
46 TCLSH = tclsh
47
48 # LIB = -lz
49 LIB = compat/zlib/libz.a
50 TCC += -g -O0 -DHAVE_AUTOCONFIG_H
51 TCC += -Icompat/zlib
52 TCC += -DSQLITE_WITHOUT_ZONEMALLOC
53 TCC += -D_BSD_SOURCE=1
54 TCC += -DWITHOUT_ICONV
55 TCC += -Dsocklen_t=int
56 TCC += -DSQLITE_MAX_MMAP_SIZE=0
57 TCC += -DF
58 INSTALLDIR = $(DESTDIR)/usr/lo
59 USE_LINENOISE = 1
60 # FOSSIL_ENABLE_TCL = @FOSSIL_ENABLE_TCL@
61 FOSSIL_ENABLE_TCL = 0
62 FOSSIL_ENABLE_MINIZ = 0
63
64 include $(SRCDIR)/main.mk
65
66 distclean: clean
67 rm -f autoconfig.h config.log Makefile
+1 -1
--- VERSION
+++ VERSION
@@ -1,1 +1,1 @@
1
-2.5
1
+2.6
22
--- VERSION
+++ VERSION
@@ -1,1 +1,1 @@
1 2.5
2
--- VERSION
+++ VERSION
@@ -1,1 +1,1 @@
1 2.6
2
+23 -6
--- auto.def
+++ auto.def
@@ -8,17 +8,18 @@
88
with-miniz=0 => {Use miniz from the source tree}
99
with-zlib:path|auto|tree
1010
=> {Look for zlib in the given path, automatically, or in the source tree}
1111
with-exec-rel-paths=0
1212
=> {Enable relative paths for external diff/gdiff}
13
- with-legacy-mv-rm=0 => {Enable legacy behavior for mv/rm (skip checkout files)}
13
+ with-legacy-mv-rm=1 => {Enable legacy behavior for mv/rm (skip checkout files)}
1414
with-th1-docs=0 => {Enable TH1 for embedded documentation pages}
1515
with-th1-hooks=0 => {Enable TH1 hooks for commands and web pages}
1616
with-tcl:path => {Enable Tcl integration, with Tcl in the specified path}
1717
with-tcl-stubs=0 => {Enable Tcl integration via stubs library mechanism}
1818
with-tcl-private-stubs=0
1919
=> {Enable Tcl integration via private stubs mechanism}
20
+ with-mman=0 => {Enable use of POSIX memory APIs from "sys/mman.h"}
2021
with-see=0 => {Enable the SQLite Encryption Extension (SEE)}
2122
internal-sqlite=1 => {Don't use the internal SQLite, use the system one}
2223
static=0 => {Link a static executable}
2324
fusefs=1 => {Disable the Fuse Filesystem}
2425
fossil-debug=0 => {Build with fossil debugging enabled}
@@ -40,10 +41,11 @@
4041
define EXTRA_CFLAGS "-Wall"
4142
define EXTRA_LDFLAGS ""
4243
define USE_SYSTEM_SQLITE 0
4344
define USE_LINENOISE 0
4445
define FOSSIL_ENABLE_MINIZ 0
46
+define USE_MMAN_H 0
4547
define USE_SEE 0
4648
4749
# This procedure is a customized version of "cc-check-function-in-lib",
4850
# that does not modify the LIBS variable. Its use prevents prematurely
4951
# pulling in libraries that will be added later anyhow (e.g. "-ldl").
@@ -89,17 +91,17 @@
8991
# the code below will append -ldl to LIBS.
9092
#
9193
foreach extralibs {{} {-ldl}} {
9294
9395
# Locate the system SQLite by searching for sqlite3_open(). Then check
94
- # if sqlite3_prepare_v3() can be found as well. If we can find open() but
95
- # not prepare_v3(), then the system SQLite is too old to link against
96
+ # if sqlite3_keyword_check() can be found as well. If we can find open() but
97
+ # not keyword_check(), then the system SQLite is too old to link against
9698
# fossil.
9799
#
98100
if {[check-function-in-lib sqlite3_open sqlite3 $extralibs]} {
99
- if {![check-function-in-lib sqlite3_prepare_v3 sqlite3 $extralibs]} {
100
- user-error "system sqlite3 too old (require >= 3.20.0)"
101
+ if {![check-function-in-lib sqlite3_keyword_check sqlite3 $extralibs]} {
102
+ user-error "system sqlite3 too old (require >= 3.24.0)"
101103
}
102104
103105
# Success. Update symbols and return.
104106
#
105107
define USE_SYSTEM_SQLITE 1
@@ -141,10 +143,16 @@
141143
142144
if {[opt-bool no-opt]} {
143145
define CFLAGS {-g -O0 -Wall}
144146
msg-result "Builting without compiler optimization"
145147
}
148
+
149
+if {[opt-bool with-mman]} {
150
+ define-append EXTRA_CFLAGS -DUSE_MMAN_H
151
+ define USE_MMAN_H 1
152
+ msg-result "Enabling \"sys/mman.h\" support"
153
+}
146154
147155
if {[opt-bool with-see]} {
148156
define-append EXTRA_CFLAGS -DUSE_SEE
149157
define USE_SEE 1
150158
msg-result "Enabling encryption support"
@@ -470,13 +478,15 @@
470478
# Last resort, may be Windows
471479
if {[is_mingw]} {
472480
define-append LIBS -lwsock32
473481
}
474482
}
483
+cc-check-function-in-lib ns_name_uncompress resolv
475484
cc-check-functions utime
476485
cc-check-functions usleep
477486
cc-check-functions strchrnul
487
+cc-check-functions pledge
478488
479489
# Check for getloadavg(), and if it doesn't exist, define FOSSIL_OMIT_LOAD_AVERAGE
480490
if {![cc-check-functions getloadavg]} {
481491
define FOSSIL_OMIT_LOAD_AVERAGE 1
482492
msg-result "Load average support unavailable"
@@ -485,11 +495,10 @@
485495
# Check for getpassphrase() for Solaris 10 where getpass() truncates to 10 chars
486496
if {![cc-check-functions getpassphrase]} {
487497
# Haiku needs this
488498
cc-check-function-in-lib getpass bsd
489499
}
490
-cc-check-function-in-lib dlopen dl
491500
cc-check-function-in-lib sin m
492501
493502
# Check for the FuseFS library
494503
if {[opt-bool fusefs]} {
495504
if {[cc-check-function-in-lib fuse_mount fuse]} {
@@ -497,8 +506,16 @@
497506
define FOSSIL_HAVE_FUSEFS 1
498507
define-append LIBS -lfuse
499508
msg-result "FuseFS support enabled"
500509
}
501510
}
511
+
512
+# Finally, append -ldl to make sure it's the last in the list.
513
+# The library order matters in case of static linking.
514
+if {[check-function-in-lib dlopen dl]} {
515
+ # Some platforms (*BSD) have the dl functions already in libc and no libdl.
516
+ # In such case we can link directly without -ldl.
517
+ define-append LIBS [get-define lib_dlopen]
518
+}
502519
503520
make-template Makefile.in
504521
make-config-header autoconfig.h -auto {USE_* FOSSIL_*}
505522
506523
ADDED skins/ardoise/README.md
507524
ADDED skins/ardoise/css.txt
508525
ADDED skins/ardoise/details.txt
509526
ADDED skins/ardoise/footer.txt
510527
ADDED skins/ardoise/header.txt
511528
ADDED skins/ardoise/images/active.svg
512529
ADDED skins/ardoise/images/addindent.svg
513530
ADDED skins/ardoise/images/addindent_h.svg
514531
ADDED skins/ardoise/images/blist.svg
515532
ADDED skins/ardoise/images/blist_h.svg
516533
ADDED skins/ardoise/images/bold.svg
517534
ADDED skins/ardoise/images/bold_h.svg
518535
ADDED skins/ardoise/images/calign.svg
519536
ADDED skins/ardoise/images/calign_h.svg
520537
ADDED skins/ardoise/images/clrfmt.svg
521538
ADDED skins/ardoise/images/clrfmt_h.svg
522539
ADDED skins/ardoise/images/delindent.svg
523540
ADDED skins/ardoise/images/delindent_h.svg
524541
ADDED skins/ardoise/images/dir.svg
525542
ADDED skins/ardoise/images/file.svg
526543
ADDED skins/ardoise/images/italic.svg
527544
ADDED skins/ardoise/images/italic_h.svg
528545
ADDED skins/ardoise/images/lalign.svg
529546
ADDED skins/ardoise/images/lalign_h.svg
530547
ADDED skins/ardoise/images/link.svg
531548
ADDED skins/ardoise/images/link_h.svg
532549
ADDED skins/ardoise/images/nlist.svg
533550
ADDED skins/ardoise/images/nlist_h.svg
534551
ADDED skins/ardoise/images/quote.svg
535552
ADDED skins/ardoise/images/quote_h.svg
536553
ADDED skins/ardoise/images/ralign.svg
537554
ADDED skins/ardoise/images/ralign_h.svg
538555
ADDED skins/ardoise/images/redo.svg
539556
ADDED skins/ardoise/images/redo_h.svg
540557
ADDED skins/ardoise/images/underline.svg
541558
ADDED skins/ardoise/images/underline_h.svg
542559
ADDED skins/ardoise/images/undo.svg
543560
ADDED skins/ardoise/images/undo_h.svg
--- auto.def
+++ auto.def
@@ -8,17 +8,18 @@
8 with-miniz=0 => {Use miniz from the source tree}
9 with-zlib:path|auto|tree
10 => {Look for zlib in the given path, automatically, or in the source tree}
11 with-exec-rel-paths=0
12 => {Enable relative paths for external diff/gdiff}
13 with-legacy-mv-rm=0 => {Enable legacy behavior for mv/rm (skip checkout files)}
14 with-th1-docs=0 => {Enable TH1 for embedded documentation pages}
15 with-th1-hooks=0 => {Enable TH1 hooks for commands and web pages}
16 with-tcl:path => {Enable Tcl integration, with Tcl in the specified path}
17 with-tcl-stubs=0 => {Enable Tcl integration via stubs library mechanism}
18 with-tcl-private-stubs=0
19 => {Enable Tcl integration via private stubs mechanism}
 
20 with-see=0 => {Enable the SQLite Encryption Extension (SEE)}
21 internal-sqlite=1 => {Don't use the internal SQLite, use the system one}
22 static=0 => {Link a static executable}
23 fusefs=1 => {Disable the Fuse Filesystem}
24 fossil-debug=0 => {Build with fossil debugging enabled}
@@ -40,10 +41,11 @@
40 define EXTRA_CFLAGS "-Wall"
41 define EXTRA_LDFLAGS ""
42 define USE_SYSTEM_SQLITE 0
43 define USE_LINENOISE 0
44 define FOSSIL_ENABLE_MINIZ 0
 
45 define USE_SEE 0
46
47 # This procedure is a customized version of "cc-check-function-in-lib",
48 # that does not modify the LIBS variable. Its use prevents prematurely
49 # pulling in libraries that will be added later anyhow (e.g. "-ldl").
@@ -89,17 +91,17 @@
89 # the code below will append -ldl to LIBS.
90 #
91 foreach extralibs {{} {-ldl}} {
92
93 # Locate the system SQLite by searching for sqlite3_open(). Then check
94 # if sqlite3_prepare_v3() can be found as well. If we can find open() but
95 # not prepare_v3(), then the system SQLite is too old to link against
96 # fossil.
97 #
98 if {[check-function-in-lib sqlite3_open sqlite3 $extralibs]} {
99 if {![check-function-in-lib sqlite3_prepare_v3 sqlite3 $extralibs]} {
100 user-error "system sqlite3 too old (require >= 3.20.0)"
101 }
102
103 # Success. Update symbols and return.
104 #
105 define USE_SYSTEM_SQLITE 1
@@ -141,10 +143,16 @@
141
142 if {[opt-bool no-opt]} {
143 define CFLAGS {-g -O0 -Wall}
144 msg-result "Builting without compiler optimization"
145 }
 
 
 
 
 
 
146
147 if {[opt-bool with-see]} {
148 define-append EXTRA_CFLAGS -DUSE_SEE
149 define USE_SEE 1
150 msg-result "Enabling encryption support"
@@ -470,13 +478,15 @@
470 # Last resort, may be Windows
471 if {[is_mingw]} {
472 define-append LIBS -lwsock32
473 }
474 }
 
475 cc-check-functions utime
476 cc-check-functions usleep
477 cc-check-functions strchrnul
 
478
479 # Check for getloadavg(), and if it doesn't exist, define FOSSIL_OMIT_LOAD_AVERAGE
480 if {![cc-check-functions getloadavg]} {
481 define FOSSIL_OMIT_LOAD_AVERAGE 1
482 msg-result "Load average support unavailable"
@@ -485,11 +495,10 @@
485 # Check for getpassphrase() for Solaris 10 where getpass() truncates to 10 chars
486 if {![cc-check-functions getpassphrase]} {
487 # Haiku needs this
488 cc-check-function-in-lib getpass bsd
489 }
490 cc-check-function-in-lib dlopen dl
491 cc-check-function-in-lib sin m
492
493 # Check for the FuseFS library
494 if {[opt-bool fusefs]} {
495 if {[cc-check-function-in-lib fuse_mount fuse]} {
@@ -497,8 +506,16 @@
497 define FOSSIL_HAVE_FUSEFS 1
498 define-append LIBS -lfuse
499 msg-result "FuseFS support enabled"
500 }
501 }
 
 
 
 
 
 
 
 
502
503 make-template Makefile.in
504 make-config-header autoconfig.h -auto {USE_* FOSSIL_*}
505
506 DDED skins/ardoise/README.md
507 DDED skins/ardoise/css.txt
508 DDED skins/ardoise/details.txt
509 DDED skins/ardoise/footer.txt
510 DDED skins/ardoise/header.txt
511 DDED skins/ardoise/images/active.svg
512 DDED skins/ardoise/images/addindent.svg
513 DDED skins/ardoise/images/addindent_h.svg
514 DDED skins/ardoise/images/blist.svg
515 DDED skins/ardoise/images/blist_h.svg
516 DDED skins/ardoise/images/bold.svg
517 DDED skins/ardoise/images/bold_h.svg
518 DDED skins/ardoise/images/calign.svg
519 DDED skins/ardoise/images/calign_h.svg
520 DDED skins/ardoise/images/clrfmt.svg
521 DDED skins/ardoise/images/clrfmt_h.svg
522 DDED skins/ardoise/images/delindent.svg
523 DDED skins/ardoise/images/delindent_h.svg
524 DDED skins/ardoise/images/dir.svg
525 DDED skins/ardoise/images/file.svg
526 DDED skins/ardoise/images/italic.svg
527 DDED skins/ardoise/images/italic_h.svg
528 DDED skins/ardoise/images/lalign.svg
529 DDED skins/ardoise/images/lalign_h.svg
530 DDED skins/ardoise/images/link.svg
531 DDED skins/ardoise/images/link_h.svg
532 DDED skins/ardoise/images/nlist.svg
533 DDED skins/ardoise/images/nlist_h.svg
534 DDED skins/ardoise/images/quote.svg
535 DDED skins/ardoise/images/quote_h.svg
536 DDED skins/ardoise/images/ralign.svg
537 DDED skins/ardoise/images/ralign_h.svg
538 DDED skins/ardoise/images/redo.svg
539 DDED skins/ardoise/images/redo_h.svg
540 DDED skins/ardoise/images/underline.svg
541 DDED skins/ardoise/images/underline_h.svg
542 DDED skins/ardoise/images/undo.svg
543 DDED skins/ardoise/images/undo_h.svg
--- auto.def
+++ auto.def
@@ -8,17 +8,18 @@
8 with-miniz=0 => {Use miniz from the source tree}
9 with-zlib:path|auto|tree
10 => {Look for zlib in the given path, automatically, or in the source tree}
11 with-exec-rel-paths=0
12 => {Enable relative paths for external diff/gdiff}
13 with-legacy-mv-rm=1 => {Enable legacy behavior for mv/rm (skip checkout files)}
14 with-th1-docs=0 => {Enable TH1 for embedded documentation pages}
15 with-th1-hooks=0 => {Enable TH1 hooks for commands and web pages}
16 with-tcl:path => {Enable Tcl integration, with Tcl in the specified path}
17 with-tcl-stubs=0 => {Enable Tcl integration via stubs library mechanism}
18 with-tcl-private-stubs=0
19 => {Enable Tcl integration via private stubs mechanism}
20 with-mman=0 => {Enable use of POSIX memory APIs from "sys/mman.h"}
21 with-see=0 => {Enable the SQLite Encryption Extension (SEE)}
22 internal-sqlite=1 => {Don't use the internal SQLite, use the system one}
23 static=0 => {Link a static executable}
24 fusefs=1 => {Disable the Fuse Filesystem}
25 fossil-debug=0 => {Build with fossil debugging enabled}
@@ -40,10 +41,11 @@
41 define EXTRA_CFLAGS "-Wall"
42 define EXTRA_LDFLAGS ""
43 define USE_SYSTEM_SQLITE 0
44 define USE_LINENOISE 0
45 define FOSSIL_ENABLE_MINIZ 0
46 define USE_MMAN_H 0
47 define USE_SEE 0
48
49 # This procedure is a customized version of "cc-check-function-in-lib",
50 # that does not modify the LIBS variable. Its use prevents prematurely
51 # pulling in libraries that will be added later anyhow (e.g. "-ldl").
@@ -89,17 +91,17 @@
91 # the code below will append -ldl to LIBS.
92 #
93 foreach extralibs {{} {-ldl}} {
94
95 # Locate the system SQLite by searching for sqlite3_open(). Then check
96 # if sqlite3_keyword_check() can be found as well. If we can find open() but
97 # not keyword_check(), then the system SQLite is too old to link against
98 # fossil.
99 #
100 if {[check-function-in-lib sqlite3_open sqlite3 $extralibs]} {
101 if {![check-function-in-lib sqlite3_keyword_check sqlite3 $extralibs]} {
102 user-error "system sqlite3 too old (require >= 3.24.0)"
103 }
104
105 # Success. Update symbols and return.
106 #
107 define USE_SYSTEM_SQLITE 1
@@ -141,10 +143,16 @@
143
144 if {[opt-bool no-opt]} {
145 define CFLAGS {-g -O0 -Wall}
146 msg-result "Builting without compiler optimization"
147 }
148
149 if {[opt-bool with-mman]} {
150 define-append EXTRA_CFLAGS -DUSE_MMAN_H
151 define USE_MMAN_H 1
152 msg-result "Enabling \"sys/mman.h\" support"
153 }
154
155 if {[opt-bool with-see]} {
156 define-append EXTRA_CFLAGS -DUSE_SEE
157 define USE_SEE 1
158 msg-result "Enabling encryption support"
@@ -470,13 +478,15 @@
478 # Last resort, may be Windows
479 if {[is_mingw]} {
480 define-append LIBS -lwsock32
481 }
482 }
483 cc-check-function-in-lib ns_name_uncompress resolv
484 cc-check-functions utime
485 cc-check-functions usleep
486 cc-check-functions strchrnul
487 cc-check-functions pledge
488
489 # Check for getloadavg(), and if it doesn't exist, define FOSSIL_OMIT_LOAD_AVERAGE
490 if {![cc-check-functions getloadavg]} {
491 define FOSSIL_OMIT_LOAD_AVERAGE 1
492 msg-result "Load average support unavailable"
@@ -485,11 +495,10 @@
495 # Check for getpassphrase() for Solaris 10 where getpass() truncates to 10 chars
496 if {![cc-check-functions getpassphrase]} {
497 # Haiku needs this
498 cc-check-function-in-lib getpass bsd
499 }
 
500 cc-check-function-in-lib sin m
501
502 # Check for the FuseFS library
503 if {[opt-bool fusefs]} {
504 if {[cc-check-function-in-lib fuse_mount fuse]} {
@@ -497,8 +506,16 @@
506 define FOSSIL_HAVE_FUSEFS 1
507 define-append LIBS -lfuse
508 msg-result "FuseFS support enabled"
509 }
510 }
511
512 # Finally, append -ldl to make sure it's the last in the list.
513 # The library order matters in case of static linking.
514 if {[check-function-in-lib dlopen dl]} {
515 # Some platforms (*BSD) have the dl functions already in libc and no libdl.
516 # In such case we can link directly without -ldl.
517 define-append LIBS [get-define lib_dlopen]
518 }
519
520 make-template Makefile.in
521 make-config-header autoconfig.h -auto {USE_* FOSSIL_*}
522
523 DDED skins/ardoise/README.md
524 DDED skins/ardoise/css.txt
525 DDED skins/ardoise/details.txt
526 DDED skins/ardoise/footer.txt
527 DDED skins/ardoise/header.txt
528 DDED skins/ardoise/images/active.svg
529 DDED skins/ardoise/images/addindent.svg
530 DDED skins/ardoise/images/addindent_h.svg
531 DDED skins/ardoise/images/blist.svg
532 DDED skins/ardoise/images/blist_h.svg
533 DDED skins/ardoise/images/bold.svg
534 DDED skins/ardoise/images/bold_h.svg
535 DDED skins/ardoise/images/calign.svg
536 DDED skins/ardoise/images/calign_h.svg
537 DDED skins/ardoise/images/clrfmt.svg
538 DDED skins/ardoise/images/clrfmt_h.svg
539 DDED skins/ardoise/images/delindent.svg
540 DDED skins/ardoise/images/delindent_h.svg
541 DDED skins/ardoise/images/dir.svg
542 DDED skins/ardoise/images/file.svg
543 DDED skins/ardoise/images/italic.svg
544 DDED skins/ardoise/images/italic_h.svg
545 DDED skins/ardoise/images/lalign.svg
546 DDED skins/ardoise/images/lalign_h.svg
547 DDED skins/ardoise/images/link.svg
548 DDED skins/ardoise/images/link_h.svg
549 DDED skins/ardoise/images/nlist.svg
550 DDED skins/ardoise/images/nlist_h.svg
551 DDED skins/ardoise/images/quote.svg
552 DDED skins/ardoise/images/quote_h.svg
553 DDED skins/ardoise/images/ralign.svg
554 DDED skins/ardoise/images/ralign_h.svg
555 DDED skins/ardoise/images/redo.svg
556 DDED skins/ardoise/images/redo_h.svg
557 DDED skins/ardoise/images/underline.svg
558 DDED skins/ardoise/images/underline_h.svg
559 DDED skins/ardoise/images/undo.svg
560 DDED skins/ardoise/images/undo_h.svg
--- a/skins/ardoise/README.md
+++ b/skins/ardoise/README.md
@@ -0,0 +1,15 @@
1
+## Ardoise theme
2
+
3
+A black and grey skin ("Ardoise" is the french word for slate).
4
+
5
+The skin includes custom icons for the file browser and the WYSIWYG editor, which are embedded directly in the css as base64 blobs. For convenience, they are also provided as standalone files in the images subdirectory.
6
+
7
+This skin was contributed by Antoine Chavasse.
8
+
9
+This theme is loosely based upon, and still contains some elements from the Blitz theme by James Moger.
10
+
11
+This theme embeds & uses a modified copy of [Normalize 3.0.2](https://necolas.github.io/normalize.css/) which is distributed under an [MIT license](https://github.com/necolas/normalize.css/blob/master/LICENSE.md).
12
+
13
+This theme embeds & uses a modified copy of [Skeleton](http://getskeleton.com) which is distributed under an [MIT license](https://github.com/dhg/Skeleton/blob/master/LICENSE.md).
14
+
15
+The sass version of Skeleton used in this project was made by [Seth Coelen](https://github.com/whatsnewsaes).
--- a/skins/ardoise/README.md
+++ b/skins/ardoise/README.md
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/skins/ardoise/README.md
+++ b/skins/ardoise/README.md
@@ -0,0 +1,15 @@
1 ## Ardoise theme
2
3 A black and grey skin ("Ardoise" is the french word for slate).
4
5 The skin includes custom icons for the file browser and the WYSIWYG editor, which are embedded directly in the css as base64 blobs. For convenience, they are also provided as standalone files in the images subdirectory.
6
7 This skin was contributed by Antoine Chavasse.
8
9 This theme is loosely based upon, and still contains some elements from the Blitz theme by James Moger.
10
11 This theme embeds & uses a modified copy of [Normalize 3.0.2](https://necolas.github.io/normalize.css/) which is distributed under an [MIT license](https://github.com/necolas/normalize.css/blob/master/LICENSE.md).
12
13 This theme embeds & uses a modified copy of [Skeleton](http://getskeleton.com) which is distributed under an [MIT license](https://github.com/dhg/Skeleton/blob/master/LICENSE.md).
14
15 The sass version of Skeleton used in this project was made by [Seth Coelen](https://github.com/whatsnewsaes).
--- a/skins/ardoise/css.txt
+++ b/skins/ardoise/css.txt
@@ -0,0 +1,77 @@
1
+width: 100%;
2
+ max-widt idth: 100%;
3
+ max-widt 1200px;
4
+ margin block32@2tS,19Z@2yb,7:: #4428N@3~l,ka@47j,R:pre.udiff,
5
+table.sbsdiffcolL@~E,I@23z,E:padding: 0 5pxy@4s~,j:
6
+}
7
+pre.udiff,
8
+pre.udiff pre,
9
+table.sbsdiffcolsR@4uA,1a:
10
+}
11
+pre.udiff {
12
+ padding: 10px 0
13
+}
14
+div.difftxtcol {
15
+ width: 52rem;
16
+ overflow-x: auto
17
+}
18
+span.diffchnP@AsW,Z:8080e8;
19
+ color: #000
20
+}
21
+span.diffadP@3_F,L@4wk,E:
22
+}
23
+span.diffrmO@3_G,3:c55C@2gD,a:000
24
+}
25
+div.diffmkrcol {
26
+ padding: 0 1J@4tG,J:111
27
+}
28
+span.diffhr {S@15W,11:margin: .5em 0 1em;
29
+ color: #555
30
+}
31
+span.diffln {
32
+ color: #666
33
+}5oX@54u,3c_Poq;t: solid #ff8000tr{
34
+ background: #663C, tr{
35
+ box-shadow: 0px: solid #0080ff2844
36
+}
37
+trackground: #003C66
38
+}x-shadow: 0span, tr{
39
+ background: #66003C;
40
+ box-shadow: 0px 0px 6px rgb(255, 0, 127)
41
+}
42
+late {
43
+ dinatemplate {
44
+ didipre.uidipre.udiff:focus,
45
+tab 255, 255, 0) 255, 255, 0)
46
+}
47
+,Center
48
+}
49
+.debugaa0
50
+}
51
+th: 100%;
52
+ max-widt 1200px;
53
+ margin block32@2tS,19Z@2yb,7:: #4428N@3~l,ka@47jpx;
54
+ margin blo;tr;th: 100%;
55
+ max-widt 1200px;
56
+ px;
57
+ m#663C00127, 0)
58
+}overflow-x: auto
59
+width: 100%;
60
+ m;
61
+ width: 100%2.43
62
+}
63
+h4 {
64
+ font-size: 235
65
+}
66
+h564.5rem;
67
+ margin: 0 .2rem00;;2px 3px
68
+}
69
+,}{};;;255,255,0);255,25 -ms-text-size-adjust: 100%;
70
+ -we 100%JO@Ii,R:-webkit-appearance: button;L@3rg,6L@bN,Y: {
71
+ -webkit-appearance: textfieldq@hN,Z@hQ,e:decoration {
72
+ -webkit-appearance: none
73
+}1DZ@iB,17: -webkit-appearance: none;
74
+ -moz-appearance: none;
75
+ appearance: noneK@25W,1n@1tB,N@YB,2X@1yA,H@1vl,49@20S,15:-webkit-appearance: none;
76
+ -moz-appearance: none;
77
+ appearance: none8eV@24X,3oJZCX;float: left
--- a/skins/ardoise/css.txt
+++ b/skins/ardoise/css.txt
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/skins/ardoise/css.txt
+++ b/skins/ardoise/css.txt
@@ -0,0 +1,77 @@
1 width: 100%;
2 max-widt idth: 100%;
3 max-widt 1200px;
4 margin block32@2tS,19Z@2yb,7:: #4428N@3~l,ka@47j,R:pre.udiff,
5 table.sbsdiffcolL@~E,I@23z,E:padding: 0 5pxy@4s~,j:
6 }
7 pre.udiff,
8 pre.udiff pre,
9 table.sbsdiffcolsR@4uA,1a:
10 }
11 pre.udiff {
12 padding: 10px 0
13 }
14 div.difftxtcol {
15 width: 52rem;
16 overflow-x: auto
17 }
18 span.diffchnP@AsW,Z:8080e8;
19 color: #000
20 }
21 span.diffadP@3_F,L@4wk,E:
22 }
23 span.diffrmO@3_G,3:c55C@2gD,a:000
24 }
25 div.diffmkrcol {
26 padding: 0 1J@4tG,J:111
27 }
28 span.diffhr {S@15W,11:margin: .5em 0 1em;
29 color: #555
30 }
31 span.diffln {
32 color: #666
33 }5oX@54u,3c_Poq;t: solid #ff8000tr{
34 background: #663C, tr{
35 box-shadow: 0px: solid #0080ff2844
36 }
37 trackground: #003C66
38 }x-shadow: 0span, tr{
39 background: #66003C;
40 box-shadow: 0px 0px 6px rgb(255, 0, 127)
41 }
42 late {
43 dinatemplate {
44 didipre.uidipre.udiff:focus,
45 tab 255, 255, 0) 255, 255, 0)
46 }
47 ,Center
48 }
49 .debugaa0
50 }
51 th: 100%;
52 max-widt 1200px;
53 margin block32@2tS,19Z@2yb,7:: #4428N@3~l,ka@47jpx;
54 margin blo;tr;th: 100%;
55 max-widt 1200px;
56 px;
57 m#663C00127, 0)
58 }overflow-x: auto
59 width: 100%;
60 m;
61 width: 100%2.43
62 }
63 h4 {
64 font-size: 235
65 }
66 h564.5rem;
67 margin: 0 .2rem00;;2px 3px
68 }
69 ,}{};;;255,255,0);255,25 -ms-text-size-adjust: 100%;
70 -we 100%JO@Ii,R:-webkit-appearance: button;L@3rg,6L@bN,Y: {
71 -webkit-appearance: textfieldq@hN,Z@hQ,e:decoration {
72 -webkit-appearance: none
73 }1DZ@iB,17: -webkit-appearance: none;
74 -moz-appearance: none;
75 appearance: noneK@25W,1n@1tB,N@YB,2X@1yA,H@1vl,49@20S,15:-webkit-appearance: none;
76 -moz-appearance: none;
77 appearance: none8eV@24X,3oJZCX;float: left
--- a/skins/ardoise/details.txt
+++ b/skins/ardoise/details.txt
@@ -0,0 +1,4 @@
1
+timeline-arrowheads: 0
2
+timeline-circle-nodes: 1
3
+timeline-color-graph-lines: 1
4
+whi
--- a/skins/ardoise/details.txt
+++ b/skins/ardoise/details.txt
@@ -0,0 +1,4 @@
 
 
 
 
--- a/skins/ardoise/details.txt
+++ b/skins/ardoise/details.txt
@@ -0,0 +1,4 @@
1 timeline-arrowheads: 0
2 timeline-circle-nodes: 1
3 timeline-color-graph-lines: 1
4 whi
--- a/skins/ardoise/footer.txt
+++ b/skins/ardoise/footer.txt
@@ -0,0 +1,12 @@
1
+ <th1>
2
+ if {[string first artifact $current_page] == 0 || [string first hexdump $chtml "</div>"
3
+ }
4
+ </th1>
5
+ </div> <!-- end div container -->
6
+</div> <!-- end div middle max-full-width -->
7
+<footer class="footer" max-fdiv class="footer" max-full-width -->
8
+<footer>
9
+ <div class="container">
10
+ <div class="pull-righwww.fossil-scm.org/">Fossil $release_version $manifest_version $manifest_date</a>
11
+ </div>
12
+ This page was generated in about <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]div>
--- a/skins/ardoise/footer.txt
+++ b/skins/ardoise/footer.txt
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
--- a/skins/ardoise/footer.txt
+++ b/skins/ardoise/footer.txt
@@ -0,0 +1,12 @@
1 <th1>
2 if {[string first artifact $current_page] == 0 || [string first hexdump $chtml "</div>"
3 }
4 </th1>
5 </div> <!-- end div container -->
6 </div> <!-- end div middle max-full-width -->
7 <footer class="footer" max-fdiv class="footer" max-full-width -->
8 <footer>
9 <div class="container">
10 <div class="pull-righwww.fossil-scm.org/">Fossil $release_version $manifest_version $manifest_date</a>
11 </div>
12 This page was generated in about <th1>puts [expr {([utime]+[stime]+1000)/1000*0.001}]div>
--- a/skins/ardoise/header.txt
+++ b/skins/ardoise/header.txt
@@ -0,0 +1,53 @@
1
+<div class="header">
2
+ <div class="container">
3
+ <div class="login pull-right">
4
+ <th1>
5
+ if {[info exists login]} {
6
+ html "<b>$login</b> — <a class='button' href='$home/login'>Logout</a>\n"
7
+ } else {
8
+ html "<a class='button' href='$home/login'>Login</a>\n"
9
+ }
10
+ </th1>
11
+ </div>
12
+ <div class='title'>
13
+ <h1>$<project_name>
14
+ <th1>
15
+ if {[anycap jor]} {
16
+ html "<a class='rss' href='$home/timeline.rss'></a>"
17
+ }
18
+ </th1>
19
+ <small> &nbsp;$<title></small></h1>
20
+ </div>
21
+
22
+ <!-- Main Menu -->
23
+ <div class="mainmenu"proc menulink {url name} {
24
+ upvar current_page current
25
+ upvar home home
26
+ if {[string r$current]] eq "/$current"} {
27
+ html "<li class='active'>"
28
+ l>
29
+et class</nav> <!homess "$c<nav id='hbdropdi menulink $index_page Home
30
+if {[anycap jor]} {
31
+ menulink /timeline Timeline
32
+}
33
+if {[hascap oh]} {
34
+ menulink /dir?ci=tip Files
35
+}
36
+if {[hascap o]} {
37
+ menulink /brlist Branches
38
+ menulink /taglist Tags
39
+}
40
+if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
41
+ menulink /forum Forum
42
+}
43
+if {[hashascap riv iki
44
+}
45
+if {[hascap o]} {
46
+ menulink /help Help
47
+ }
48
+if {[hascap s]} {
49
+ menulink /setup Admin
50
+} elseif {[hascap a]} {
51
+ menulink /setup_ulist Users
52
+}
53
+ c<nav id='hbdropdi <div opdi <div cladiv i
--- a/skins/ardoise/header.txt
+++ b/skins/ardoise/header.txt
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/skins/ardoise/header.txt
+++ b/skins/ardoise/header.txt
@@ -0,0 +1,53 @@
1 <div class="header">
2 <div class="container">
3 <div class="login pull-right">
4 <th1>
5 if {[info exists login]} {
6 html "<b>$login</b> — <a class='button' href='$home/login'>Logout</a>\n"
7 } else {
8 html "<a class='button' href='$home/login'>Login</a>\n"
9 }
10 </th1>
11 </div>
12 <div class='title'>
13 <h1>$<project_name>
14 <th1>
15 if {[anycap jor]} {
16 html "<a class='rss' href='$home/timeline.rss'></a>"
17 }
18 </th1>
19 <small> &nbsp;$<title></small></h1>
20 </div>
21
22 <!-- Main Menu -->
23 <div class="mainmenu"proc menulink {url name} {
24 upvar current_page current
25 upvar home home
26 if {[string r$current]] eq "/$current"} {
27 html "<li class='active'>"
28 l>
29 et class</nav> <!homess "$c<nav id='hbdropdi menulink $index_page Home
30 if {[anycap jor]} {
31 menulink /timeline Timeline
32 }
33 if {[hascap oh]} {
34 menulink /dir?ci=tip Files
35 }
36 if {[hascap o]} {
37 menulink /brlist Branches
38 menulink /taglist Tags
39 }
40 if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
41 menulink /forum Forum
42 }
43 if {[hashascap riv iki
44 }
45 if {[hascap o]} {
46 menulink /help Help
47 }
48 if {[hascap s]} {
49 menulink /setup Admin
50 } elseif {[hascap a]} {
51 menulink /setup_ulist Users
52 }
53 c<nav id='hbdropdi <div opdi <div cladiv i
--- a/skins/ardoise/images/active.svg
+++ b/skins/ardoise/images/active.svg
@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" width="17" height="9" viewBox="0 0 4.498 2.381"><path d="M4.233 2.381H.265l.998-1.058.986-1.058.998 1.062z" fill="#ff8000"/></svg>
--- a/skins/ardoise/images/active.svg
+++ b/skins/ardoise/images/active.svg
@@ -0,0 +1 @@
 
--- a/skins/ardoise/images/active.svg
+++ b/skins/ardoise/images/active.svg
@@ -0,0 +1 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="17" height="9" viewBox="0 0 4.498 2.381"><path d="M4.233 2.381H.265l.998-1.058.986-1.058.998 1.062z" fill="#ff8000"/></svg>
--- a/skins/ardoise/images/addindent.svg
+++ b/skins/ardoise/images/addindent.svg
@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M1.058 1.323h3.704M2.381 2.381h2.382M2.381 3.44h2.382M1.058 4.498h3.704" fill="none" stroke="#aaa" stroke-width=".529"/><path d="M2.117 2.91L.794 2.117v1.587z" fill="#66a8c7"/></svg>
--- a/skins/ardoise/images/addindent.svg
+++ b/skins/ardoise/images/addindent.svg
@@ -0,0 +1 @@
 
--- a/skins/ardoise/images/addindent.svg
+++ b/skins/ardoise/images/addindent.svg
@@ -0,0 +1 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M1.058 1.323h3.704M2.381 2.381h2.382M2.381 3.44h2.382M1.058 4.498h3.704" fill="none" stroke="#aaa" stroke-width=".529"/><path d="M2.117 2.91L.794 2.117v1.587z" fill="#66a8c7"/></svg>
--- a/skins/ardoise/images/addindent_h.svg
+++ b/skins/ardoise/images/addindent_h.svg
@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 5.821 5.821" height="22" width="22"><rect width="5.821" height="5.821" y="-.001" rx=".265" ry=".265" fill="#555"/><path d="M1.058 1.322h3.704M2.381 2.38h2.382M2.381 3.439h2.382M1.058 4.497h3.704" fill="none" stroke="#ddd" stroke-width=".529"/><path d="M2.117 2.91L.794 2.116v1.587z" fill="#ff8000"/></svg>
--- a/skins/ardoise/images/addindent_h.svg
+++ b/skins/ardoise/images/addindent_h.svg
@@ -0,0 +1 @@
 
--- a/skins/ardoise/images/addindent_h.svg
+++ b/skins/ardoise/images/addindent_h.svg
@@ -0,0 +1 @@
1 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 5.821 5.821" height="22" width="22"><rect width="5.821" height="5.821" y="-.001" rx=".265" ry=".265" fill="#555"/><path d="M1.058 1.322h3.704M2.381 2.38h2.382M2.381 3.439h2.382M1.058 4.497h3.704" fill="none" stroke="#ddd" stroke-width=".529"/><path d="M2.117 2.91L.794 2.116v1.587z" fill="#ff8000"/></svg>
--- a/skins/ardoise/images/blist.svg
+++ b/skins/ardoise/images/blist.svg
@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M2.117 1.322h2.91M2.117 2.91h2.91m-2.91 1.587h2.91" fill="none" stroke="#aaa" stroke-width=".529"/><circle r=".265" cy="1.322" cx="1.323" fill="#66a8c7"/><circle r=".265" cy="2.91" cx="1.323" fill="#66a8c7"/><circle r=".265" cy="4.497" cx="1.323" fill="#66a8c7"/></svg>
--- a/skins/ardoise/images/blist.svg
+++ b/skins/ardoise/images/blist.svg
@@ -0,0 +1 @@
 
--- a/skins/ardoise/images/blist.svg
+++ b/skins/ardoise/images/blist.svg
@@ -0,0 +1 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M2.117 1.322h2.91M2.117 2.91h2.91m-2.91 1.587h2.91" fill="none" stroke="#aaa" stroke-width=".529"/><circle r=".265" cy="1.322" cx="1.323" fill="#66a8c7"/><circle r=".265" cy="2.91" cx="1.323" fill="#66a8c7"/><circle r=".265" cy="4.497" cx="1.323" fill="#66a8c7"/></svg>
--- a/skins/ardoise/images/blist_h.svg
+++ b/skins/ardoise/images/blist_h.svg
@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M2.117 1.322h2.91M2.117 2.91h2.91m-2.91 1.587h2.91" fill="none" stroke="#ddd" stroke-width=".529"/><circle r=".265" cy="1.322" cx="1.323" fill="#ff8000"/><circle r=".265" cy="2.91" cx="1.323" fill="#ff8000"/><circle r=".265" cy="4.497" cx="1.323" fill="#ff8000"/></svg>
--- a/skins/ardoise/images/blist_h.svg
+++ b/skins/ardoise/images/blist_h.svg
@@ -0,0 +1 @@
 
--- a/skins/ardoise/images/blist_h.svg
+++ b/skins/ardoise/images/blist_h.svg
@@ -0,0 +1 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M2.117 1.322h2.91M2.117 2.91h2.91m-2.91 1.587h2.91" fill="none" stroke="#ddd" stroke-width=".529"/><circle r=".265" cy="1.322" cx="1.323" fill="#ff8000"/><circle r=".265" cy="2.91" cx="1.323" fill="#ff8000"/><circle r=".265" cy="4.497" cx="1.323" fill="#ff8000"/></svg>
--- a/skins/ardoise/images/bold.svg
+++ b/skins/ardoise/images/bold.svg
@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M1.866 1.349h.98q.67 0 .971.191.304.19.304.606 0 .282-.134.463-.131.18-.35.217v.022q.299.066.43.25.133.182.133.486 0 .43-.312.672-.31.241-.844.241H1.866zm.668 1.247h.387q.272 0 .392-.084.123-.084.123-.278 0-.181-.134-.259-.13-.08-.417-.08h-.351zm0 .53v.82h.435q.275 0 .407-.106.131-.105.131-.323 0-.392-.56-.392z" fill="#aaa"/></svg>
--- a/skins/ardoise/images/bold.svg
+++ b/skins/ardoise/images/bold.svg
@@ -0,0 +1 @@
 
--- a/skins/ardoise/images/bold.svg
+++ b/skins/ardoise/images/bold.svg
@@ -0,0 +1 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M1.866 1.349h.98q.67 0 .971.191.304.19.304.606 0 .282-.134.463-.131.18-.35.217v.022q.299.066.43.25.133.182.133.486 0 .43-.312.672-.31.241-.844.241H1.866zm.668 1.247h.387q.272 0 .392-.084.123-.084.123-.278 0-.181-.134-.259-.13-.08-.417-.08h-.351zm0 .53v.82h.435q.275 0 .407-.106.131-.105.131-.323 0-.392-.56-.392z" fill="#aaa"/></svg>
--- a/skins/ardoise/images/bold_h.svg
+++ b/skins/ardoise/images/bold_h.svg
@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M1.866 1.349h.98q.67 0 .971.191.304.19.304.606 0 .282-.134.463-.131.18-.35.217v.022q.299.066.43.25.133.182.133.486 0 .43-.312.672-.31.241-.844.241H1.866zm.668 1.247h.387q.272 0 .392-.084.123-.084.123-.278 0-.181-.134-.259-.13-.08-.417-.08h-.351zm0 .53v.82h.435q.275 0 .407-.106.131-.105.131-.323 0-.392-.56-.392z" fill="#ddd"/></svg>
--- a/skins/ardoise/images/bold_h.svg
+++ b/skins/ardoise/images/bold_h.svg
@@ -0,0 +1 @@
 
--- a/skins/ardoise/images/bold_h.svg
+++ b/skins/ardoise/images/bold_h.svg
@@ -0,0 +1 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M1.866 1.349h.98q.67 0 .971.191.304.19.304.606 0 .282-.134.463-.131.18-.35.217v.022q.299.066.43.25.133.182.133.486 0 .43-.312.672-.31.241-.844.241H1.866zm.668 1.247h.387q.272 0 .392-.084.123-.084.123-.278 0-.181-.134-.259-.13-.08-.417-.08h-.351zm0 .53v.82h.435q.275 0 .407-.106.131-.105.131-.323 0-.392-.56-.392z" fill="#ddd"/></svg>
--- a/skins/ardoise/images/calign.svg
+++ b/skins/ardoise/images/calign.svg
@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M1.058 1.323h3.704m-2.91 1.058H3.97M1.058 3.44h3.704m-2.91 1.058H3.97" fill="none" stroke="#aaa" stroke-width=".529"/></svg>
--- a/skins/ardoise/images/calign.svg
+++ b/skins/ardoise/images/calign.svg
@@ -0,0 +1 @@
 
--- a/skins/ardoise/images/calign.svg
+++ b/skins/ardoise/images/calign.svg
@@ -0,0 +1 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M1.058 1.323h3.704m-2.91 1.058H3.97M1.058 3.44h3.704m-2.91 1.058H3.97" fill="none" stroke="#aaa" stroke-width=".529"/></svg>
--- a/skins/ardoise/images/calign_h.svg
+++ b/skins/ardoise/images/calign_h.svg
@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M1.058 1.322h3.704M1.852 2.38H3.97M1.058 3.439h3.704m-2.91 1.058H3.97" fill="none" stroke="#ddd" stroke-width=".529"/></svg>
--- a/skins/ardoise/images/calign_h.svg
+++ b/skins/ardoise/images/calign_h.svg
@@ -0,0 +1 @@
 
--- a/skins/ardoise/images/calign_h.svg
+++ b/skins/ardoise/images/calign_h.svg
@@ -0,0 +1 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M1.058 1.322h3.704M1.852 2.38H3.97M1.058 3.439h3.704m-2.91 1.058H3.97" fill="none" stroke="#ddd" stroke-width=".529"/></svg>
--- a/skins/ardoise/images/clrfmt.svg
+++ b/skins/ardoise/images/clrfmt.svg
@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M1.72 2.346l-.172.317q-.034.064-.051.112-.016.049-.016.086 0 .066.048.097.049.03.15.03h.07l-.039.187H.794l.038-.188h.074q.048 0 .084-.01.038-.012.072-.043.035-.031.071-.085.038-.053.086-.136L2.416.657H2.9l.296 2.077q.009.055.022.102.016.046.04.08.024.033.06.052.038.02.093.02h.064l-.038.187H2.272l.038-.188h.08q.114 0 .181-.034.068-.036.068-.114 0-.029-.002-.057l-.005-.055-.054-.38zm.77-.807q-.008-.078-.017-.145L2.46 1.26l-.01-.124-.005-.124q-.026.064-.052.12l-.055.11-.065.118-.081.145-.35.625h.713zm2.238 3.753l-.128-.42h-.642l-.128.42h-.403l.622-1.77h.457l.625 1.77zm-.217-.733l-.2-.646q-.022-.074-.031-.118-.04.155-.228.764z" fill="#66a8c7"/><path d="M1.323 3.704c0 1.059 1.323.764 1.323.764" fill="none" stroke="#aaa" stroke-width=".529" stroke-linecap="square"/><path d="M2.381 3.704v1.588l1.059-.794z" fill="#aaa"/></svg>
--- a/skins/ardoise/images/clrfmt.svg
+++ b/skins/ardoise/images/clrfmt.svg
@@ -0,0 +1 @@
 
--- a/skins/ardoise/images/clrfmt.svg
+++ b/skins/ardoise/images/clrfmt.svg
@@ -0,0 +1 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M1.72 2.346l-.172.317q-.034.064-.051.112-.016.049-.016.086 0 .066.048.097.049.03.15.03h.07l-.039.187H.794l.038-.188h.074q.048 0 .084-.01.038-.012.072-.043.035-.031.071-.085.038-.053.086-.136L2.416.657H2.9l.296 2.077q.009.055.022.102.016.046.04.08.024.033.06.052.038.02.093.02h.064l-.038.187H2.272l.038-.188h.08q.114 0 .181-.034.068-.036.068-.114 0-.029-.002-.057l-.005-.055-.054-.38zm.77-.807q-.008-.078-.017-.145L2.46 1.26l-.01-.124-.005-.124q-.026.064-.052.12l-.055.11-.065.118-.081.145-.35.625h.713zm2.238 3.753l-.128-.42h-.642l-.128.42h-.403l.622-1.77h.457l.625 1.77zm-.217-.733l-.2-.646q-.022-.074-.031-.118-.04.155-.228.764z" fill="#66a8c7"/><path d="M1.323 3.704c0 1.059 1.323.764 1.323.764" fill="none" stroke="#aaa" stroke-width=".529" stroke-linecap="square"/><path d="M2.381 3.704v1.588l1.059-.794z" fill="#aaa"/></svg>
--- a/skins/ardoise/images/clrfmt_h.svg
+++ b/skins/ardoise/images/clrfmt_h.svg
@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 5.821 5.821" height="22" width="22"><rect fill="#555" width="5.821" height="5.821" y="-.001" rx=".265" ry=".265"/><path fill="#ff8000" d="M1.72 2.346l-.172.317q-.034.063-.051.112-.016.048-.016.086 0 .065.048.096.049.03.15.03h.07l-.039.187H.794l.038-.188h.074q.048 0 .084-.01.038-.012.072-.043.035-.031.071-.084.038-.054.086-.136L2.416.656H2.9l.296 2.077q.009.055.022.102.016.046.04.08t.06.053q.038.019.093.019h.064l-.038.187H2.272l.038-.187h.08q.114 0 .181-.035.068-.036.068-.114 0-.029-.002-.056l-.005-.055-.054-.381zm.77-.808q-.008-.078-.017-.145l-.014-.133-.01-.124-.005-.124q-.026.064-.052.12l-.055.11-.065.118-.081.145-.35.625h.713zM4.728 5.29L4.6 4.871h-.642l-.128.42h-.403l.622-1.77h.457l.625 1.77zm-.217-.732l-.2-.645q-.022-.075-.031-.119-.04.155-.228.764z"/><path stroke-linecap="square" stroke-width=".529" stroke="#ddd" fill="none" d="M1.323 3.703c0 1.059 1.323.764 1.323.764"/><path fill="#ddd" d="M2.381 3.703v1.588l1.059-.794z"/></svg>
--- a/skins/ardoise/images/clrfmt_h.svg
+++ b/skins/ardoise/images/clrfmt_h.svg
@@ -0,0 +1 @@
 
--- a/skins/ardoise/images/clrfmt_h.svg
+++ b/skins/ardoise/images/clrfmt_h.svg
@@ -0,0 +1 @@
1 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 5.821 5.821" height="22" width="22"><rect fill="#555" width="5.821" height="5.821" y="-.001" rx=".265" ry=".265"/><path fill="#ff8000" d="M1.72 2.346l-.172.317q-.034.063-.051.112-.016.048-.016.086 0 .065.048.096.049.03.15.03h.07l-.039.187H.794l.038-.188h.074q.048 0 .084-.01.038-.012.072-.043.035-.031.071-.084.038-.054.086-.136L2.416.656H2.9l.296 2.077q.009.055.022.102.016.046.04.08t.06.053q.038.019.093.019h.064l-.038.187H2.272l.038-.187h.08q.114 0 .181-.035.068-.036.068-.114 0-.029-.002-.056l-.005-.055-.054-.381zm.77-.808q-.008-.078-.017-.145l-.014-.133-.01-.124-.005-.124q-.026.064-.052.12l-.055.11-.065.118-.081.145-.35.625h.713zM4.728 5.29L4.6 4.871h-.642l-.128.42h-.403l.622-1.77h.457l.625 1.77zm-.217-.732l-.2-.645q-.022-.075-.031-.119-.04.155-.228.764z"/><path stroke-linecap="square" stroke-width=".529" stroke="#ddd" fill="none" d="M1.323 3.703c0 1.059 1.323.764 1.323.764"/><path fill="#ddd" d="M2.381 3.703v1.588l1.059-.794z"/></svg>
--- a/skins/ardoise/images/delindent.svg
+++ b/skins/ardoise/images/delindent.svg
@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M1.058 1.323h3.704M2.381 2.381h2.382M2.381 3.44h2.382M1.058 4.498h3.704" fill="none" stroke="#aaa" stroke-width=".529"/><path d="M.53 2.91l1.322-.793v1.587z" fill="#66a8c7"/></svg>
--- a/skins/ardoise/images/delindent.svg
+++ b/skins/ardoise/images/delindent.svg
@@ -0,0 +1 @@
 
--- a/skins/ardoise/images/delindent.svg
+++ b/skins/ardoise/images/delindent.svg
@@ -0,0 +1 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M1.058 1.323h3.704M2.381 2.381h2.382M2.381 3.44h2.382M1.058 4.498h3.704" fill="none" stroke="#aaa" stroke-width=".529"/><path d="M.53 2.91l1.322-.793v1.587z" fill="#66a8c7"/></svg>
--- a/skins/ardoise/images/delindent_h.svg
+++ b/skins/ardoise/images/delindent_h.svg
@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M1.058 1.322h3.704M2.381 2.38h2.382M2.381 3.439h2.382M1.058 4.497h3.704" fill="none" stroke="#ddd" stroke-width=".529"/><path d="M.53 2.91l1.322-.794v1.587z" fill="#ff8000"/></svg>
--- a/skins/ardoise/images/delindent_h.svg
+++ b/skins/ardoise/images/delindent_h.svg
@@ -0,0 +1 @@
 
--- a/skins/ardoise/images/delindent_h.svg
+++ b/skins/ardoise/images/delindent_h.svg
@@ -0,0 +1 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M1.058 1.322h3.704M2.381 2.38h2.382M2.381 3.439h2.382M1.058 4.497h3.704" fill="none" stroke="#ddd" stroke-width=".529"/><path d="M.53 2.91l1.322-.794v1.587z" fill="#ff8000"/></svg>
--- a/skins/ardoise/images/dir.svg
+++ b/skins/ardoise/images/dir.svg
@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" width="20" height="16" viewBox="0 0 5.292 4.233"><path d="M.794.53v3.174h3.704V1.323H2.91V.529z" fill="#1d2021" stroke="#ff8000" stroke-width=".529" stroke-linecap="round" stroke-linejoin="round"/></svg>
--- a/skins/ardoise/images/dir.svg
+++ b/skins/ardoise/images/dir.svg
@@ -0,0 +1 @@
 
--- a/skins/ardoise/images/dir.svg
+++ b/skins/ardoise/images/dir.svg
@@ -0,0 +1 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="20" height="16" viewBox="0 0 5.292 4.233"><path d="M.794.53v3.174h3.704V1.323H2.91V.529z" fill="#1d2021" stroke="#ff8000" stroke-width=".529" stroke-linecap="round" stroke-linejoin="round"/></svg>
--- a/skins/ardoise/images/file.svg
+++ b/skins/ardoise/images/file.svg
@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" width="20" height="16" viewBox="0 0 5.292 4.233"><path d="M1.323.265v3.704h2.646V1.323L2.91.265z" fill="#1d2021" stroke="#ddd" stroke-width=".529" stroke-linejoin="round"/><path d="M2.646.265h.264v1.323h1.06" fill="#1d2021" stroke="#ddd" stroke-width=".529" stroke-linejoin="round"/></svg>
--- a/skins/ardoise/images/file.svg
+++ b/skins/ardoise/images/file.svg
@@ -0,0 +1 @@
 
--- a/skins/ardoise/images/file.svg
+++ b/skins/ardoise/images/file.svg
@@ -0,0 +1 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="20" height="16" viewBox="0 0 5.292 4.233"><path d="M1.323.265v3.704h2.646V1.323L2.91.265z" fill="#1d2021" stroke="#ddd" stroke-width=".529" stroke-linejoin="round"/><path d="M2.646.265h.264v1.323h1.06" fill="#1d2021" stroke="#ddd" stroke-width=".529" stroke-linejoin="round"/></svg>
--- a/skins/ardoise/images/italic.svg
+++ b/skins/ardoise/images/italic.svg
@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M3.245 4.497H1.852l.077-.379.403-.177.433-2.037-.327-.176.08-.38H3.91l-.08.38-.41.176-.432 2.037.336.177z" fill="#aaa"/></svg>
--- a/skins/ardoise/images/italic.svg
+++ b/skins/ardoise/images/italic.svg
@@ -0,0 +1 @@
 
--- a/skins/ardoise/images/italic.svg
+++ b/skins/ardoise/images/italic.svg
@@ -0,0 +1 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M3.245 4.497H1.852l.077-.379.403-.177.433-2.037-.327-.176.08-.38H3.91l-.08.38-.41.176-.432 2.037.336.177z" fill="#aaa"/></svg>
--- a/skins/ardoise/images/italic_h.svg
+++ b/skins/ardoise/images/italic_h.svg
@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M3.245 4.497H1.852l.077-.379.403-.177.433-2.037-.327-.176.08-.38H3.91l-.08.38-.41.176-.432 2.037.336.177z" fill="#ddd"/></svg>
--- a/skins/ardoise/images/italic_h.svg
+++ b/skins/ardoise/images/italic_h.svg
@@ -0,0 +1 @@
 
--- a/skins/ardoise/images/italic_h.svg
+++ b/skins/ardoise/images/italic_h.svg
@@ -0,0 +1 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M3.245 4.497H1.852l.077-.379.403-.177.433-2.037-.327-.176.08-.38H3.91l-.08.38-.41.176-.432 2.037.336.177z" fill="#ddd"/></svg>
--- a/skins/ardoise/images/lalign.svg
+++ b/skins/ardoise/images/lalign.svg
@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M1.058 1.323h3.704M1.058 2.381h2.91M1.058 3.44h3.704M1.058 4.498h2.91" fill="none" stroke="#aaa" stroke-width=".529"/></svg>
--- a/skins/ardoise/images/lalign.svg
+++ b/skins/ardoise/images/lalign.svg
@@ -0,0 +1 @@
 
--- a/skins/ardoise/images/lalign.svg
+++ b/skins/ardoise/images/lalign.svg
@@ -0,0 +1 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M1.058 1.323h3.704M1.058 2.381h2.91M1.058 3.44h3.704M1.058 4.498h2.91" fill="none" stroke="#aaa" stroke-width=".529"/></svg>
--- a/skins/ardoise/images/lalign_h.svg
+++ b/skins/ardoise/images/lalign_h.svg
@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M1.058 1.322h3.704M1.058 2.38h2.91m-2.91 1.059h3.704M1.058 4.497h2.91" fill="none" stroke="#ddd" stroke-width=".529"/></svg>
--- a/skins/ardoise/images/lalign_h.svg
+++ b/skins/ardoise/images/lalign_h.svg
@@ -0,0 +1 @@
 
--- a/skins/ardoise/images/lalign_h.svg
+++ b/skins/ardoise/images/lalign_h.svg
@@ -0,0 +1 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M1.058 1.322h3.704M1.058 2.38h2.91m-2.91 1.059h3.704M1.058 4.497h2.91" fill="none" stroke="#ddd" stroke-width=".529"/></svg>
--- a/skins/ardoise/images/link.svg
+++ b/skins/ardoise/images/link.svg
@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M1.752 3.969h.793m-.793-2.117h.793M1.752 3.97c-.343 0-.659-.119-.83-.415-.171-.297-.171-.99 0-1.287.171-.296.487-.415.83-.415m2.217 2.116h-.794m.794-2.117h-.794m.794 2.117c.342 0 .658-.119.83-.415.17-.297.17-.99 0-1.287-.172-.296-.488-.415-.83-.415M2.117 2.91h1.587" fill="none" stroke="#aaa" stroke-width=".529"/></svg>
--- a/skins/ardoise/images/link.svg
+++ b/skins/ardoise/images/link.svg
@@ -0,0 +1 @@
 
--- a/skins/ardoise/images/link.svg
+++ b/skins/ardoise/images/link.svg
@@ -0,0 +1 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M1.752 3.969h.793m-.793-2.117h.793M1.752 3.97c-.343 0-.659-.119-.83-.415-.171-.297-.171-.99 0-1.287.171-.296.487-.415.83-.415m2.217 2.116h-.794m.794-2.117h-.794m.794 2.117c.342 0 .658-.119.83-.415.17-.297.17-.99 0-1.287-.172-.296-.488-.415-.83-.415M2.117 2.91h1.587" fill="none" stroke="#aaa" stroke-width=".529"/></svg>
--- a/skins/ardoise/images/link_h.svg
+++ b/skins/ardoise/images/link_h.svg
@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M1.752 3.968h.793m-.793-2.117h.793m-.793 2.117c-.343 0-.659-.119-.83-.415-.171-.297-.171-.99 0-1.287.171-.296.487-.415.83-.415m2.217 2.117h-.794m.794-2.117h-.794m.794 2.117c.342 0 .658-.119.83-.415.17-.297.17-.99 0-1.287-.172-.296-.488-.415-.83-.415M2.117 2.91h1.587" fill="none" stroke="#ddd" stroke-width=".529"/></svg>
--- a/skins/ardoise/images/link_h.svg
+++ b/skins/ardoise/images/link_h.svg
@@ -0,0 +1 @@
 
--- a/skins/ardoise/images/link_h.svg
+++ b/skins/ardoise/images/link_h.svg
@@ -0,0 +1 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M1.752 3.968h.793m-.793-2.117h.793m-.793 2.117c-.343 0-.659-.119-.83-.415-.171-.297-.171-.99 0-1.287.171-.296.487-.415.83-.415m2.217 2.117h-.794m.794-2.117h-.794m.794 2.117c.342 0 .658-.119.83-.415.17-.297.17-.99 0-1.287-.172-.296-.488-.415-.83-.415M2.117 2.91h1.587" fill="none" stroke="#ddd" stroke-width=".529"/></svg>
--- a/skins/ardoise/images/nlist.svg
+++ b/skins/ardoise/images/nlist.svg
@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M2.117 1.323h2.91M2.117 2.91h2.91m-2.91 1.588h2.91" fill="none" stroke="#aaa" stroke-width=".529"/><path d="M1.523 1.851h-.266v-.729l.002-.12.004-.13q-.066.066-.092.087l-.144.116-.129-.16.406-.323h.219zm.222 1.588h-.88v-.185l.316-.32q.14-.144.183-.199.043-.056.062-.103.02-.047.02-.098 0-.076-.043-.113-.041-.037-.111-.037-.073 0-.142.033-.07.034-.144.096L.86 2.342q.093-.08.154-.112.062-.033.134-.05.072-.018.162-.018.118 0 .208.043t.14.12q.05.078.05.178 0 .087-.03.163-.03.076-.095.156-.064.08-.226.229l-.162.152v.012h.549zm-.049.609q0 .118-.072.201-.071.083-.2.114v.005q.152.019.23.093.079.073.079.198 0 .182-.132.283-.132.101-.376.101-.205 0-.364-.068v-.226q.073.037.161.06.088.023.174.023.132 0 .195-.045t.063-.144q0-.088-.073-.125-.072-.037-.23-.037h-.096v-.205h.097q.147 0 .214-.037.068-.04.068-.132 0-.143-.18-.143-.061 0-.126.02-.064.021-.142.072l-.123-.183q.172-.124.41-.124.196 0 .31.079.113.079.113.22z" fill="#66a8c7"/></svg>
--- a/skins/ardoise/images/nlist.svg
+++ b/skins/ardoise/images/nlist.svg
@@ -0,0 +1 @@
 
--- a/skins/ardoise/images/nlist.svg
+++ b/skins/ardoise/images/nlist.svg
@@ -0,0 +1 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M2.117 1.323h2.91M2.117 2.91h2.91m-2.91 1.588h2.91" fill="none" stroke="#aaa" stroke-width=".529"/><path d="M1.523 1.851h-.266v-.729l.002-.12.004-.13q-.066.066-.092.087l-.144.116-.129-.16.406-.323h.219zm.222 1.588h-.88v-.185l.316-.32q.14-.144.183-.199.043-.056.062-.103.02-.047.02-.098 0-.076-.043-.113-.041-.037-.111-.037-.073 0-.142.033-.07.034-.144.096L.86 2.342q.093-.08.154-.112.062-.033.134-.05.072-.018.162-.018.118 0 .208.043t.14.12q.05.078.05.178 0 .087-.03.163-.03.076-.095.156-.064.08-.226.229l-.162.152v.012h.549zm-.049.609q0 .118-.072.201-.071.083-.2.114v.005q.152.019.23.093.079.073.079.198 0 .182-.132.283-.132.101-.376.101-.205 0-.364-.068v-.226q.073.037.161.06.088.023.174.023.132 0 .195-.045t.063-.144q0-.088-.073-.125-.072-.037-.23-.037h-.096v-.205h.097q.147 0 .214-.037.068-.04.068-.132 0-.143-.18-.143-.061 0-.126.02-.064.021-.142.072l-.123-.183q.172-.124.41-.124.196 0 .31.079.113.079.113.22z" fill="#66a8c7"/></svg>
--- a/skins/ardoise/images/nlist_h.svg
+++ b/skins/ardoise/images/nlist_h.svg
@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M2.117 1.322h2.91M2.117 2.91h2.91m-2.91 1.587h2.91" fill="none" stroke="#ddd" stroke-width=".529"/><path d="M1.523 1.851h-.266v-.729l.002-.12.004-.13q-.066.066-.092.087l-.144.116-.129-.16.406-.323h.219zm.222 1.588h-.88v-.185l.316-.32q.14-.144.183-.199.043-.056.062-.103.02-.047.02-.098 0-.076-.043-.113-.041-.037-.111-.037-.073 0-.142.033-.07.034-.144.096L.86 2.342q.093-.08.154-.112.062-.033.134-.05.072-.018.162-.018.118 0 .208.043t.14.12q.05.078.05.178 0 .087-.03.163-.03.076-.095.156-.064.08-.226.229l-.162.152v.012h.549zm-.049.609q0 .118-.072.201-.071.083-.2.114v.005q.152.019.23.093.079.073.079.198 0 .182-.132.283-.132.101-.376.101-.205 0-.364-.068v-.226q.073.037.161.06.088.023.174.023.132 0 .195-.045t.063-.144q0-.088-.073-.125-.072-.037-.23-.037h-.096v-.205h.097q.147 0 .214-.037.068-.04.068-.132 0-.143-.18-.143-.061 0-.126.02-.064.021-.142.072l-.123-.183q.172-.124.41-.124.196 0 .31.079.113.079.113.22z" fill="#ff8000"/></svg>
--- a/skins/ardoise/images/nlist_h.svg
+++ b/skins/ardoise/images/nlist_h.svg
@@ -0,0 +1 @@
 
--- a/skins/ardoise/images/nlist_h.svg
+++ b/skins/ardoise/images/nlist_h.svg
@@ -0,0 +1 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M2.117 1.322h2.91M2.117 2.91h2.91m-2.91 1.587h2.91" fill="none" stroke="#ddd" stroke-width=".529"/><path d="M1.523 1.851h-.266v-.729l.002-.12.004-.13q-.066.066-.092.087l-.144.116-.129-.16.406-.323h.219zm.222 1.588h-.88v-.185l.316-.32q.14-.144.183-.199.043-.056.062-.103.02-.047.02-.098 0-.076-.043-.113-.041-.037-.111-.037-.073 0-.142.033-.07.034-.144.096L.86 2.342q.093-.08.154-.112.062-.033.134-.05.072-.018.162-.018.118 0 .208.043t.14.12q.05.078.05.178 0 .087-.03.163-.03.076-.095.156-.064.08-.226.229l-.162.152v.012h.549zm-.049.609q0 .118-.072.201-.071.083-.2.114v.005q.152.019.23.093.079.073.079.198 0 .182-.132.283-.132.101-.376.101-.205 0-.364-.068v-.226q.073.037.161.06.088.023.174.023.132 0 .195-.045t.063-.144q0-.088-.073-.125-.072-.037-.23-.037h-.096v-.205h.097q.147 0 .214-.037.068-.04.068-.132 0-.143-.18-.143-.061 0-.126.02-.064.021-.142.072l-.123-.183q.172-.124.41-.124.196 0 .31.079.113.079.113.22z" fill="#ff8000"/></svg>
--- a/skins/ardoise/images/quote.svg
+++ b/skins/ardoise/images/quote.svg
@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M3.092 2.509q0-.27.083-.521.082-.253.26-.468.182-.215.467-.384.29-.174.703-.285v.438q-.182.066-.322.132-.141.066-.236.145-.091.074-.14.17-.046.09-.046.214 0 .083.045.133.046.05.116.095l.149.095q.079.045.149.12.07.07.116.182.045.111.045.28 0 .286-.178.435-.173.149-.434.149-.36 0-.57-.248-.207-.248-.207-.682zm-1.77 0q0-.27.083-.521.083-.253.26-.468.182-.215.468-.384.29-.174.703-.285v.438q-.186.066-.327.132-.136.066-.232.145-.09.074-.14.17-.046.09-.046.214 0 .083.046.133.045.05.116.095l.148.095q.079.045.15.12.07.07.115.182.046.111.046.28 0 .286-.178.435-.174.149-.434.149-.36 0-.57-.248-.208-.248-.208-.682z" fill="#66a8c7"/></svg>
--- a/skins/ardoise/images/quote.svg
+++ b/skins/ardoise/images/quote.svg
@@ -0,0 +1 @@
 
--- a/skins/ardoise/images/quote.svg
+++ b/skins/ardoise/images/quote.svg
@@ -0,0 +1 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M3.092 2.509q0-.27.083-.521.082-.253.26-.468.182-.215.467-.384.29-.174.703-.285v.438q-.182.066-.322.132-.141.066-.236.145-.091.074-.14.17-.046.09-.046.214 0 .083.045.133.046.05.116.095l.149.095q.079.045.149.12.07.07.116.182.045.111.045.28 0 .286-.178.435-.173.149-.434.149-.36 0-.57-.248-.207-.248-.207-.682zm-1.77 0q0-.27.083-.521.083-.253.26-.468.182-.215.468-.384.29-.174.703-.285v.438q-.186.066-.327.132-.136.066-.232.145-.09.074-.14.17-.046.09-.046.214 0 .083.046.133.045.05.116.095l.148.095q.079.045.15.12.07.07.115.182.046.111.046.28 0 .286-.178.435-.174.149-.434.149-.36 0-.57-.248-.208-.248-.208-.682z" fill="#66a8c7"/></svg>
--- a/skins/ardoise/images/quote_h.svg
+++ b/skins/ardoise/images/quote_h.svg
@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M3.092 2.509q0-.27.083-.521.082-.253.26-.468.182-.215.467-.384.29-.174.703-.285v.438q-.182.066-.322.132-.141.066-.236.145-.091.074-.14.17-.046.09-.046.214 0 .083.045.133.046.05.116.095l.149.095q.079.045.149.12.07.07.116.182.045.111.045.28 0 .286-.178.435-.173.149-.434.149-.36 0-.57-.248-.207-.248-.207-.682zm-1.77 0q0-.27.083-.521.083-.253.26-.468.182-.215.468-.384.29-.174.703-.285v.438q-.186.066-.327.132-.136.066-.232.145-.09.074-.14.17-.046.09-.046.214 0 .083.046.133.045.05.116.095l.148.095q.079.045.15.12.07.07.115.182.046.111.046.28 0 .286-.178.435-.174.149-.434.149-.36 0-.57-.248-.208-.248-.208-.682z" fill="#ff8000"/></svg>
--- a/skins/ardoise/images/quote_h.svg
+++ b/skins/ardoise/images/quote_h.svg
@@ -0,0 +1 @@
 
--- a/skins/ardoise/images/quote_h.svg
+++ b/skins/ardoise/images/quote_h.svg
@@ -0,0 +1 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M3.092 2.509q0-.27.083-.521.082-.253.26-.468.182-.215.467-.384.29-.174.703-.285v.438q-.182.066-.322.132-.141.066-.236.145-.091.074-.14.17-.046.09-.046.214 0 .083.045.133.046.05.116.095l.149.095q.079.045.149.12.07.07.116.182.045.111.045.28 0 .286-.178.435-.173.149-.434.149-.36 0-.57-.248-.207-.248-.207-.682zm-1.77 0q0-.27.083-.521.083-.253.26-.468.182-.215.468-.384.29-.174.703-.285v.438q-.186.066-.327.132-.136.066-.232.145-.09.074-.14.17-.046.09-.046.214 0 .083.046.133.045.05.116.095l.148.095q.079.045.15.12.07.07.115.182.046.111.046.28 0 .286-.178.435-.174.149-.434.149-.36 0-.57-.248-.208-.248-.208-.682z" fill="#ff8000"/></svg>
--- a/skins/ardoise/images/ralign.svg
+++ b/skins/ardoise/images/ralign.svg
@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M1.058 1.323h3.704m-2.91 1.058h2.91M1.058 3.44h3.704m-2.91 1.058h2.91" fill="none" stroke="#aaa" stroke-width=".529"/></svg>
--- a/skins/ardoise/images/ralign.svg
+++ b/skins/ardoise/images/ralign.svg
@@ -0,0 +1 @@
 
--- a/skins/ardoise/images/ralign.svg
+++ b/skins/ardoise/images/ralign.svg
@@ -0,0 +1 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M1.058 1.323h3.704m-2.91 1.058h2.91M1.058 3.44h3.704m-2.91 1.058h2.91" fill="none" stroke="#aaa" stroke-width=".529"/></svg>
--- a/skins/ardoise/images/ralign_h.svg
+++ b/skins/ardoise/images/ralign_h.svg
@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M1.058 1.322h3.704M1.852 2.38h2.91M1.058 3.439h3.704m-2.91 1.058h2.91" fill="none" stroke="#ddd" stroke-width=".529"/></svg>
--- a/skins/ardoise/images/ralign_h.svg
+++ b/skins/ardoise/images/ralign_h.svg
@@ -0,0 +1 @@
 
--- a/skins/ardoise/images/ralign_h.svg
+++ b/skins/ardoise/images/ralign_h.svg
@@ -0,0 +1 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M1.058 1.322h3.704M1.852 2.38h2.91M1.058 3.439h3.704m-2.91 1.058h2.91" fill="none" stroke="#ddd" stroke-width=".529"/></svg>
--- a/skins/ardoise/images/redo.svg
+++ b/skins/ardoise/images/redo.svg
@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M3.734 2.007c-.314-.418-.847-.655-1.323-.529s-.946.528-1.058 1.058c-.113.531.154 1.237.529 1.588" fill="none" stroke="#66a8c7" stroke-width=".529" stroke-linecap="square" stroke-linejoin="bevel"/><path d="M4.528 1.19L2.94 2.91h1.588z" fill="#66a8c7"/></svg>
--- a/skins/ardoise/images/redo.svg
+++ b/skins/ardoise/images/redo.svg
@@ -0,0 +1 @@
 
--- a/skins/ardoise/images/redo.svg
+++ b/skins/ardoise/images/redo.svg
@@ -0,0 +1 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M3.734 2.007c-.314-.418-.847-.655-1.323-.529s-.946.528-1.058 1.058c-.113.531.154 1.237.529 1.588" fill="none" stroke="#66a8c7" stroke-width=".529" stroke-linecap="square" stroke-linejoin="bevel"/><path d="M4.528 1.19L2.94 2.91h1.588z" fill="#66a8c7"/></svg>
--- a/skins/ardoise/images/redo_h.svg
+++ b/skins/ardoise/images/redo_h.svg
@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M3.734 2.006c-.314-.417-.847-.655-1.323-.529s-.946.528-1.058 1.059c-.113.53.154 1.236.529 1.587" fill="none" stroke="#ff8000" stroke-width=".529" stroke-linecap="square" stroke-linejoin="bevel"/><path d="M4.528 1.19L2.94 2.91h1.588z" fill="#ff8000"/></svg>
--- a/skins/ardoise/images/redo_h.svg
+++ b/skins/ardoise/images/redo_h.svg
@@ -0,0 +1 @@
 
--- a/skins/ardoise/images/redo_h.svg
+++ b/skins/ardoise/images/redo_h.svg
@@ -0,0 +1 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M3.734 2.006c-.314-.417-.847-.655-1.323-.529s-.946.528-1.058 1.059c-.113.53.154 1.236.529 1.587" fill="none" stroke="#ff8000" stroke-width=".529" stroke-linecap="square" stroke-linejoin="bevel"/><path d="M4.528 1.19L2.94 2.91h1.588z" fill="#ff8000"/></svg>
--- a/skins/ardoise/images/underline.svg
+++ b/skins/ardoise/images/underline.svg
@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M4.172 1.04v2.038q0 .349-.158.611-.155.263-.45.403-.295.14-.697.14-.607 0-.943-.31-.336-.312-.336-.853V1.041h.665v1.927q0 .364.146.534.147.17.485.17.327 0 .474-.17.148-.172.148-.538V1.04z" fill="#aaa"/><path d="M1.323 4.762h3.175" fill="none" stroke="#aaa" stroke-width=".529"/></svg>
--- a/skins/ardoise/images/underline.svg
+++ b/skins/ardoise/images/underline.svg
@@ -0,0 +1 @@
 
--- a/skins/ardoise/images/underline.svg
+++ b/skins/ardoise/images/underline.svg
@@ -0,0 +1 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M4.172 1.04v2.038q0 .349-.158.611-.155.263-.45.403-.295.14-.697.14-.607 0-.943-.31-.336-.312-.336-.853V1.041h.665v1.927q0 .364.146.534.147.17.485.17.327 0 .474-.17.148-.172.148-.538V1.04z" fill="#aaa"/><path d="M1.323 4.762h3.175" fill="none" stroke="#aaa" stroke-width=".529"/></svg>
--- a/skins/ardoise/images/underline_h.svg
+++ b/skins/ardoise/images/underline_h.svg
@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M4.172 1.04v2.038q0 .349-.158.611-.155.263-.45.403-.295.14-.697.14-.607 0-.943-.31-.336-.312-.336-.853V1.041h.665v1.927q0 .364.146.534.147.17.485.17.327 0 .474-.17.148-.172.148-.538V1.04z" fill="#ddd"/><path d="M1.323 4.762h3.175" fill="none" stroke="#ddd" stroke-width=".529"/></svg>
--- a/skins/ardoise/images/underline_h.svg
+++ b/skins/ardoise/images/underline_h.svg
@@ -0,0 +1 @@
 
--- a/skins/ardoise/images/underline_h.svg
+++ b/skins/ardoise/images/underline_h.svg
@@ -0,0 +1 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M4.172 1.04v2.038q0 .349-.158.611-.155.263-.45.403-.295.14-.697.14-.607 0-.943-.31-.336-.312-.336-.853V1.041h.665v1.927q0 .364.146.534.147.17.485.17.327 0 .474-.17.148-.172.148-.538V1.04z" fill="#ddd"/><path d="M1.323 4.762h3.175" fill="none" stroke="#ddd" stroke-width=".529"/></svg>
--- a/skins/ardoise/images/undo.svg
+++ b/skins/ardoise/images/undo.svg
@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M1.852 2.007c.314-.418.847-.655 1.323-.529s.946.528 1.058 1.058c.113.531-.154 1.237-.529 1.588" fill="none" stroke="#66a8c7" stroke-width=".529" stroke-linecap="square" stroke-linejoin="bevel"/><path d="M1.058 1.19l1.588 1.72H1.058z" fill="#66a8c7"/></svg>
--- a/skins/ardoise/images/undo.svg
+++ b/skins/ardoise/images/undo.svg
@@ -0,0 +1 @@
 
--- a/skins/ardoise/images/undo.svg
+++ b/skins/ardoise/images/undo.svg
@@ -0,0 +1 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><path d="M1.852 2.007c.314-.418.847-.655 1.323-.529s.946.528 1.058 1.058c.113.531-.154 1.237-.529 1.588" fill="none" stroke="#66a8c7" stroke-width=".529" stroke-linecap="square" stroke-linejoin="bevel"/><path d="M1.058 1.19l1.588 1.72H1.058z" fill="#66a8c7"/></svg>
--- a/skins/ardoise/images/undo_h.svg
+++ b/skins/ardoise/images/undo_h.svg
@@ -0,0 +1 @@
1
+<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M1.852 2.006c.314-.417.847-.655 1.323-.529s.946.528 1.058 1.059c.113.53-.154 1.236-.529 1.587" fill="none" stroke="#ff8000" stroke-width=".529" stroke-linecap="square" stroke-linejoin="bevel"/><path d="M1.058 1.19l1.588 1.72H1.058z" fill="#ff8000"/></svg>
--- a/skins/ardoise/images/undo_h.svg
+++ b/skins/ardoise/images/undo_h.svg
@@ -0,0 +1 @@
 
--- a/skins/ardoise/images/undo_h.svg
+++ b/skins/ardoise/images/undo_h.svg
@@ -0,0 +1 @@
1 <svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 5.821 5.821"><rect ry=".265" rx=".265" y="-.001" height="5.821" width="5.821" fill="#555"/><path d="M1.852 2.006c.314-.417.847-.655 1.323-.529s.946.528 1.058 1.059c.113.53-.154 1.236-.529 1.587" fill="none" stroke="#ff8000" stroke-width=".529" stroke-linecap="square" stroke-linejoin="bevel"/><path d="M1.058 1.19l1.588 1.72H1.058z" fill="#ff8000"/></svg>
--- skins/default/css.txt
+++ skins/default/css.txt
@@ -42,11 +42,11 @@
4242
font-size: 1.25em;
4343
}
4444
.content h2 {
4545
font-size: 1.15em;
4646
}
47
-.content h2 {
47
+.content h3 {
4848
font-size: 1.05em;
4949
font-weight: bold;
5050
}
5151
5252
.section {
5353
--- skins/default/css.txt
+++ skins/default/css.txt
@@ -42,11 +42,11 @@
42 font-size: 1.25em;
43 }
44 .content h2 {
45 font-size: 1.15em;
46 }
47 .content h2 {
48 font-size: 1.05em;
49 font-weight: bold;
50 }
51
52 .section {
53
--- skins/default/css.txt
+++ skins/default/css.txt
@@ -42,11 +42,11 @@
42 font-size: 1.25em;
43 }
44 .content h2 {
45 font-size: 1.15em;
46 }
47 .content h3 {
48 font-size: 1.05em;
49 font-weight: bold;
50 }
51
52 .section {
53
+142 -71
--- skins/xekri/css.txt
+++ skins/xekri/css.txt
@@ -707,99 +707,170 @@
707707
708708
/**************************************
709709
* Timeline
710710
*/
711711
712
-div.divider {
713
- color: #ee0;
714
- font-size: 1.2rem;
715
- font-weight: bold;
716
- margin-top: 1rem;
717
- white-space: nowrap;
718
-}
719
-
720712
/* The suppressed duplicates lines in timeline, .. */
721
-span.timelineDisabled {
722
- font-size: 0.5rem;
713
+.timelineDisabled {
714
+ font-size: 0.5rem;
723715
font-style: italic;
724716
}
725717
726
-/* the format for the timeline data table */
727
-table.timelineTable {
728
- border: 0;
729
-}
730
-
731
-/* The row in the timeline table that contains the entry of interest */
732
-tr.timelineSelected {
733
- border: 1px solid #eee;
734
- border-radius: 1rem;
735
-}
736
-
737
-tr.timelineSelected td.timelineTime
738
-, tr.timelineSelected td.timelineTableCell {
739
- background-color: #333;
740
- box-shadow: 2px 2px 1px #000;
741
- padding: 0.5rem;
742
-}
743
-
744
-tr.timelineSelected td.timelineTime {
745
- border-radius: 1rem 0 0 1rem;
746
-}
747
-
748
-tr.timelineSelected td.timelineTableCell {
749
- border-radius: 0 1rem 1rem 0;
750
-}
751
-
752
-.timelineModernCell[id], .timelineColumnarCell[id], .timelineDetailCell[id] {
753
- background-color: #282828;
754
-}
755
-
756
-/* the format for the timeline data cells */
757
-td.timelineTableCell {
758
- padding: 0.3rem;
759
- text-align: left;
718
+/* the format for the timeline version display(no history permission!) */
719
+.timelineHistDsp {
720
+ font-weight: bold;
721
+}
722
+
723
+.content .timelineTable {
724
+ border: 0;
725
+ border-spacing: 0 0.5rem;
726
+}
727
+
728
+.content .timelineTable tr {
729
+ background: #222;
730
+ border: 0;
731
+ padding: 0;
732
+ box-shadow: none;
733
+}
734
+
735
+.timelineTable .timelineDate {
736
+ color: #ee0;
737
+ font-size: 1.2rem;
738
+ font-weight: bold;
739
+ margin-top: 1rem;
740
+ white-space: nowrap;
741
+}
742
+
743
+.timelineTable .timelineTime {
744
+ border-radius: 0;
745
+ border-width: 0;
746
+ padding: 0.25rem 0.5rem 0.5rem 0.5rem;
747
+ white-space: nowrap;
748
+}
749
+
750
+.timelineGraph {
751
+ text-align: left;
760752
vertical-align: top;
753
+ width: 20px;
754
+}
755
+
756
+.timelineTable .timelineModernCell ,
757
+.timelineTable .timelineCompactCell ,
758
+.timelineTable .timelineVerboseCell ,
759
+.timelineTable .timelineDetailCell {
760
+/*
761
+ background: linear-gradient(to bottom, #222 0%, #333 16%, #222 100%);
762
+*/
763
+ border-radius: 0;
764
+ border-width: 0;
765
+ padding: 0.25rem 0.5rem 0.5rem 0.5rem;
766
+}
767
+
768
+.timelineTable .timelineColumnarCell {
769
+/*
770
+ background: linear-gradient(to bottom, #222 0%, #333 16%, #222 100%);
771
+*/
772
+ border-radius: 0;
773
+ border-width: 0;
774
+ padding: 0.25rem 0.5rem 0.5rem 0.5rem;
775
+}
776
+
777
+.timelineTable .timelineModernCell[id] ,
778
+.timelineTable .timelineCompactCell[id] ,
779
+.timelineTable .timelineVerboseCell[id] ,
780
+.timelineTable .timelineColumnarCell[id] ,
781
+.timelineTable .timelineDetailCell[id] {
782
+ background: #272727;
783
+}
784
+
785
+.timelineTable .timelineCurrent .timelineTime {
786
+ background: #333;
787
+ border-radius: 1rem 0 0 1rem;
788
+ border-width: 0;
789
+}
790
+
791
+.timelineTable .timelineCurrent .timelineColumnarCell {
792
+ background: #333;
793
+}
794
+
795
+.timelineTable .timelineCurrent .timelineModernCell ,
796
+.timelineTable .timelineCurrent .timelineCompactCell ,
797
+.timelineTable .timelineCurrent .timelineVerboseCell ,
798
+.timelineTable .timelineCurrent .timelineDetailCell {
799
+ background: #333;
800
+ border-radius: 0 1rem 1rem 0;
801
+}
802
+
803
+.timelineTable .timelineSelected {
804
+ background: #222;
805
+ border: 0;
806
+ box-shadow: none;
807
+}
808
+
809
+.timelineTable .timelineSelected .timelineTime {
810
+ background: #333;
811
+ border-radius: 1rem 0 0 1rem;
812
+ box-shadow: 2px 2px 1px #000;
813
+}
814
+
815
+.timelineTable .timelineSelected .timelineColumnarCell {
816
+ background: #333;
817
+ box-shadow: 2px 2px 1px #000;
761818
}
762819
763
-td.timelineTableCell[style] {
764
- color: #000;
820
+.timelineTable .timelineSelected .timelineModernCell ,
821
+.timelineTable .timelineSelected .timelineCompactCell ,
822
+.timelineTable .timelineSelected .timelineVerboseCell ,
823
+.timelineTable .timelineSelected .timelineDetailCell {
824
+ background: #333;
825
+ border-radius: 0 1rem 1rem 0;
826
+ box-shadow: 2px 2px 1px #000;
765827
}
766828
767
-/* the format for the timeline data cell of the current checkout */
768
-tr.timelineCurrent td.timelineTableCell {
769
- border: 0;
770
- border-radius: 1em 0em;
829
+.timelineTable .timelineModernCell .timelineModernComment ,
830
+.timelineTable .timelineModernCell .timelineModernDetail ,
831
+.timelineTable .timelineCompactCell .timelineCompactComment ,
832
+.timelineTable .timelineCompactCell .timelineCompactDetail ,
833
+.timelineTable .timelineVerboseCell .timelineVerboseComment ,
834
+.timelineTable .timelineVerboseCell .timelineVerboseDetail {
771835
}
772836
773
-/* the format for the timeline leaf marks */
774
-span.timelineLeaf {
837
+.timelineTable .timelineModernCell .timelineLeaf ,
838
+.timelineTable .timelineCompactCell .timelineLeaf ,
839
+.timelineTable .timelineVerboseCell .timelineLeaf ,
840
+.timelineTable .timelineVerboseComment .timelineLeaf {
775841
font-weight: bold;
776842
}
843
+
844
+.timelineTable .timelineModernCell .timelineModernDetail ,
845
+.timelineTable .timelineDetailCell {
846
+ font-size: 85%;
847
+}
848
+
849
+.timelineTable .timelineDetailCell .timelineColumnarDetail {
850
+ white-space: pre-line;
851
+}
852
+
853
+.timelineTable .timelineDetailCell ul.filelist::before {
854
+ content: "files:";
855
+}
856
+
857
+.timelineTable .timelineDetailCell ul.filelist {
858
+ margin-left: 0;
859
+ padding-left: 0;
860
+}
861
+
862
+.timelineTable .timelineDetailCell ul.filelist li {
863
+ margin-left: 1.5rem;
864
+ padding-left: 0;
865
+ white-space: nowrap;
866
+}
777867
778868
/* the format for the timeline version links */
779869
a.timelineHistLink {
780870
}
781871
782
-/* the format for the timeline version display(no history permission!) */
783
-span.timelineHistDsp {
784
- font-weight: bold;
785
-}
786
-
787
-/* the format for the timeline time display */
788
-td.timelineTime {
789
- text-align: right;
790
- vertical-align: top;
791
- white-space: nowrap;
792
-}
793
-
794
-/* the format for the grap placeholder cells in timelines */
795
-td.timelineGraph {
796
- text-align: left;
797
- vertical-align: top;
798
- width: 20px;
799
-}
800
-
801872
802873
/**************************************
803874
* User Edit
804875
*/
805876
806877
--- skins/xekri/css.txt
+++ skins/xekri/css.txt
@@ -707,99 +707,170 @@
707
708 /**************************************
709 * Timeline
710 */
711
712 div.divider {
713 color: #ee0;
714 font-size: 1.2rem;
715 font-weight: bold;
716 margin-top: 1rem;
717 white-space: nowrap;
718 }
719
720 /* The suppressed duplicates lines in timeline, .. */
721 span.timelineDisabled {
722 font-size: 0.5rem;
723 font-style: italic;
724 }
725
726 /* the format for the timeline data table */
727 table.timelineTable {
728 border: 0;
729 }
730
731 /* The row in the timeline table that contains the entry of interest */
732 tr.timelineSelected {
733 border: 1px solid #eee;
734 border-radius: 1rem;
735 }
736
737 tr.timelineSelected td.timelineTime
738 , tr.timelineSelected td.timelineTableCell {
739 background-color: #333;
740 box-shadow: 2px 2px 1px #000;
741 padding: 0.5rem;
742 }
743
744 tr.timelineSelected td.timelineTime {
745 border-radius: 1rem 0 0 1rem;
746 }
747
748 tr.timelineSelected td.timelineTableCell {
749 border-radius: 0 1rem 1rem 0;
750 }
751
752 .timelineModernCell[id], .timelineColumnarCell[id], .timelineDetailCell[id] {
753 background-color: #282828;
754 }
755
756 /* the format for the timeline data cells */
757 td.timelineTableCell {
758 padding: 0.3rem;
759 text-align: left;
760 vertical-align: top;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
761 }
762
763 td.timelineTableCell[style] {
764 color: #000;
 
 
 
 
 
765 }
766
767 /* the format for the timeline data cell of the current checkout */
768 tr.timelineCurrent td.timelineTableCell {
769 border: 0;
770 border-radius: 1em 0em;
 
 
771 }
772
773 /* the format for the timeline leaf marks */
774 span.timelineLeaf {
 
 
775 font-weight: bold;
776 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
777
778 /* the format for the timeline version links */
779 a.timelineHistLink {
780 }
781
782 /* the format for the timeline version display(no history permission!) */
783 span.timelineHistDsp {
784 font-weight: bold;
785 }
786
787 /* the format for the timeline time display */
788 td.timelineTime {
789 text-align: right;
790 vertical-align: top;
791 white-space: nowrap;
792 }
793
794 /* the format for the grap placeholder cells in timelines */
795 td.timelineGraph {
796 text-align: left;
797 vertical-align: top;
798 width: 20px;
799 }
800
801
802 /**************************************
803 * User Edit
804 */
805
806
--- skins/xekri/css.txt
+++ skins/xekri/css.txt
@@ -707,99 +707,170 @@
707
708 /**************************************
709 * Timeline
710 */
711
 
 
 
 
 
 
 
 
712 /* The suppressed duplicates lines in timeline, .. */
713 .timelineDisabled {
714 font-size: 0.5rem;
715 font-style: italic;
716 }
717
718 /* the format for the timeline version display(no history permission!) */
719 .timelineHistDsp {
720 font-weight: bold;
721 }
722
723 .content .timelineTable {
724 border: 0;
725 border-spacing: 0 0.5rem;
726 }
727
728 .content .timelineTable tr {
729 background: #222;
730 border: 0;
731 padding: 0;
732 box-shadow: none;
733 }
734
735 .timelineTable .timelineDate {
736 color: #ee0;
737 font-size: 1.2rem;
738 font-weight: bold;
739 margin-top: 1rem;
740 white-space: nowrap;
741 }
742
743 .timelineTable .timelineTime {
744 border-radius: 0;
745 border-width: 0;
746 padding: 0.25rem 0.5rem 0.5rem 0.5rem;
747 white-space: nowrap;
748 }
749
750 .timelineGraph {
751 text-align: left;
752 vertical-align: top;
753 width: 20px;
754 }
755
756 .timelineTable .timelineModernCell ,
757 .timelineTable .timelineCompactCell ,
758 .timelineTable .timelineVerboseCell ,
759 .timelineTable .timelineDetailCell {
760 /*
761 background: linear-gradient(to bottom, #222 0%, #333 16%, #222 100%);
762 */
763 border-radius: 0;
764 border-width: 0;
765 padding: 0.25rem 0.5rem 0.5rem 0.5rem;
766 }
767
768 .timelineTable .timelineColumnarCell {
769 /*
770 background: linear-gradient(to bottom, #222 0%, #333 16%, #222 100%);
771 */
772 border-radius: 0;
773 border-width: 0;
774 padding: 0.25rem 0.5rem 0.5rem 0.5rem;
775 }
776
777 .timelineTable .timelineModernCell[id] ,
778 .timelineTable .timelineCompactCell[id] ,
779 .timelineTable .timelineVerboseCell[id] ,
780 .timelineTable .timelineColumnarCell[id] ,
781 .timelineTable .timelineDetailCell[id] {
782 background: #272727;
783 }
784
785 .timelineTable .timelineCurrent .timelineTime {
786 background: #333;
787 border-radius: 1rem 0 0 1rem;
788 border-width: 0;
789 }
790
791 .timelineTable .timelineCurrent .timelineColumnarCell {
792 background: #333;
793 }
794
795 .timelineTable .timelineCurrent .timelineModernCell ,
796 .timelineTable .timelineCurrent .timelineCompactCell ,
797 .timelineTable .timelineCurrent .timelineVerboseCell ,
798 .timelineTable .timelineCurrent .timelineDetailCell {
799 background: #333;
800 border-radius: 0 1rem 1rem 0;
801 }
802
803 .timelineTable .timelineSelected {
804 background: #222;
805 border: 0;
806 box-shadow: none;
807 }
808
809 .timelineTable .timelineSelected .timelineTime {
810 background: #333;
811 border-radius: 1rem 0 0 1rem;
812 box-shadow: 2px 2px 1px #000;
813 }
814
815 .timelineTable .timelineSelected .timelineColumnarCell {
816 background: #333;
817 box-shadow: 2px 2px 1px #000;
818 }
819
820 .timelineTable .timelineSelected .timelineModernCell ,
821 .timelineTable .timelineSelected .timelineCompactCell ,
822 .timelineTable .timelineSelected .timelineVerboseCell ,
823 .timelineTable .timelineSelected .timelineDetailCell {
824 background: #333;
825 border-radius: 0 1rem 1rem 0;
826 box-shadow: 2px 2px 1px #000;
827 }
828
829 .timelineTable .timelineModernCell .timelineModernComment ,
830 .timelineTable .timelineModernCell .timelineModernDetail ,
831 .timelineTable .timelineCompactCell .timelineCompactComment ,
832 .timelineTable .timelineCompactCell .timelineCompactDetail ,
833 .timelineTable .timelineVerboseCell .timelineVerboseComment ,
834 .timelineTable .timelineVerboseCell .timelineVerboseDetail {
835 }
836
837 .timelineTable .timelineModernCell .timelineLeaf ,
838 .timelineTable .timelineCompactCell .timelineLeaf ,
839 .timelineTable .timelineVerboseCell .timelineLeaf ,
840 .timelineTable .timelineVerboseComment .timelineLeaf {
841 font-weight: bold;
842 }
843
844 .timelineTable .timelineModernCell .timelineModernDetail ,
845 .timelineTable .timelineDetailCell {
846 font-size: 85%;
847 }
848
849 .timelineTable .timelineDetailCell .timelineColumnarDetail {
850 white-space: pre-line;
851 }
852
853 .timelineTable .timelineDetailCell ul.filelist::before {
854 content: "files:";
855 }
856
857 .timelineTable .timelineDetailCell ul.filelist {
858 margin-left: 0;
859 padding-left: 0;
860 }
861
862 .timelineTable .timelineDetailCell ul.filelist li {
863 margin-left: 1.5rem;
864 padding-left: 0;
865 white-space: nowrap;
866 }
867
868 /* the format for the timeline version links */
869 a.timelineHistLink {
870 }
871
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
872
873 /**************************************
874 * User Edit
875 */
876
877
--- skins/xekri/details.txt
+++ skins/xekri/details.txt
@@ -1,4 +1,4 @@
11
timeline-arrowheads: 1
22
timeline-circle-nodes: 0
3
-timeline-color-graph-lines: 0
3
+timeline-color-graph-lines: 1
44
white-foreground: 0
55
--- skins/xekri/details.txt
+++ skins/xekri/details.txt
@@ -1,4 +1,4 @@
1 timeline-arrowheads: 1
2 timeline-circle-nodes: 0
3 timeline-color-graph-lines: 0
4 white-foreground: 0
5
--- skins/xekri/details.txt
+++ skins/xekri/details.txt
@@ -1,4 +1,4 @@
1 timeline-arrowheads: 1
2 timeline-circle-nodes: 0
3 timeline-color-graph-lines: 1
4 white-foreground: 0
5
+18 -6
--- src/add.c
+++ src/add.c
@@ -253,11 +253,11 @@
253253
**
254254
** When adding files or directories recursively, filenames that begin
255255
** with "." are excluded by default. To include such files, add
256256
** the "--dotfiles" option to the command-line.
257257
**
258
-** The --ignore and --clean options are comma-separate lists of glob patterns
258
+** The --ignore and --clean options are comma-separated lists of glob patterns
259259
** for files to be excluded. Example: '*.o,*.obj,*.exe' If the --ignore
260260
** option does not appear on the command line then the "ignore-glob" setting
261261
** is used. If the --clean option does not appear on the command line then
262262
** the "clean-glob" setting is used.
263263
**
@@ -859,10 +859,12 @@
859859
int vid;
860860
int moveFiles;
861861
int dryRunFlag;
862862
int softFlag;
863863
int hardFlag;
864
+ int origType;
865
+ int destType;
864866
char *zDest;
865867
Blob dest;
866868
Stmt q;
867869
868870
db_must_be_within_tree();
@@ -873,11 +875,11 @@
873875
/* We should be done with options.. */
874876
verify_all_options();
875877
876878
vid = db_lget_int("checkout", 0);
877879
if( vid==0 ){
878
- fossil_fatal("no checkout rename files in");
880
+ fossil_fatal("no checkout in which to rename files");
879881
}
880882
if( g.argc<4 ){
881883
usage("OLDNAME NEWNAME");
882884
}
883885
zDest = g.argv[g.argc-1];
@@ -900,15 +902,23 @@
900902
"UPDATE vfile SET origname=pathname WHERE origname IS NULL;"
901903
);
902904
db_multi_exec(
903905
"CREATE TEMP TABLE mv(f TEXT UNIQUE ON CONFLICT IGNORE, t TEXT);"
904906
);
905
- if( file_isdir(zDest, RepoFILE)!=1 ){
907
+ if( g.argc!=4 ){
908
+ origType = -1;
909
+ }else{
910
+ origType = (file_isdir(g.argv[2], RepoFILE) == 1);
911
+ }
912
+ destType = file_isdir(zDest, RepoFILE);
913
+ if( origType==-1 && destType!=1 ){
914
+ usage("OLDNAME NEWNAME");
915
+ }else if( origType==1 && destType==2 ){
916
+ fossil_fatal("cannot rename '%s' to '%s' since another file named"
917
+ " '%s' exists", g.argv[2], zDest, zDest);
918
+ }else if( origType==0 && destType!=1 ){
906919
Blob orig;
907
- if( g.argc!=4 ){
908
- usage("OLDNAME NEWNAME");
909
- }
910920
file_tree_name(g.argv[2], &orig, 0, 1);
911921
db_multi_exec(
912922
"INSERT INTO mv VALUES(%B,%B)", &orig, &dest
913923
);
914924
}else{
@@ -936,10 +946,12 @@
936946
const char *zPath = db_column_text(&q, 0);
937947
int nPath = db_column_bytes(&q, 0);
938948
const char *zTail;
939949
if( nPath==nOrig ){
940950
zTail = file_tail(zPath);
951
+ }else if( destType==1 ){
952
+ zTail = &zPath[nOrig-strlen(file_tail(zOrig))];
941953
}else{
942954
zTail = &zPath[nOrig+1];
943955
}
944956
db_multi_exec(
945957
"INSERT INTO mv VALUES('%q','%q%q')",
946958
--- src/add.c
+++ src/add.c
@@ -253,11 +253,11 @@
253 **
254 ** When adding files or directories recursively, filenames that begin
255 ** with "." are excluded by default. To include such files, add
256 ** the "--dotfiles" option to the command-line.
257 **
258 ** The --ignore and --clean options are comma-separate lists of glob patterns
259 ** for files to be excluded. Example: '*.o,*.obj,*.exe' If the --ignore
260 ** option does not appear on the command line then the "ignore-glob" setting
261 ** is used. If the --clean option does not appear on the command line then
262 ** the "clean-glob" setting is used.
263 **
@@ -859,10 +859,12 @@
859 int vid;
860 int moveFiles;
861 int dryRunFlag;
862 int softFlag;
863 int hardFlag;
 
 
864 char *zDest;
865 Blob dest;
866 Stmt q;
867
868 db_must_be_within_tree();
@@ -873,11 +875,11 @@
873 /* We should be done with options.. */
874 verify_all_options();
875
876 vid = db_lget_int("checkout", 0);
877 if( vid==0 ){
878 fossil_fatal("no checkout rename files in");
879 }
880 if( g.argc<4 ){
881 usage("OLDNAME NEWNAME");
882 }
883 zDest = g.argv[g.argc-1];
@@ -900,15 +902,23 @@
900 "UPDATE vfile SET origname=pathname WHERE origname IS NULL;"
901 );
902 db_multi_exec(
903 "CREATE TEMP TABLE mv(f TEXT UNIQUE ON CONFLICT IGNORE, t TEXT);"
904 );
905 if( file_isdir(zDest, RepoFILE)!=1 ){
 
 
 
 
 
 
 
 
 
 
 
906 Blob orig;
907 if( g.argc!=4 ){
908 usage("OLDNAME NEWNAME");
909 }
910 file_tree_name(g.argv[2], &orig, 0, 1);
911 db_multi_exec(
912 "INSERT INTO mv VALUES(%B,%B)", &orig, &dest
913 );
914 }else{
@@ -936,10 +946,12 @@
936 const char *zPath = db_column_text(&q, 0);
937 int nPath = db_column_bytes(&q, 0);
938 const char *zTail;
939 if( nPath==nOrig ){
940 zTail = file_tail(zPath);
 
 
941 }else{
942 zTail = &zPath[nOrig+1];
943 }
944 db_multi_exec(
945 "INSERT INTO mv VALUES('%q','%q%q')",
946
--- src/add.c
+++ src/add.c
@@ -253,11 +253,11 @@
253 **
254 ** When adding files or directories recursively, filenames that begin
255 ** with "." are excluded by default. To include such files, add
256 ** the "--dotfiles" option to the command-line.
257 **
258 ** The --ignore and --clean options are comma-separated lists of glob patterns
259 ** for files to be excluded. Example: '*.o,*.obj,*.exe' If the --ignore
260 ** option does not appear on the command line then the "ignore-glob" setting
261 ** is used. If the --clean option does not appear on the command line then
262 ** the "clean-glob" setting is used.
263 **
@@ -859,10 +859,12 @@
859 int vid;
860 int moveFiles;
861 int dryRunFlag;
862 int softFlag;
863 int hardFlag;
864 int origType;
865 int destType;
866 char *zDest;
867 Blob dest;
868 Stmt q;
869
870 db_must_be_within_tree();
@@ -873,11 +875,11 @@
875 /* We should be done with options.. */
876 verify_all_options();
877
878 vid = db_lget_int("checkout", 0);
879 if( vid==0 ){
880 fossil_fatal("no checkout in which to rename files");
881 }
882 if( g.argc<4 ){
883 usage("OLDNAME NEWNAME");
884 }
885 zDest = g.argv[g.argc-1];
@@ -900,15 +902,23 @@
902 "UPDATE vfile SET origname=pathname WHERE origname IS NULL;"
903 );
904 db_multi_exec(
905 "CREATE TEMP TABLE mv(f TEXT UNIQUE ON CONFLICT IGNORE, t TEXT);"
906 );
907 if( g.argc!=4 ){
908 origType = -1;
909 }else{
910 origType = (file_isdir(g.argv[2], RepoFILE) == 1);
911 }
912 destType = file_isdir(zDest, RepoFILE);
913 if( origType==-1 && destType!=1 ){
914 usage("OLDNAME NEWNAME");
915 }else if( origType==1 && destType==2 ){
916 fossil_fatal("cannot rename '%s' to '%s' since another file named"
917 " '%s' exists", g.argv[2], zDest, zDest);
918 }else if( origType==0 && destType!=1 ){
919 Blob orig;
 
 
 
920 file_tree_name(g.argv[2], &orig, 0, 1);
921 db_multi_exec(
922 "INSERT INTO mv VALUES(%B,%B)", &orig, &dest
923 );
924 }else{
@@ -936,10 +946,12 @@
946 const char *zPath = db_column_text(&q, 0);
947 int nPath = db_column_bytes(&q, 0);
948 const char *zTail;
949 if( nPath==nOrig ){
950 zTail = file_tail(zPath);
951 }else if( destType==1 ){
952 zTail = &zPath[nOrig-strlen(file_tail(zOrig))];
953 }else{
954 zTail = &zPath[nOrig+1];
955 }
956 db_multi_exec(
957 "INSERT INTO mv VALUES('%q','%q%q')",
958
+1 -1
--- src/attach.c
+++ src/attach.c
@@ -377,11 +377,11 @@
377377
}
378378
if( zFrom==0 ) zFrom = mprintf("%s/home", g.zTop);
379379
if( P("cancel") ){
380380
cgi_redirect(zFrom);
381381
}
382
- if( P("ok") && szContent>0 && (goodCaptcha = captcha_is_correct()) ){
382
+ if( P("ok") && szContent>0 && (goodCaptcha = captcha_is_correct(0)) ){
383383
int needModerator = (zTkt!=0 && ticket_need_moderation(0)) ||
384384
(zPage!=0 && wiki_need_moderation(0));
385385
const char *zComment = PD("comment", "");
386386
attach_commit(zName, zTarget, aContent, szContent, needModerator, zComment);
387387
cgi_redirect(zFrom);
388388
389389
ADDED src/backoffice.c
--- src/attach.c
+++ src/attach.c
@@ -377,11 +377,11 @@
377 }
378 if( zFrom==0 ) zFrom = mprintf("%s/home", g.zTop);
379 if( P("cancel") ){
380 cgi_redirect(zFrom);
381 }
382 if( P("ok") && szContent>0 && (goodCaptcha = captcha_is_correct()) ){
383 int needModerator = (zTkt!=0 && ticket_need_moderation(0)) ||
384 (zPage!=0 && wiki_need_moderation(0));
385 const char *zComment = PD("comment", "");
386 attach_commit(zName, zTarget, aContent, szContent, needModerator, zComment);
387 cgi_redirect(zFrom);
388
389 DDED src/backoffice.c
--- src/attach.c
+++ src/attach.c
@@ -377,11 +377,11 @@
377 }
378 if( zFrom==0 ) zFrom = mprintf("%s/home", g.zTop);
379 if( P("cancel") ){
380 cgi_redirect(zFrom);
381 }
382 if( P("ok") && szContent>0 && (goodCaptcha = captcha_is_correct(0)) ){
383 int needModerator = (zTkt!=0 && ticket_need_moderation(0)) ||
384 (zPage!=0 && wiki_need_moderation(0));
385 const char *zComment = PD("comment", "");
386 attach_commit(zName, zTarget, aContent, szContent, needModerator, zComment);
387 cgi_redirect(zFrom);
388
389 DDED src/backoffice.c
--- a/src/backoffice.c
+++ b/src/backoffice.c
@@ -0,0 +1,214 @@
1
+i#if 0 /* Disabletimez#if 0 /* Disabletimeout for now */
2
+/*
3
+** Set an alarm to cause the process to exit after "x" seconds. This
4
+** prevents any kind of bug from keeping a running
5
+** indefinitelySigalrmHandler(int x){
6
+ fossil_panic("backoffice timeout (%d seconds)", x);
7
+i.com
8
+** http://www.eeBSD License".)
9
+**
10
+** This ignal thatshould not be run at the conclusiontact information:
11
+** [email protected]
12
+** http://www.hwaemail} backofficeWi#if 0 /* Disabletimeout for now */
13
+/*
14
+** Set an alarm to cause the process to exit after "x" seconds. This
15
+** prevents any kind of b.eeBSD License".)
16
+**
17
+** This eeBSD License".)
18
+*
19
+/*
20
+** WEBPAGE:rpose.
21
+**
22
+** Author contact information:
23
+** [email protected]
24
+** http://www.hwaemail} backofficeWi#if 0 /* Dis Set an alarm to cause teeBSlogin_check_credentials();
25
+ if( !g.perm.Setup ){
26
+ login_needed(0);
27
+ retbackoffbackoffice_do_not_run();
28
+ @ @ <tr><th>Now:</th><td>%lld(tmNow)</td></tr>
29
+ @ <tr><th>Lease:</th><td colspan@ <tr><th>idCurrent:</th><td>%lld(x.idCurrent)</td><td>&nbsp;</td><td>\Exists@ (exists) \
30
+ }Current) ){
31
+ @ (done) \
32
+ }
33
+ @ </td></tr>
34
+ @ <tr><th>tmCurrent:</th><td>%lld(x.tm@ (now%+lld() \
35
+ }
36
+ @ </td></tr>>
37
+# include <windows logcredentials();
38
+ if( !g.perm.Setup ){
39
+ login_needed(0);
40
+ retbackofckoffbackoffice_do_not_run();
41
+ @ @ <tr><th>Now:</th><td>%lld(tmNow)</td></tr>
42
+ @ <tr><th>Lease:</th><td colspan@ <tr><th>idCurrent:</th><td>%lld(x.idCurrent)</td><td>&nbsp;</td><td>\Exists@ (exists) \
43
+ }Current) ){
44
+ {
45
+ @ (done) \
46
+ }
47
+ @ </td></tr>
48
+ @ <tr><th>tmCurrent:</th><td>%lld(x.tm@ (now%+lld() \
49
+ }
50
+ @ </td></tr>
51
+ @ <tr><th>i#if ! (now%+lld() \
52
+ /tr>
53
+ }
54
+#endif
55
+
56
+ @1; \
57
+ }
58
+ @ </td></tr>
59
+#ifndef ");w.hwaemail} backofficeWi! (now%+lld() \
60
+ }d>%lld(tmNow)</td></tr>
61
+ @ <tr><th>Lease:</th><td colspan@ <tr><th>idCurrent:</th><td>%lld(x.idCurrent)</td><td>&nbsp;</td><td>\Exists@ (exists) \
62
+ }Current) ){
63
+ @ (done) \
64
+ }
65
+ @ </td></tr>
66
+ @ <tr><th>tmCurrent:</th><td>%lld(x.tm@ (now%+lld() \
67
+ }
68
+ @ </td></tr>
69
+ @ <tr>ut for now */
70
+/*
71
+** Set an alarm to cause the process to exit after "x" seconds. This
72
+** prevents any kind of bug from keeping a running
73
+** indefinitelySigalrmHandler(int x){
74
+ fossil_panic("backoffice timeout (%d seconds)", x);
75
+i.com
76
+** http://www.eeBSD License".)
77
+**
78
+** This ignal thatshould not be run at the conclusiontact information:
79
+** [email protected]
80
+** http://www.hwaemail} backofficeWi#if 0 /* Disabletimeout for now */
81
+/*
82
+** Set an alarm to cause the process to exit after "x" seconds. This
83
+** prevents any kind of b.eeBSD License".)
84
+**
85
+** This eeBSD License".)
86
+*
87
+/*
88
+** WEBPAGE:rpose.
89
+**
90
+** Author contact information:
91
+** [email protected]
92
+** http://www.hwaemail} backofficeWi#if 0 /* Dis Set an alarm to cause teeBSlogin_check_credentials();
93
+ if( !g.perm.Setup ){
94
+ login_needed(0);
95
+ retbackoffbackoffice_do_not_run();
96
+ @ @ <tr><th>Now:</th><td>%lld(tmNow)</td></tr>
97
+ @ <tr><th>Lease:</th><td colspan@ <tr><th>idCurrent:</th><td>%lld(x.idCurrent)</td><td>&nbsp;</td><td>\Exists@ (exists) \
98
+ }Current) ){
99
+ @ (done) \
100
+ }
101
+ @ </td></tr>
102
+ @ <tr><th>tmCurrent:</th><td>%lld(x.tm@ (now%+lld() \
103
+ }
104
+ @ </td></tr>>
105
+# include <windows logcredentials();
106
+ if( !g.perm.Setup ){
107
+ login_needed(0);
108
+ retbackofckoffbackoffice_do_not_run();
109
+ @ @ <tr><th>Now:</th><td>%lld(tmNow)</td></tr>
110
+ @ <tr><th>Lease:</th><td colspan@ <tr><th>idCurrent:</th><td>%lld(x.idCurrent)</td><td>&nbsp;</td><td>\Exists@ (exists) \
111
+ }Current) ){
112
+ {
113
+ @ (done) \
114
+ }
115
+ @ </td></tr>
116
+ @ <tr><th>tmCurrent:</th><td>%lld(x.tm@ (now%+lld() \
117
+ }
118
+ @ </td></tr>
119
+ @ <tr><th>i#if ! (now%+lld() \
120
+ /tr>
121
+ }
122
+#endif
123
+
124
+ @1; \
125
+ }
126
+ @ </td></tr>
127
+#ifndef ");w.hwaemail} backofficeWi! (now%+lld() \
128
+ }d>%lld(tmNow)</td></tr>
129
+ @ <tr><th>Lease:</th><td colspan@ <tr><th>idCurrent:</th><td>%lld(x.idCurrent)</td><td>&nbsp;</td><td>\Exists@ (exists) \
130
+ }Current) ){
131
+ @ (done) \
132
+ }
133
+ @ </td></tr>
134
+ @ <tr><th>tmCurrent:</th><td>%lld(x.tm@ (now%+lld() \
135
+ }
136
+ @ </td></tr>
137
+ @ <tr><th>idNext:</th><td>%lld(x.idNext)</td><td>&nbsp;</td><td>\{
138
+ @ (exists) \
139
+ }{
140
+ @ (done) \
141
+ }
142
+ @ </td></tr>
143
+ @ <tr><th>tmNext:</th><td>%lld(x.tmNext)</td><td></td><td>\
144
+ if( x.tmNext>0 ){
145
+ @ (now%+lld(x.tmNext-tmNow)) \
146
+ }
147
+ @ </td></tr>
148
+#ifndef _WIN32
149
+ if( P("fds")!=0 ){
150
+ int i;
151
+ @ <tr><th>open fds:</th><td colspan="3">
152
+ for(i=0; i<1000; i++){
153
+ int rc = fcntl(i, F_GETFL, 0);
154
+ if( rc<0 ) continue;
155
+ @ %d(i) \
156
+ }
157
+ @ </td></tr>
158
+ }
159
+#endif
160
+
161
+ @1;
162
+ if( 3; close(i)==0 || i<10; i++){0/*
163
+** IfdbdD License".)
164
+**
165
+** This progcense"D License".)
166
+**
167
+** This program is distributed in the hope that it will be useful,
168
+** but without any warranty; without even the implied warranty of
169
+** merchantability or fitness for a particular purpose.
170
+**
171
+** Author contact information:
172
+** [email protected]
173
+** http://www.hwaemail} backofficeWi#if 0 /* Disabletimeout for now */
174
+/*
175
+** Set an alarm to cause the process to exit after "x" seconds. This
176
+** prevents any kind of bug from keeping a running
177
+** indefinitelySigalrmHandler(int x){
178
+ fossil_panic("backoffice timeout (%d seconds)", x);
179
+i.com
180
+** http://www.eeBSD License".)
181
+**
182
+** This ignal thatshould not be run at thidSelf =#if 0 /* Disabletimeout for now */
183
+/*
184
+** Set an alarm to cause the process to exit after "x" seconds. This
185
+** prevents any kind of bug from keeping a running
186
+** indefinitelySigalrmHandler(int x){
187
+ fossil_panic("backoffice timeout (%d seconds)", x);
188
+i.com
189
+** http://www.eeBSD License".)
190
+**
191
+** This ignal thatshould not be run at the conclusiontact information:
192
+** [email protected]
193
+** http://www.hwaemail} backofficeWi#if 0 /* Disabletimeout for now */
194
+/*
195
+** Set an alarm to cause the process to exit after "x" seconds. This
196
+** prevents any kind of b.eeBSD License".)
197
+**
198
+** This eeBSD License".)
199
+*x.tmCurrent:rpose.
200
+**
201
+** Author contact information:
202
+** [email protected]
203
+** http://www.hwaemail} backofficeWi#if 0 /* Dis Set an alarm to cause teeBSlogin_check_credentials();
204
+ if( !g.perm.Setup ){
205
+ login_needed(0);
206
+ retbackoffbackoffice_do_not_run();
207
+ @ @ <tr><th>Now:</th><td>%lld(tmNow)</td></tr>
208
+ @ <tr><th>Lease:</th><td colspan@ <tr><th>idCurrent:</th><td>%lld(x.idCurrent)</td><td>&nbsp;</td><td>\Exists@ (exists) \
209
+ }Current)
210
+/*
211
+** Set an alarm to cause the process to exit after "x" seconds. This
212
+** prevents any kind of bug from keeping a running
213
+** indefinitelySigalrmHandler(int x){
214
+ fossil_panic("backoffice timeout (%d sec
--- a/src/backoffice.c
+++ b/src/backoffice.c
@@ -0,0 +1,214 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/src/backoffice.c
+++ b/src/backoffice.c
@@ -0,0 +1,214 @@
1 i#if 0 /* Disabletimez#if 0 /* Disabletimeout for now */
2 /*
3 ** Set an alarm to cause the process to exit after "x" seconds. This
4 ** prevents any kind of bug from keeping a running
5 ** indefinitelySigalrmHandler(int x){
6 fossil_panic("backoffice timeout (%d seconds)", x);
7 i.com
8 ** http://www.eeBSD License".)
9 **
10 ** This ignal thatshould not be run at the conclusiontact information:
11 ** [email protected]
12 ** http://www.hwaemail} backofficeWi#if 0 /* Disabletimeout for now */
13 /*
14 ** Set an alarm to cause the process to exit after "x" seconds. This
15 ** prevents any kind of b.eeBSD License".)
16 **
17 ** This eeBSD License".)
18 *
19 /*
20 ** WEBPAGE:rpose.
21 **
22 ** Author contact information:
23 ** [email protected]
24 ** http://www.hwaemail} backofficeWi#if 0 /* Dis Set an alarm to cause teeBSlogin_check_credentials();
25 if( !g.perm.Setup ){
26 login_needed(0);
27 retbackoffbackoffice_do_not_run();
28 @ @ <tr><th>Now:</th><td>%lld(tmNow)</td></tr>
29 @ <tr><th>Lease:</th><td colspan@ <tr><th>idCurrent:</th><td>%lld(x.idCurrent)</td><td>&nbsp;</td><td>\Exists@ (exists) \
30 }Current) ){
31 @ (done) \
32 }
33 @ </td></tr>
34 @ <tr><th>tmCurrent:</th><td>%lld(x.tm@ (now%+lld() \
35 }
36 @ </td></tr>>
37 # include <windows logcredentials();
38 if( !g.perm.Setup ){
39 login_needed(0);
40 retbackofckoffbackoffice_do_not_run();
41 @ @ <tr><th>Now:</th><td>%lld(tmNow)</td></tr>
42 @ <tr><th>Lease:</th><td colspan@ <tr><th>idCurrent:</th><td>%lld(x.idCurrent)</td><td>&nbsp;</td><td>\Exists@ (exists) \
43 }Current) ){
44 {
45 @ (done) \
46 }
47 @ </td></tr>
48 @ <tr><th>tmCurrent:</th><td>%lld(x.tm@ (now%+lld() \
49 }
50 @ </td></tr>
51 @ <tr><th>i#if ! (now%+lld() \
52 /tr>
53 }
54 #endif
55
56 @1; \
57 }
58 @ </td></tr>
59 #ifndef ");w.hwaemail} backofficeWi! (now%+lld() \
60 }d>%lld(tmNow)</td></tr>
61 @ <tr><th>Lease:</th><td colspan@ <tr><th>idCurrent:</th><td>%lld(x.idCurrent)</td><td>&nbsp;</td><td>\Exists@ (exists) \
62 }Current) ){
63 @ (done) \
64 }
65 @ </td></tr>
66 @ <tr><th>tmCurrent:</th><td>%lld(x.tm@ (now%+lld() \
67 }
68 @ </td></tr>
69 @ <tr>ut for now */
70 /*
71 ** Set an alarm to cause the process to exit after "x" seconds. This
72 ** prevents any kind of bug from keeping a running
73 ** indefinitelySigalrmHandler(int x){
74 fossil_panic("backoffice timeout (%d seconds)", x);
75 i.com
76 ** http://www.eeBSD License".)
77 **
78 ** This ignal thatshould not be run at the conclusiontact information:
79 ** [email protected]
80 ** http://www.hwaemail} backofficeWi#if 0 /* Disabletimeout for now */
81 /*
82 ** Set an alarm to cause the process to exit after "x" seconds. This
83 ** prevents any kind of b.eeBSD License".)
84 **
85 ** This eeBSD License".)
86 *
87 /*
88 ** WEBPAGE:rpose.
89 **
90 ** Author contact information:
91 ** [email protected]
92 ** http://www.hwaemail} backofficeWi#if 0 /* Dis Set an alarm to cause teeBSlogin_check_credentials();
93 if( !g.perm.Setup ){
94 login_needed(0);
95 retbackoffbackoffice_do_not_run();
96 @ @ <tr><th>Now:</th><td>%lld(tmNow)</td></tr>
97 @ <tr><th>Lease:</th><td colspan@ <tr><th>idCurrent:</th><td>%lld(x.idCurrent)</td><td>&nbsp;</td><td>\Exists@ (exists) \
98 }Current) ){
99 @ (done) \
100 }
101 @ </td></tr>
102 @ <tr><th>tmCurrent:</th><td>%lld(x.tm@ (now%+lld() \
103 }
104 @ </td></tr>>
105 # include <windows logcredentials();
106 if( !g.perm.Setup ){
107 login_needed(0);
108 retbackofckoffbackoffice_do_not_run();
109 @ @ <tr><th>Now:</th><td>%lld(tmNow)</td></tr>
110 @ <tr><th>Lease:</th><td colspan@ <tr><th>idCurrent:</th><td>%lld(x.idCurrent)</td><td>&nbsp;</td><td>\Exists@ (exists) \
111 }Current) ){
112 {
113 @ (done) \
114 }
115 @ </td></tr>
116 @ <tr><th>tmCurrent:</th><td>%lld(x.tm@ (now%+lld() \
117 }
118 @ </td></tr>
119 @ <tr><th>i#if ! (now%+lld() \
120 /tr>
121 }
122 #endif
123
124 @1; \
125 }
126 @ </td></tr>
127 #ifndef ");w.hwaemail} backofficeWi! (now%+lld() \
128 }d>%lld(tmNow)</td></tr>
129 @ <tr><th>Lease:</th><td colspan@ <tr><th>idCurrent:</th><td>%lld(x.idCurrent)</td><td>&nbsp;</td><td>\Exists@ (exists) \
130 }Current) ){
131 @ (done) \
132 }
133 @ </td></tr>
134 @ <tr><th>tmCurrent:</th><td>%lld(x.tm@ (now%+lld() \
135 }
136 @ </td></tr>
137 @ <tr><th>idNext:</th><td>%lld(x.idNext)</td><td>&nbsp;</td><td>\{
138 @ (exists) \
139 }{
140 @ (done) \
141 }
142 @ </td></tr>
143 @ <tr><th>tmNext:</th><td>%lld(x.tmNext)</td><td></td><td>\
144 if( x.tmNext>0 ){
145 @ (now%+lld(x.tmNext-tmNow)) \
146 }
147 @ </td></tr>
148 #ifndef _WIN32
149 if( P("fds")!=0 ){
150 int i;
151 @ <tr><th>open fds:</th><td colspan="3">
152 for(i=0; i<1000; i++){
153 int rc = fcntl(i, F_GETFL, 0);
154 if( rc<0 ) continue;
155 @ %d(i) \
156 }
157 @ </td></tr>
158 }
159 #endif
160
161 @1;
162 if( 3; close(i)==0 || i<10; i++){0/*
163 ** IfdbdD License".)
164 **
165 ** This progcense"D License".)
166 **
167 ** This program is distributed in the hope that it will be useful,
168 ** but without any warranty; without even the implied warranty of
169 ** merchantability or fitness for a particular purpose.
170 **
171 ** Author contact information:
172 ** [email protected]
173 ** http://www.hwaemail} backofficeWi#if 0 /* Disabletimeout for now */
174 /*
175 ** Set an alarm to cause the process to exit after "x" seconds. This
176 ** prevents any kind of bug from keeping a running
177 ** indefinitelySigalrmHandler(int x){
178 fossil_panic("backoffice timeout (%d seconds)", x);
179 i.com
180 ** http://www.eeBSD License".)
181 **
182 ** This ignal thatshould not be run at thidSelf =#if 0 /* Disabletimeout for now */
183 /*
184 ** Set an alarm to cause the process to exit after "x" seconds. This
185 ** prevents any kind of bug from keeping a running
186 ** indefinitelySigalrmHandler(int x){
187 fossil_panic("backoffice timeout (%d seconds)", x);
188 i.com
189 ** http://www.eeBSD License".)
190 **
191 ** This ignal thatshould not be run at the conclusiontact information:
192 ** [email protected]
193 ** http://www.hwaemail} backofficeWi#if 0 /* Disabletimeout for now */
194 /*
195 ** Set an alarm to cause the process to exit after "x" seconds. This
196 ** prevents any kind of b.eeBSD License".)
197 **
198 ** This eeBSD License".)
199 *x.tmCurrent:rpose.
200 **
201 ** Author contact information:
202 ** [email protected]
203 ** http://www.hwaemail} backofficeWi#if 0 /* Dis Set an alarm to cause teeBSlogin_check_credentials();
204 if( !g.perm.Setup ){
205 login_needed(0);
206 retbackoffbackoffice_do_not_run();
207 @ @ <tr><th>Now:</th><td>%lld(tmNow)</td></tr>
208 @ <tr><th>Lease:</th><td colspan@ <tr><th>idCurrent:</th><td>%lld(x.idCurrent)</td><td>&nbsp;</td><td>\Exists@ (exists) \
209 }Current)
210 /*
211 ** Set an alarm to cause the process to exit after "x" seconds. This
212 ** prevents any kind of bug from keeping a running
213 ** indefinitelySigalrmHandler(int x){
214 fossil_panic("backoffice timeout (%d sec
+44 -6
--- src/blob.c
+++ src/blob.c
@@ -115,10 +115,18 @@
115115
}
116116
int fossil_isalnum(char c){
117117
return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9');
118118
}
119119
120
+/* Return true if and only if the entire string consists of only
121
+** alphanumeric characters.
122
+*/
123
+int fossil_no_strange_characters(const char *z){
124
+ while( z && (fossil_isalnum(z[0]) || z[0]=='_' || z[0]=='-') ) z++;
125
+ return z[0]==0;
126
+}
127
+
120128
121129
/*
122130
** COMMAND: test-isspace
123131
**
124132
** Verify that the fossil_isspace() routine is working correctly by
@@ -285,10 +293,23 @@
285293
}
286294
memcpy(&pBlob->aData[pBlob->nUsed], aData, nData);
287295
pBlob->nUsed += nData;
288296
pBlob->aData[pBlob->nUsed] = 0; /* Blobs are always nul-terminated */
289297
}
298
+
299
+/*
300
+** Append a single character to the blob
301
+*/
302
+void blob_append_char(Blob *pBlob, char c){
303
+ if( pBlob->nUsed+1 >= pBlob->nAlloc ){
304
+ pBlob->xRealloc(pBlob, pBlob->nUsed + pBlob->nAlloc + 100);
305
+ if( pBlob->nUsed + 1 >= pBlob->nAlloc ){
306
+ blob_panic();
307
+ }
308
+ }
309
+ pBlob->aData[pBlob->nUsed++] = c;
310
+}
290311
291312
/*
292313
** Copy a blob
293314
*/
294315
void blob_copy(Blob *pTo, Blob *pFrom){
@@ -301,11 +322,11 @@
301322
** Return a pointer to a null-terminated string for a blob.
302323
*/
303324
char *blob_str(Blob *p){
304325
blob_is_init(p);
305326
if( p->nUsed==0 ){
306
- blob_append(p, "", 1); /* NOTE: Changes nUsed. */
327
+ blob_append_char(p, 0); /* NOTE: Changes nUsed. */
307328
p->nUsed = 0;
308329
}
309330
if( p->aData[p->nUsed]!=0 ){
310331
blob_materialize(p);
311332
}
@@ -318,11 +339,11 @@
318339
** text was ever added using blob_appendf() then throw an error.
319340
*/
320341
char *blob_sql_text(Blob *p){
321342
blob_is_init(p);
322343
if( (p->blobFlags & BLOBFLAG_NotSQL) ){
323
- fossil_fatal("Internal error: Use of blob_appendf() to construct SQL text");
344
+ fossil_panic("use of blob_appendf() to construct SQL text");
324345
}
325346
return blob_str(p);
326347
}
327348
328349
@@ -472,10 +493,17 @@
472493
** Rewind the cursor on a blob back to the beginning.
473494
*/
474495
void blob_rewind(Blob *p){
475496
p->iCursor = 0;
476497
}
498
+
499
+/*
500
+** Truncate a blob back to zero length
501
+*/
502
+void blob_truncate(Blob *p, int sz){
503
+ if( sz>=0 && sz<p->nUsed ) p->nUsed = sz;
504
+}
477505
478506
/*
479507
** Seek the cursor in a blob to the indicated offset.
480508
*/
481509
int blob_seek(Blob *p, int offset, int whence){
@@ -645,10 +673,20 @@
645673
if( pTo ){
646674
blob_append(pTo, &pFrom->aData[pFrom->iCursor], i - pFrom->iCursor);
647675
}
648676
pFrom->iCursor = i;
649677
}
678
+
679
+/*
680
+** Ensure that the text in pBlob ends with '\n'
681
+*/
682
+void blob_add_final_newline(Blob *pBlob){
683
+ if( pBlob->nUsed<=0 ) return;
684
+ if( pBlob->aData[pBlob->nUsed-1]!='\n' ){
685
+ blob_append_char(pBlob, '\n');
686
+ }
687
+}
650688
651689
/*
652690
** Return true if the blob contains a valid base16 identifier artifact hash.
653691
**
654692
** The value returned is actually one of HNAME_SHA1 OR HNAME_K256 if the
@@ -1229,16 +1267,16 @@
12291267
if( !needEscape && !fossil_isalnum(c) && c!='/' && c!='.' && c!='_' ){
12301268
needEscape = 1;
12311269
}
12321270
}
12331271
if( n>0 && !fossil_isspace(z[n-1]) ){
1234
- blob_append(pBlob, " ", 1);
1272
+ blob_append_char(pBlob, ' ');
12351273
}
1236
- if( needEscape ) blob_append(pBlob, &cQuote, 1);
1274
+ if( needEscape ) blob_append_char(pBlob, cQuote);
12371275
if( zIn[0]=='-' ) blob_append(pBlob, "./", 2);
12381276
blob_append(pBlob, zIn, -1);
1239
- if( needEscape ) blob_append(pBlob, &cQuote, 1);
1277
+ if( needEscape ) blob_append_char(pBlob, cQuote);
12401278
}
12411279
12421280
/*
12431281
** A read(2)-like impl for the Blob class. Reads (copies) up to nLen
12441282
** bytes from pIn, starting at position pIn->iCursor, and copies them
@@ -1305,11 +1343,11 @@
13051343
zUtf8[i] = zUtf8[i-1];
13061344
zUtf8[--i] = zTemp;
13071345
}
13081346
}
13091347
/* Make sure the blob contains two terminating 0-bytes */
1310
- blob_append(pBlob, "", 1);
1348
+ blob_append_char(pBlob, 0);
13111349
zUtf8 = blob_str(pBlob) + bomSize;
13121350
zUtf8 = fossil_unicode_to_utf8(zUtf8);
13131351
blob_set_dynamic(pBlob, zUtf8);
13141352
}else if( useMbcs && invalid_utf8(pBlob) ){
13151353
#if defined(_WIN32) || defined(__CYGWIN__)
13161354
--- src/blob.c
+++ src/blob.c
@@ -115,10 +115,18 @@
115 }
116 int fossil_isalnum(char c){
117 return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9');
118 }
119
 
 
 
 
 
 
 
 
120
121 /*
122 ** COMMAND: test-isspace
123 **
124 ** Verify that the fossil_isspace() routine is working correctly by
@@ -285,10 +293,23 @@
285 }
286 memcpy(&pBlob->aData[pBlob->nUsed], aData, nData);
287 pBlob->nUsed += nData;
288 pBlob->aData[pBlob->nUsed] = 0; /* Blobs are always nul-terminated */
289 }
 
 
 
 
 
 
 
 
 
 
 
 
 
290
291 /*
292 ** Copy a blob
293 */
294 void blob_copy(Blob *pTo, Blob *pFrom){
@@ -301,11 +322,11 @@
301 ** Return a pointer to a null-terminated string for a blob.
302 */
303 char *blob_str(Blob *p){
304 blob_is_init(p);
305 if( p->nUsed==0 ){
306 blob_append(p, "", 1); /* NOTE: Changes nUsed. */
307 p->nUsed = 0;
308 }
309 if( p->aData[p->nUsed]!=0 ){
310 blob_materialize(p);
311 }
@@ -318,11 +339,11 @@
318 ** text was ever added using blob_appendf() then throw an error.
319 */
320 char *blob_sql_text(Blob *p){
321 blob_is_init(p);
322 if( (p->blobFlags & BLOBFLAG_NotSQL) ){
323 fossil_fatal("Internal error: Use of blob_appendf() to construct SQL text");
324 }
325 return blob_str(p);
326 }
327
328
@@ -472,10 +493,17 @@
472 ** Rewind the cursor on a blob back to the beginning.
473 */
474 void blob_rewind(Blob *p){
475 p->iCursor = 0;
476 }
 
 
 
 
 
 
 
477
478 /*
479 ** Seek the cursor in a blob to the indicated offset.
480 */
481 int blob_seek(Blob *p, int offset, int whence){
@@ -645,10 +673,20 @@
645 if( pTo ){
646 blob_append(pTo, &pFrom->aData[pFrom->iCursor], i - pFrom->iCursor);
647 }
648 pFrom->iCursor = i;
649 }
 
 
 
 
 
 
 
 
 
 
650
651 /*
652 ** Return true if the blob contains a valid base16 identifier artifact hash.
653 **
654 ** The value returned is actually one of HNAME_SHA1 OR HNAME_K256 if the
@@ -1229,16 +1267,16 @@
1229 if( !needEscape && !fossil_isalnum(c) && c!='/' && c!='.' && c!='_' ){
1230 needEscape = 1;
1231 }
1232 }
1233 if( n>0 && !fossil_isspace(z[n-1]) ){
1234 blob_append(pBlob, " ", 1);
1235 }
1236 if( needEscape ) blob_append(pBlob, &cQuote, 1);
1237 if( zIn[0]=='-' ) blob_append(pBlob, "./", 2);
1238 blob_append(pBlob, zIn, -1);
1239 if( needEscape ) blob_append(pBlob, &cQuote, 1);
1240 }
1241
1242 /*
1243 ** A read(2)-like impl for the Blob class. Reads (copies) up to nLen
1244 ** bytes from pIn, starting at position pIn->iCursor, and copies them
@@ -1305,11 +1343,11 @@
1305 zUtf8[i] = zUtf8[i-1];
1306 zUtf8[--i] = zTemp;
1307 }
1308 }
1309 /* Make sure the blob contains two terminating 0-bytes */
1310 blob_append(pBlob, "", 1);
1311 zUtf8 = blob_str(pBlob) + bomSize;
1312 zUtf8 = fossil_unicode_to_utf8(zUtf8);
1313 blob_set_dynamic(pBlob, zUtf8);
1314 }else if( useMbcs && invalid_utf8(pBlob) ){
1315 #if defined(_WIN32) || defined(__CYGWIN__)
1316
--- src/blob.c
+++ src/blob.c
@@ -115,10 +115,18 @@
115 }
116 int fossil_isalnum(char c){
117 return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9');
118 }
119
120 /* Return true if and only if the entire string consists of only
121 ** alphanumeric characters.
122 */
123 int fossil_no_strange_characters(const char *z){
124 while( z && (fossil_isalnum(z[0]) || z[0]=='_' || z[0]=='-') ) z++;
125 return z[0]==0;
126 }
127
128
129 /*
130 ** COMMAND: test-isspace
131 **
132 ** Verify that the fossil_isspace() routine is working correctly by
@@ -285,10 +293,23 @@
293 }
294 memcpy(&pBlob->aData[pBlob->nUsed], aData, nData);
295 pBlob->nUsed += nData;
296 pBlob->aData[pBlob->nUsed] = 0; /* Blobs are always nul-terminated */
297 }
298
299 /*
300 ** Append a single character to the blob
301 */
302 void blob_append_char(Blob *pBlob, char c){
303 if( pBlob->nUsed+1 >= pBlob->nAlloc ){
304 pBlob->xRealloc(pBlob, pBlob->nUsed + pBlob->nAlloc + 100);
305 if( pBlob->nUsed + 1 >= pBlob->nAlloc ){
306 blob_panic();
307 }
308 }
309 pBlob->aData[pBlob->nUsed++] = c;
310 }
311
312 /*
313 ** Copy a blob
314 */
315 void blob_copy(Blob *pTo, Blob *pFrom){
@@ -301,11 +322,11 @@
322 ** Return a pointer to a null-terminated string for a blob.
323 */
324 char *blob_str(Blob *p){
325 blob_is_init(p);
326 if( p->nUsed==0 ){
327 blob_append_char(p, 0); /* NOTE: Changes nUsed. */
328 p->nUsed = 0;
329 }
330 if( p->aData[p->nUsed]!=0 ){
331 blob_materialize(p);
332 }
@@ -318,11 +339,11 @@
339 ** text was ever added using blob_appendf() then throw an error.
340 */
341 char *blob_sql_text(Blob *p){
342 blob_is_init(p);
343 if( (p->blobFlags & BLOBFLAG_NotSQL) ){
344 fossil_panic("use of blob_appendf() to construct SQL text");
345 }
346 return blob_str(p);
347 }
348
349
@@ -472,10 +493,17 @@
493 ** Rewind the cursor on a blob back to the beginning.
494 */
495 void blob_rewind(Blob *p){
496 p->iCursor = 0;
497 }
498
499 /*
500 ** Truncate a blob back to zero length
501 */
502 void blob_truncate(Blob *p, int sz){
503 if( sz>=0 && sz<p->nUsed ) p->nUsed = sz;
504 }
505
506 /*
507 ** Seek the cursor in a blob to the indicated offset.
508 */
509 int blob_seek(Blob *p, int offset, int whence){
@@ -645,10 +673,20 @@
673 if( pTo ){
674 blob_append(pTo, &pFrom->aData[pFrom->iCursor], i - pFrom->iCursor);
675 }
676 pFrom->iCursor = i;
677 }
678
679 /*
680 ** Ensure that the text in pBlob ends with '\n'
681 */
682 void blob_add_final_newline(Blob *pBlob){
683 if( pBlob->nUsed<=0 ) return;
684 if( pBlob->aData[pBlob->nUsed-1]!='\n' ){
685 blob_append_char(pBlob, '\n');
686 }
687 }
688
689 /*
690 ** Return true if the blob contains a valid base16 identifier artifact hash.
691 **
692 ** The value returned is actually one of HNAME_SHA1 OR HNAME_K256 if the
@@ -1229,16 +1267,16 @@
1267 if( !needEscape && !fossil_isalnum(c) && c!='/' && c!='.' && c!='_' ){
1268 needEscape = 1;
1269 }
1270 }
1271 if( n>0 && !fossil_isspace(z[n-1]) ){
1272 blob_append_char(pBlob, ' ');
1273 }
1274 if( needEscape ) blob_append_char(pBlob, cQuote);
1275 if( zIn[0]=='-' ) blob_append(pBlob, "./", 2);
1276 blob_append(pBlob, zIn, -1);
1277 if( needEscape ) blob_append_char(pBlob, cQuote);
1278 }
1279
1280 /*
1281 ** A read(2)-like impl for the Blob class. Reads (copies) up to nLen
1282 ** bytes from pIn, starting at position pIn->iCursor, and copies them
@@ -1305,11 +1343,11 @@
1343 zUtf8[i] = zUtf8[i-1];
1344 zUtf8[--i] = zTemp;
1345 }
1346 }
1347 /* Make sure the blob contains two terminating 0-bytes */
1348 blob_append_char(pBlob, 0);
1349 zUtf8 = blob_str(pBlob) + bomSize;
1350 zUtf8 = fossil_unicode_to_utf8(zUtf8);
1351 blob_set_dynamic(pBlob, zUtf8);
1352 }else if( useMbcs && invalid_utf8(pBlob) ){
1353 #if defined(_WIN32) || defined(__CYGWIN__)
1354
+5 -1
--- src/browse.c
+++ src/browse.c
@@ -197,11 +197,15 @@
197197
if( nD==0 ){
198198
style_submenu_element("File Ages", "%R/fileage?name=%!S", zUuid);
199199
}
200200
}else{
201201
@ <h2>The union of all files from all check-ins
202
- @ %s(blob_str(&dirname))</h2>
202
+ @ %s(blob_str(&dirname))
203
+ if( zD ){
204
+ @ &nbsp;&nbsp;%z(href("%R/timeline?chng=%T/*", zD))[history]</a>
205
+ }
206
+ @ </h2>
203207
zSubdirLink = mprintf("%R/dir?name=%T", zPrefix);
204208
}
205209
style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0));
206210
style_submenu_element("Tree-View", "%s",
207211
url_render(&sURI, "type", "tree", 0, 0));
208212
--- src/browse.c
+++ src/browse.c
@@ -197,11 +197,15 @@
197 if( nD==0 ){
198 style_submenu_element("File Ages", "%R/fileage?name=%!S", zUuid);
199 }
200 }else{
201 @ <h2>The union of all files from all check-ins
202 @ %s(blob_str(&dirname))</h2>
 
 
 
 
203 zSubdirLink = mprintf("%R/dir?name=%T", zPrefix);
204 }
205 style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0));
206 style_submenu_element("Tree-View", "%s",
207 url_render(&sURI, "type", "tree", 0, 0));
208
--- src/browse.c
+++ src/browse.c
@@ -197,11 +197,15 @@
197 if( nD==0 ){
198 style_submenu_element("File Ages", "%R/fileage?name=%!S", zUuid);
199 }
200 }else{
201 @ <h2>The union of all files from all check-ins
202 @ %s(blob_str(&dirname))
203 if( zD ){
204 @ &nbsp;&nbsp;%z(href("%R/timeline?chng=%T/*", zD))[history]</a>
205 }
206 @ </h2>
207 zSubdirLink = mprintf("%R/dir?name=%T", zPrefix);
208 }
209 style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0));
210 style_submenu_element("Tree-View", "%s",
211 url_render(&sURI, "type", "tree", 0, 0));
212
+3 -3
--- src/bundle.c
+++ src/bundle.c
@@ -60,11 +60,11 @@
6060
if( !doInit && file_size(zFile, ExtFILE)<0 ){
6161
fossil_fatal("no such file: %s", zFile);
6262
}
6363
assert( g.db );
6464
zSql = sqlite3_mprintf("ATTACH %Q AS %Q", zFile, zBName);
65
- if( zSql==0 ) fossil_fatal("out of memory");
65
+ if( zSql==0 ) fossil_panic("out of memory");
6666
rc = sqlite3_exec(g.db, zSql, 0, 0, &zErrMsg);
6767
sqlite3_free(zSql);
6868
if( rc!=SQLITE_OK || zErrMsg ){
6969
if( zErrMsg==0 ) zErrMsg = (char*)sqlite3_errmsg(g.db);
7070
fossil_fatal("not a valid bundle: %s", zFile);
@@ -73,18 +73,18 @@
7373
db_multi_exec(zBundleInit /*works-like:"%w%w"*/, zBName, zBName);
7474
}else{
7575
sqlite3_stmt *pStmt;
7676
zSql = sqlite3_mprintf("SELECT bcname, bcvalue"
7777
" FROM \"%w\".bconfig", zBName);
78
- if( zSql==0 ) fossil_fatal("out of memory");
78
+ if( zSql==0 ) fossil_panic("out of memory");
7979
rc = sqlite3_prepare(g.db, zSql, -1, &pStmt, 0);
8080
if( rc ) fossil_fatal("not a valid bundle: %s", zFile);
8181
sqlite3_free(zSql);
8282
sqlite3_finalize(pStmt);
8383
zSql = sqlite3_mprintf("SELECT blobid, uuid, sz, delta, notes, data"
8484
" FROM \"%w\".bblob", zBName);
85
- if( zSql==0 ) fossil_fatal("out of memory");
85
+ if( zSql==0 ) fossil_panic("out of memory");
8686
rc = sqlite3_prepare(g.db, zSql, -1, &pStmt, 0);
8787
if( rc ) fossil_fatal("not a valid bundle: %s", zFile);
8888
sqlite3_free(zSql);
8989
sqlite3_finalize(pStmt);
9090
}
9191
--- src/bundle.c
+++ src/bundle.c
@@ -60,11 +60,11 @@
60 if( !doInit && file_size(zFile, ExtFILE)<0 ){
61 fossil_fatal("no such file: %s", zFile);
62 }
63 assert( g.db );
64 zSql = sqlite3_mprintf("ATTACH %Q AS %Q", zFile, zBName);
65 if( zSql==0 ) fossil_fatal("out of memory");
66 rc = sqlite3_exec(g.db, zSql, 0, 0, &zErrMsg);
67 sqlite3_free(zSql);
68 if( rc!=SQLITE_OK || zErrMsg ){
69 if( zErrMsg==0 ) zErrMsg = (char*)sqlite3_errmsg(g.db);
70 fossil_fatal("not a valid bundle: %s", zFile);
@@ -73,18 +73,18 @@
73 db_multi_exec(zBundleInit /*works-like:"%w%w"*/, zBName, zBName);
74 }else{
75 sqlite3_stmt *pStmt;
76 zSql = sqlite3_mprintf("SELECT bcname, bcvalue"
77 " FROM \"%w\".bconfig", zBName);
78 if( zSql==0 ) fossil_fatal("out of memory");
79 rc = sqlite3_prepare(g.db, zSql, -1, &pStmt, 0);
80 if( rc ) fossil_fatal("not a valid bundle: %s", zFile);
81 sqlite3_free(zSql);
82 sqlite3_finalize(pStmt);
83 zSql = sqlite3_mprintf("SELECT blobid, uuid, sz, delta, notes, data"
84 " FROM \"%w\".bblob", zBName);
85 if( zSql==0 ) fossil_fatal("out of memory");
86 rc = sqlite3_prepare(g.db, zSql, -1, &pStmt, 0);
87 if( rc ) fossil_fatal("not a valid bundle: %s", zFile);
88 sqlite3_free(zSql);
89 sqlite3_finalize(pStmt);
90 }
91
--- src/bundle.c
+++ src/bundle.c
@@ -60,11 +60,11 @@
60 if( !doInit && file_size(zFile, ExtFILE)<0 ){
61 fossil_fatal("no such file: %s", zFile);
62 }
63 assert( g.db );
64 zSql = sqlite3_mprintf("ATTACH %Q AS %Q", zFile, zBName);
65 if( zSql==0 ) fossil_panic("out of memory");
66 rc = sqlite3_exec(g.db, zSql, 0, 0, &zErrMsg);
67 sqlite3_free(zSql);
68 if( rc!=SQLITE_OK || zErrMsg ){
69 if( zErrMsg==0 ) zErrMsg = (char*)sqlite3_errmsg(g.db);
70 fossil_fatal("not a valid bundle: %s", zFile);
@@ -73,18 +73,18 @@
73 db_multi_exec(zBundleInit /*works-like:"%w%w"*/, zBName, zBName);
74 }else{
75 sqlite3_stmt *pStmt;
76 zSql = sqlite3_mprintf("SELECT bcname, bcvalue"
77 " FROM \"%w\".bconfig", zBName);
78 if( zSql==0 ) fossil_panic("out of memory");
79 rc = sqlite3_prepare(g.db, zSql, -1, &pStmt, 0);
80 if( rc ) fossil_fatal("not a valid bundle: %s", zFile);
81 sqlite3_free(zSql);
82 sqlite3_finalize(pStmt);
83 zSql = sqlite3_mprintf("SELECT blobid, uuid, sz, delta, notes, data"
84 " FROM \"%w\".bblob", zBName);
85 if( zSql==0 ) fossil_panic("out of memory");
86 rc = sqlite3_prepare(g.db, zSql, -1, &pStmt, 0);
87 if( rc ) fossil_fatal("not a valid bundle: %s", zFile);
88 sqlite3_free(zSql);
89 sqlite3_finalize(pStmt);
90 }
91
+34 -21
--- src/cache.c
+++ src/cache.c
@@ -61,27 +61,31 @@
6161
fossil_free(zDbName);
6262
if( rc ){
6363
sqlite3_close(db);
6464
return 0;
6565
}
66
- rc = sqlite3_exec(db,
67
- "PRAGMA page_size=8192;"
68
- "CREATE TABLE IF NOT EXISTS blob(id INTEGER PRIMARY KEY, data BLOB);"
69
- "CREATE TABLE IF NOT EXISTS cache("
70
- "key TEXT PRIMARY KEY," /* Key used to access the cache */
71
- "id INT REFERENCES blob," /* The cache content */
72
- "sz INT," /* Size of content in bytes */
73
- "tm INT," /* Last access time (unix timestampe) */
74
- "nref INT" /* Number of uses */
75
- ");"
76
- "CREATE TRIGGER IF NOT EXISTS cacheDel AFTER DELETE ON cache BEGIN"
77
- " DELETE FROM blob WHERE id=OLD.id;"
78
- "END;",
79
- 0, 0, 0);
80
- if( rc!=SQLITE_OK ){
81
- sqlite3_close(db);
82
- return 0;
66
+ sqlite3_busy_timeout(db, 5000);
67
+ if( sqlite3_table_column_metadata(db,0,"blob","key",0,0,0,0,0)!=SQLITE_OK ){
68
+ rc = sqlite3_exec(db,
69
+ "PRAGMA page_size=8192;"
70
+ "CREATE TABLE IF NOT EXISTS blob(id INTEGER PRIMARY KEY, data BLOB);"
71
+ "CREATE TABLE IF NOT EXISTS cache("
72
+ "key TEXT PRIMARY KEY," /* Key used to access the cache */
73
+ "id INT REFERENCES blob," /* The cache content */
74
+ "sz INT," /* Size of content in bytes */
75
+ "tm INT," /* Last access time (unix timestampe) */
76
+ "nref INT" /* Number of uses */
77
+ ");"
78
+ "CREATE TRIGGER IF NOT EXISTS cacheDel AFTER DELETE ON cache BEGIN"
79
+ " DELETE FROM blob WHERE id=OLD.id;"
80
+ "END;",
81
+ 0, 0, 0
82
+ );
83
+ if( rc!=SQLITE_OK ){
84
+ sqlite3_close(db);
85
+ return 0;
86
+ }
8387
}
8488
return db;
8589
}
8690
8791
/*
@@ -165,17 +169,25 @@
165169
sqlite3_bind_int(pStmt, 3, sqlite3_last_insert_rowid(db));
166170
if( sqlite3_step(pStmt)!=SQLITE_DONE) goto cache_write_end;
167171
rc = sqlite3_changes(db);
168172
169173
/* If the write was successful, truncate the cache to keep at most
170
- ** max-cache-entry entries in the cache */
174
+ ** max-cache-entry entries in the cache.
175
+ **
176
+ ** The cache entry replacement algorithm is approximately LRU
177
+ ** (least recently used). However, each access of an entry buys
178
+ ** that entry an extra hour of grace, so that more commonly accessed
179
+ ** entries are held in cache longer. The extra "grace" allotted to
180
+ ** an entry is limited to 2 days worth.
181
+ */
171182
if( rc ){
172183
nKeep = db_get_int("max-cache-entry",10);
173184
sqlite3_finalize(pStmt);
174185
pStmt = cacheStmt(db,
175186
"DELETE FROM cache WHERE rowid IN ("
176
- "SELECT rowid FROM cache ORDER BY tm DESC"
187
+ "SELECT rowid FROM cache"
188
+ " ORDER BY (tm + 3600*min(nRef,48)) DESC"
177189
" LIMIT -1 OFFSET ?1)");
178190
if( pStmt ){
179191
sqlite3_bind_int(pStmt, 1, nKeep);
180192
sqlite3_step(pStmt);
181193
}
@@ -291,11 +303,12 @@
291303
sqlite3_close(db);
292304
fossil_print("cache cleared\n");
293305
}else{
294306
fossil_print("nothing to clear; cache does not exist\n");
295307
}
296
- }else if(( strncmp(zCmd, "list", nCmd)==0 ) || ( strncmp(zCmd, "ls", nCmd)==0 )){
308
+ }else if(( strncmp(zCmd, "list", nCmd)==0 )
309
+ || ( strncmp(zCmd, "ls", nCmd)==0 )){
297310
db = cacheOpen(0);
298311
if( db==0 ){
299312
fossil_print("cache does not exist\n");
300313
}else{
301314
int nEntry = 0;
@@ -350,11 +363,11 @@
350363
char *zDbName = cacheName();
351364
cache_register_sizename(db);
352365
pStmt = cacheStmt(db,
353366
"SELECT key, sizename(sz), nRef, datetime(tm,'unixepoch')"
354367
" FROM cache"
355
- " ORDER BY tm DESC"
368
+ " ORDER BY (tm + 3600*min(nRef,48)) DESC"
356369
);
357370
if( pStmt ){
358371
@ <ol>
359372
while( sqlite3_step(pStmt)==SQLITE_ROW ){
360373
const unsigned char *zName = sqlite3_column_text(pStmt,0);
361374
--- src/cache.c
+++ src/cache.c
@@ -61,27 +61,31 @@
61 fossil_free(zDbName);
62 if( rc ){
63 sqlite3_close(db);
64 return 0;
65 }
66 rc = sqlite3_exec(db,
67 "PRAGMA page_size=8192;"
68 "CREATE TABLE IF NOT EXISTS blob(id INTEGER PRIMARY KEY, data BLOB);"
69 "CREATE TABLE IF NOT EXISTS cache("
70 "key TEXT PRIMARY KEY," /* Key used to access the cache */
71 "id INT REFERENCES blob," /* The cache content */
72 "sz INT," /* Size of content in bytes */
73 "tm INT," /* Last access time (unix timestampe) */
74 "nref INT" /* Number of uses */
75 ");"
76 "CREATE TRIGGER IF NOT EXISTS cacheDel AFTER DELETE ON cache BEGIN"
77 " DELETE FROM blob WHERE id=OLD.id;"
78 "END;",
79 0, 0, 0);
80 if( rc!=SQLITE_OK ){
81 sqlite3_close(db);
82 return 0;
 
 
 
 
83 }
84 return db;
85 }
86
87 /*
@@ -165,17 +169,25 @@
165 sqlite3_bind_int(pStmt, 3, sqlite3_last_insert_rowid(db));
166 if( sqlite3_step(pStmt)!=SQLITE_DONE) goto cache_write_end;
167 rc = sqlite3_changes(db);
168
169 /* If the write was successful, truncate the cache to keep at most
170 ** max-cache-entry entries in the cache */
 
 
 
 
 
 
 
171 if( rc ){
172 nKeep = db_get_int("max-cache-entry",10);
173 sqlite3_finalize(pStmt);
174 pStmt = cacheStmt(db,
175 "DELETE FROM cache WHERE rowid IN ("
176 "SELECT rowid FROM cache ORDER BY tm DESC"
 
177 " LIMIT -1 OFFSET ?1)");
178 if( pStmt ){
179 sqlite3_bind_int(pStmt, 1, nKeep);
180 sqlite3_step(pStmt);
181 }
@@ -291,11 +303,12 @@
291 sqlite3_close(db);
292 fossil_print("cache cleared\n");
293 }else{
294 fossil_print("nothing to clear; cache does not exist\n");
295 }
296 }else if(( strncmp(zCmd, "list", nCmd)==0 ) || ( strncmp(zCmd, "ls", nCmd)==0 )){
 
297 db = cacheOpen(0);
298 if( db==0 ){
299 fossil_print("cache does not exist\n");
300 }else{
301 int nEntry = 0;
@@ -350,11 +363,11 @@
350 char *zDbName = cacheName();
351 cache_register_sizename(db);
352 pStmt = cacheStmt(db,
353 "SELECT key, sizename(sz), nRef, datetime(tm,'unixepoch')"
354 " FROM cache"
355 " ORDER BY tm DESC"
356 );
357 if( pStmt ){
358 @ <ol>
359 while( sqlite3_step(pStmt)==SQLITE_ROW ){
360 const unsigned char *zName = sqlite3_column_text(pStmt,0);
361
--- src/cache.c
+++ src/cache.c
@@ -61,27 +61,31 @@
61 fossil_free(zDbName);
62 if( rc ){
63 sqlite3_close(db);
64 return 0;
65 }
66 sqlite3_busy_timeout(db, 5000);
67 if( sqlite3_table_column_metadata(db,0,"blob","key",0,0,0,0,0)!=SQLITE_OK ){
68 rc = sqlite3_exec(db,
69 "PRAGMA page_size=8192;"
70 "CREATE TABLE IF NOT EXISTS blob(id INTEGER PRIMARY KEY, data BLOB);"
71 "CREATE TABLE IF NOT EXISTS cache("
72 "key TEXT PRIMARY KEY," /* Key used to access the cache */
73 "id INT REFERENCES blob," /* The cache content */
74 "sz INT," /* Size of content in bytes */
75 "tm INT," /* Last access time (unix timestampe) */
76 "nref INT" /* Number of uses */
77 ");"
78 "CREATE TRIGGER IF NOT EXISTS cacheDel AFTER DELETE ON cache BEGIN"
79 " DELETE FROM blob WHERE id=OLD.id;"
80 "END;",
81 0, 0, 0
82 );
83 if( rc!=SQLITE_OK ){
84 sqlite3_close(db);
85 return 0;
86 }
87 }
88 return db;
89 }
90
91 /*
@@ -165,17 +169,25 @@
169 sqlite3_bind_int(pStmt, 3, sqlite3_last_insert_rowid(db));
170 if( sqlite3_step(pStmt)!=SQLITE_DONE) goto cache_write_end;
171 rc = sqlite3_changes(db);
172
173 /* If the write was successful, truncate the cache to keep at most
174 ** max-cache-entry entries in the cache.
175 **
176 ** The cache entry replacement algorithm is approximately LRU
177 ** (least recently used). However, each access of an entry buys
178 ** that entry an extra hour of grace, so that more commonly accessed
179 ** entries are held in cache longer. The extra "grace" allotted to
180 ** an entry is limited to 2 days worth.
181 */
182 if( rc ){
183 nKeep = db_get_int("max-cache-entry",10);
184 sqlite3_finalize(pStmt);
185 pStmt = cacheStmt(db,
186 "DELETE FROM cache WHERE rowid IN ("
187 "SELECT rowid FROM cache"
188 " ORDER BY (tm + 3600*min(nRef,48)) DESC"
189 " LIMIT -1 OFFSET ?1)");
190 if( pStmt ){
191 sqlite3_bind_int(pStmt, 1, nKeep);
192 sqlite3_step(pStmt);
193 }
@@ -291,11 +303,12 @@
303 sqlite3_close(db);
304 fossil_print("cache cleared\n");
305 }else{
306 fossil_print("nothing to clear; cache does not exist\n");
307 }
308 }else if(( strncmp(zCmd, "list", nCmd)==0 )
309 || ( strncmp(zCmd, "ls", nCmd)==0 )){
310 db = cacheOpen(0);
311 if( db==0 ){
312 fossil_print("cache does not exist\n");
313 }else{
314 int nEntry = 0;
@@ -350,11 +363,11 @@
363 char *zDbName = cacheName();
364 cache_register_sizename(db);
365 pStmt = cacheStmt(db,
366 "SELECT key, sizename(sz), nRef, datetime(tm,'unixepoch')"
367 " FROM cache"
368 " ORDER BY (tm + 3600*min(nRef,48)) DESC"
369 );
370 if( pStmt ){
371 @ <ol>
372 while( sqlite3_step(pStmt)==SQLITE_ROW ){
373 const unsigned char *zName = sqlite3_column_text(pStmt,0);
374
+3 -3
--- src/captcha.c
+++ src/captcha.c
@@ -497,17 +497,17 @@
497497
** true (non-zero).
498498
**
499499
** The query parameters examined are "captchaseed" for the seed value and
500500
** "captcha" for text that the user types in response to the captcha prompt.
501501
*/
502
-int captcha_is_correct(void){
502
+int captcha_is_correct(int bAlwaysNeeded){
503503
const char *zSeed;
504504
const char *zEntered;
505505
const char *zDecode;
506506
char z[30];
507507
int i;
508
- if( !captcha_needed() ){
508
+ if( !bAlwaysNeeded && !captcha_needed() ){
509509
return 1; /* No captcha needed */
510510
}
511511
zSeed = P("captchaseed");
512512
if( zSeed==0 ) return 0;
513513
zEntered = P("captcha");
@@ -593,11 +593,11 @@
593593
}
594594
#endif
595595
zCookieName = mprintf("fossil-cc-%.10s", db_get("project-code","x"));
596596
zCookieValue = P(zCookieName);
597597
if( zCookieValue && atoi(zCookieValue)==1 ) return 0;
598
- if( captcha_is_correct() ){
598
+ if( captcha_is_correct(0) ){
599599
cgi_set_cookie(zCookieName, "1", login_cookie_path(), 8*3600);
600600
return 0;
601601
}
602602
603603
/* This appears to be a spider. Offer the captcha */
604604
--- src/captcha.c
+++ src/captcha.c
@@ -497,17 +497,17 @@
497 ** true (non-zero).
498 **
499 ** The query parameters examined are "captchaseed" for the seed value and
500 ** "captcha" for text that the user types in response to the captcha prompt.
501 */
502 int captcha_is_correct(void){
503 const char *zSeed;
504 const char *zEntered;
505 const char *zDecode;
506 char z[30];
507 int i;
508 if( !captcha_needed() ){
509 return 1; /* No captcha needed */
510 }
511 zSeed = P("captchaseed");
512 if( zSeed==0 ) return 0;
513 zEntered = P("captcha");
@@ -593,11 +593,11 @@
593 }
594 #endif
595 zCookieName = mprintf("fossil-cc-%.10s", db_get("project-code","x"));
596 zCookieValue = P(zCookieName);
597 if( zCookieValue && atoi(zCookieValue)==1 ) return 0;
598 if( captcha_is_correct() ){
599 cgi_set_cookie(zCookieName, "1", login_cookie_path(), 8*3600);
600 return 0;
601 }
602
603 /* This appears to be a spider. Offer the captcha */
604
--- src/captcha.c
+++ src/captcha.c
@@ -497,17 +497,17 @@
497 ** true (non-zero).
498 **
499 ** The query parameters examined are "captchaseed" for the seed value and
500 ** "captcha" for text that the user types in response to the captcha prompt.
501 */
502 int captcha_is_correct(int bAlwaysNeeded){
503 const char *zSeed;
504 const char *zEntered;
505 const char *zDecode;
506 char z[30];
507 int i;
508 if( !bAlwaysNeeded && !captcha_needed() ){
509 return 1; /* No captcha needed */
510 }
511 zSeed = P("captchaseed");
512 if( zSeed==0 ) return 0;
513 zEntered = P("captcha");
@@ -593,11 +593,11 @@
593 }
594 #endif
595 zCookieName = mprintf("fossil-cc-%.10s", db_get("project-code","x"));
596 zCookieValue = P(zCookieName);
597 if( zCookieValue && atoi(zCookieValue)==1 ) return 0;
598 if( captcha_is_correct(0) ){
599 cgi_set_cookie(zCookieName, "1", login_cookie_path(), 8*3600);
600 return 0;
601 }
602
603 /* This appears to be a spider. Offer the captcha */
604
+182 -145
--- src/cgi.c
+++ src/cgi.c
@@ -56,10 +56,12 @@
5656
#define P(x) cgi_parameter((x),0)
5757
#define PD(x,y) cgi_parameter((x),(y))
5858
#define PT(x) cgi_parameter_trimmed((x),0)
5959
#define PDT(x,y) cgi_parameter_trimmed((x),(y))
6060
#define PB(x) cgi_parameter_boolean(x)
61
+#define PCK(x) cgi_parameter_checked(x,1)
62
+#define PIF(x,y) cgi_parameter_checked(x,y)
6163
6264
6365
/*
6466
** Destinations for output text.
6567
*/
@@ -198,97 +200,47 @@
198200
void cgi_append_header(const char *zLine){
199201
blob_append(&extraHeader, zLine, -1);
200202
}
201203
202204
/*
203
-** Set a cookie.
205
+** Set a cookie by queuing up the appropriate HTTP header output. If
206
+** !g.isHTTP, this is a no-op.
204207
**
205208
** Zero lifetime implies a session cookie.
206209
*/
207210
void cgi_set_cookie(
208211
const char *zName, /* Name of the cookie */
209212
const char *zValue, /* Value of the cookie. Automatically escaped */
210213
const char *zPath, /* Path cookie applies to. NULL means "/" */
211214
int lifetime /* Expiration of the cookie in seconds from now */
212215
){
213
- char *zSecure = "";
214
- if( zPath==0 ){
216
+ char const *zSecure = "";
217
+ if(!g.isHTTP) return /* e.g. JSON CLI mode, where g.zTop is not set */;
218
+ else if( zPath==0 ){
215219
zPath = g.zTop;
216220
if( zPath[0]==0 ) zPath = "/";
217221
}
218222
if( g.zBaseURL!=0 && strncmp(g.zBaseURL, "https:", 6)==0 ){
219223
zSecure = " secure;";
220224
}
221225
if( lifetime>0 ){
222
- lifetime += (int)time(0);
223226
blob_appendf(&extraHeader,
224
- "Set-Cookie: %s=%t; Path=%s; expires=%z; HttpOnly;%s Version=1\r\n",
225
- zName, zValue, zPath, cgi_rfc822_datestamp(lifetime), zSecure);
227
+ "Set-Cookie: %s=%t; Path=%s; max-age=%d; HttpOnly;%s Version=1\r\n",
228
+ zName, zValue, zPath, lifetime, zSecure);
226229
}else{
227230
blob_appendf(&extraHeader,
228231
"Set-Cookie: %s=%t; Path=%s; HttpOnly;%s Version=1\r\n",
229232
zName, zValue, zPath, zSecure);
230233
}
231234
}
232235
233
-#if 0
234
-/*
235
-** Add an ETag header line
236
-*/
237
-static char *cgi_add_etag(char *zTxt, int nLen){
238
- MD5Context ctx;
239
- unsigned char digest[16];
240
- int i, j;
241
- char zETag[64];
242
-
243
- MD5Init(&ctx);
244
- MD5Update(&ctx,zTxt,nLen);
245
- MD5Final(digest,&ctx);
246
- for(j=i=0; i<16; i++,j+=2){
247
- bprintf(&zETag[j],sizeof(zETag)-j,"%02x",(int)digest[i]);
248
- }
249
- blob_appendf(&extraHeader, "ETag: %s\r\n", zETag);
250
- return fossil_strdup(zETag);
251
-}
252
-
253
-/*
254
-** Do some cache control stuff. First, we generate an ETag and include it in
255
-** the response headers. Second, we do whatever is necessary to determine if
256
-** the request was asking about caching and whether we need to send back the
257
-** response body. If we shouldn't send a body, return non-zero.
258
-**
259
-** Currently, we just check the ETag against any If-None-Match header.
260
-**
261
-** FIXME: In some cases (attachments, file contents) we could check
262
-** If-Modified-Since headers and always include Last-Modified in responses.
263
-*/
264
-static int check_cache_control(void){
265
- /* FIXME: there's some gotchas wth cookies and some headers. */
266
- char *zETag = cgi_add_etag(blob_buffer(&cgiContent),blob_size(&cgiContent));
267
- char *zMatch = P("HTTP_IF_NONE_MATCH");
268
-
269
- if( zETag!=0 && zMatch!=0 ) {
270
- char *zBuf = fossil_strdup(zMatch);
271
- if( zBuf!=0 ){
272
- char *zTok = 0;
273
- char *zPos;
274
- for( zTok = strtok_r(zBuf, ",\"",&zPos);
275
- zTok && fossil_stricmp(zTok,zETag);
276
- zTok = strtok_r(0, ",\"",&zPos)){}
277
- fossil_free(zBuf);
278
- if(zTok) return 1;
279
- }
280
- }
281
-
282
- return 0;
283
-}
284
-#endif
285236
286237
/*
287238
** Return true if the response should be sent with Content-Encoding: gzip.
288239
*/
289240
static int is_gzippable(void){
241
+ if( g.fNoHttpCompress ) return 0;
290242
if( strstr(PD("HTTP_ACCEPT_ENCODING", ""), "gzip")==0 ) return 0;
291243
return strncmp(zContentType, "text/", 5)==0
292244
|| sqlite3_strglob("application/*xml", zContentType)==0
293245
|| sqlite3_strglob("application/*javascript", zContentType)==0;
294246
}
@@ -301,29 +253,32 @@
301253
if( iReplyStatus<=0 ){
302254
iReplyStatus = 200;
303255
zReplyStatus = "OK";
304256
}
305257
306
-#if 0
307
- if( iReplyStatus==200 && check_cache_control() ) {
308
- /* change the status to "unchanged" and we can skip sending the
309
- ** actual response body. Obviously we only do this when we _have_ a
310
- ** body (code 200).
311
- */
312
- iReplyStatus = 304;
313
- zReplyStatus = "Not Modified";
314
- }
315
-#endif
316
-
317258
if( g.fullHttpReply ){
318259
fprintf(g.httpOut, "HTTP/1.0 %d %s\r\n", iReplyStatus, zReplyStatus);
319260
fprintf(g.httpOut, "Date: %s\r\n", cgi_rfc822_datestamp(time(0)));
320261
fprintf(g.httpOut, "Connection: close\r\n");
321262
fprintf(g.httpOut, "X-UA-Compatible: IE=edge\r\n");
322263
}else{
323264
fprintf(g.httpOut, "Status: %d %s\r\n", iReplyStatus, zReplyStatus);
324265
}
266
+ if( g.isConst ){
267
+ /* isConst means that the reply is guaranteed to be invariant, even
268
+ ** after configuration changes and/or Fossil binary recompiles. */
269
+ fprintf(g.httpOut, "Cache-Control: max-age=31536000\r\n");
270
+ }else if( etag_tag()!=0 ){
271
+ fprintf(g.httpOut, "ETag: %s\r\n", etag_tag());
272
+ fprintf(g.httpOut, "Cache-Control: max-age=%d\r\n", etag_maxage());
273
+ }else{
274
+ fprintf(g.httpOut, "Cache-control: no-cache\r\n");
275
+ }
276
+ if( etag_mtime()>0 ){
277
+ fprintf(g.httpOut, "Last-Modified: %s\r\n",
278
+ cgi_rfc822_datestamp(etag_mtime()));
279
+ }
325280
326281
if( blob_size(&extraHeader)>0 ){
327282
fprintf(g.httpOut, "%s", blob_buffer(&extraHeader));
328283
}
329284
@@ -343,24 +298,10 @@
343298
**
344299
** These headers are probably best added by the web server hosting fossil as
345300
** a CGI script.
346301
*/
347302
348
- if( g.isConst ){
349
- /* constant means that the input URL will _never_ generate anything
350
- ** else. In the case of attachments, the contents won't change because
351
- ** an attempt to change them generates a new attachment number. In the
352
- ** case of most /getfile calls for specific versions, the only way the
353
- ** content changes is if someone breaks the SCM. And if that happens, a
354
- ** stale cache is the least of the problem. So we provide an Expires
355
- ** header set to a reasonable period (default: one week).
356
- */
357
- fprintf(g.httpOut, "Cache-control: max-age=28800\r\n");
358
- }else{
359
- fprintf(g.httpOut, "Cache-control: no-cache\r\n");
360
- }
361
-
362303
/* Content intended for logged in users should only be cached in
363304
** the browser, not some shared location.
364305
*/
365306
fprintf(g.httpOut, "Content-Type: %s; charset=utf-8\r\n", zContentType);
366307
if( fossil_strcmp(zContentType,"application/x-fossil")==0 ){
@@ -398,10 +339,18 @@
398339
}
399340
}
400341
}
401342
fflush(g.httpOut);
402343
CGIDEBUG(("DONE\n"));
344
+
345
+ /* After the webpage has been sent, do any useful background
346
+ ** processing.
347
+ */
348
+ if( g.db!=0 && db_repository_has_changed() ){
349
+ if( g.fAnyTrace ) fprintf(stderr, "-- repository changes have occurred\n");
350
+ backoffice_run();
351
+ }
403352
}
404353
405354
/*
406355
** Do a redirect request to the URL given in the argument.
407356
**
@@ -456,10 +405,33 @@
456405
zRef = P("HTTP_REFERER");
457406
if( zRef==0 ) zRef = zDefault;
458407
}
459408
return zRef;
460409
}
410
+
411
+/*
412
+** Return true if the current request appears to be safe from a
413
+** Cross-Site Request Forgery (CSRF) attack. Conditions that must
414
+** be met:
415
+**
416
+** * The HTTP_REFERER must have the same origin
417
+** * The REQUEST_METHOD must be POST - or requirePost==0
418
+*/
419
+int cgi_csrf_safe(int requirePost){
420
+ const char *zRef = P("HTTP_REFERER");
421
+ int nBase;
422
+ if( zRef==0 ) return 0;
423
+ if( requirePost ){
424
+ const char *zMethod = P("REQUEST_METHOD");
425
+ if( zMethod==0 ) return 0;
426
+ if( strcmp(zMethod,"POST")!=0 ) return 0;
427
+ }
428
+ nBase = (int)strlen(g.zBaseURL);
429
+ if( strncmp(g.zBaseURL,zRef,nBase)!=0 ) return 0;
430
+ if( zRef[nBase]!=0 && zRef[nBase]!='/' ) return 0;
431
+ return 1;
432
+}
461433
462434
/*
463435
** Information about all query parameters and cookies are stored
464436
** in these variables.
465437
*/
@@ -596,10 +568,15 @@
596568
** are ignored.
597569
**
598570
** * it is impossible for a cookie or query parameter to
599571
** override the value of an environment variable since
600572
** environment variables always have uppercase names.
573
+**
574
+** 2018-03-29: Also ignore the entry if NAME that contains any characters
575
+** other than [a-zA-Z0-9_]. There are no known exploits involving unusual
576
+** names that contain characters outside that set, but it never hurts to
577
+** be extra cautious when sanitizing inputs.
601578
**
602579
** Parameters are separated by the "terminator" character. Whitespace
603580
** before the NAME is ignored.
604581
**
605582
** The input string "z" is modified but no copies is made. "z"
@@ -626,11 +603,11 @@
626603
dehttpize(zValue);
627604
}else{
628605
if( *z ){ *z++ = 0; }
629606
zValue = "";
630607
}
631
- if( fossil_islower(zName[0]) ){
608
+ if( fossil_islower(zName[0]) && fossil_no_strange_characters(zName+1) ){
632609
cgi_set_parameter_nocopy(zName, zValue, isQP);
633610
}
634611
#ifdef FOSSIL_ENABLE_JSON
635612
json_setenv( zName, cson_value_new_string(zValue,strlen(zValue)) );
636613
#endif /* FOSSIL_ENABLE_JSON */
@@ -912,13 +889,17 @@
912889
pLog = 0;
913890
return;
914891
}
915892
if( pLog==0 ){
916893
char zFile[50];
894
+#if defined(_WIN32)
917895
unsigned r;
918896
sqlite3_randomness(sizeof(r), &r);
919897
sqlite3_snprintf(sizeof(zFile), zFile, "httplog-%08x.txt", r);
898
+#else
899
+ sqlite3_snprintf(sizeof(zFile), zFile, "httplog-%05d.txt", getpid());
900
+#endif
920901
pLog = fossil_fopen(zFile, "wb");
921902
if( pLog ){
922903
fprintf(stderr, "# open log on %s\n", zFile);
923904
}else{
924905
fprintf(stderr, "# failed to open %s\n", zFile);
@@ -1137,10 +1118,11 @@
11371118
const char *zIn;
11381119
char *zOut;
11391120
int i;
11401121
zIn = cgi_parameter(zName, 0);
11411122
if( zIn==0 ) zIn = zDefault;
1123
+ if( zIn==0 ) return 0;
11421124
while( fossil_isspace(zIn[0]) ) zIn++;
11431125
zOut = fossil_strdup(zIn);
11441126
for(i=0; zOut[i]; i++){}
11451127
while( i>0 && fossil_isspace(zOut[i-1]) ) zOut[--i] = 0;
11461128
return zOut;
@@ -1153,10 +1135,33 @@
11531135
int cgi_parameter_boolean(const char *zName){
11541136
const char *zIn = cgi_parameter(zName, 0);
11551137
if( zIn==0 ) return 0;
11561138
return zIn[0]==0 || is_truth(zIn);
11571139
}
1140
+
1141
+/*
1142
+** Return either an empty string "" or the string "checked" depending
1143
+** on whether or not parameter zName has value iValue. If parameter
1144
+** zName does not exist, that is assumed to be the same as value 0.
1145
+**
1146
+** This routine implements the PCK(x) and PIF(x,y) macros. The PIF(x,y)
1147
+** macro generateds " checked" if the value of parameter x equals integer y.
1148
+** PCK(x) is the same as PIF(x,1). These macros are used to generate
1149
+** the "checked" attribute on checkbox and radio controls of forms.
1150
+*/
1151
+const char *cgi_parameter_checked(const char *zName, int iValue){
1152
+ const char *zIn = cgi_parameter(zName,0);
1153
+ int x;
1154
+ if( zIn==0 ){
1155
+ x = 0;
1156
+ }else if( !fossil_isdigit(zIn[0]) ){
1157
+ x = is_truth(zIn);
1158
+ }else{
1159
+ x = atoi(zIn);
1160
+ }
1161
+ return x==iValue ? "checked" : "";
1162
+}
11581163
11591164
/*
11601165
** Return the name of the i-th CGI parameter. Return NULL if there
11611166
** are fewer than i registered CGI parameters.
11621167
*/
@@ -1217,20 +1222,24 @@
12171222
** Print all query parameters on standard output. Format the
12181223
** parameters as HTML. This is used for testing and debugging.
12191224
**
12201225
** Omit the values of the cookies unless showAll is true.
12211226
*/
1222
-void cgi_print_all(int showAll){
1227
+void cgi_print_all(int showAll, int onConsole){
12231228
int i;
12241229
cgi_parameter("",""); /* Force the parameters into sorted order */
12251230
for(i=0; i<nUsedQP; i++){
12261231
const char *zName = aParamQP[i].zName;
12271232
if( !showAll ){
12281233
if( fossil_stricmp("HTTP_COOKIE",zName)==0 ) continue;
12291234
if( fossil_strnicmp("fossil-",zName,7)==0 ) continue;
12301235
}
1231
- cgi_printf("%h = %h <br />\n", zName, aParamQP[i].zValue);
1236
+ if( onConsole ){
1237
+ fossil_trace("%s = %s\n", zName, aParamQP[i].zValue);
1238
+ }else{
1239
+ cgi_printf("%h = %h <br />\n", zName, aParamQP[i].zValue);
1240
+ }
12321241
}
12331242
}
12341243
12351244
/*
12361245
** Export all untagged query parameters (but not cookies or environment
@@ -1343,12 +1352,14 @@
13431352
** NULL pointer to stick with the IP address previously computed and
13441353
** loaded into g.zIpAddr.
13451354
*/
13461355
static const char *cgi_accept_forwarded_for(const char *z){
13471356
int i;
1348
- if( fossil_strcmp(g.zIpAddr, "127.0.0.1")!=0 ) return 0;
1349
-
1357
+ if( !cgi_is_loopback(g.zIpAddr) ){
1358
+ /* Only accept X-FORWARDED-FOR if input coming from the local machine */
1359
+ return 0;
1360
+ }
13501361
i = strlen(z)-1;
13511362
while( i>=0 && z[i]!=',' && !fossil_isspace(z[i]) ) i--;
13521363
return &z[++i];
13531364
}
13541365
@@ -1372,10 +1383,36 @@
13721383
while( fossil_isspace(*zInput) ){ zInput++; }
13731384
}
13741385
if( zLeftOver ){ *zLeftOver = zInput; }
13751386
return zResult;
13761387
}
1388
+
1389
+/*
1390
+** Determine the IP address on the other side of a connection.
1391
+** Return a pointer to a string. Or return 0 if unable.
1392
+**
1393
+** The string is held in a static buffer that is overwritten on
1394
+** each call.
1395
+*/
1396
+char *cgi_remote_ip(int fd){
1397
+#if 0
1398
+ static char zIp[100];
1399
+ struct sockaddr_in6 addr;
1400
+ socklen_t sz = sizeof(addr);
1401
+ if( getpeername(fd, &addr, &sz) ) return 0;
1402
+ zIp[0] = 0;
1403
+ if( inet_ntop(AF_INET6, &addr, zIp, sizeof(zIp))==0 ){
1404
+ return 0;
1405
+ }
1406
+ return zIp;
1407
+#else
1408
+ struct sockaddr_in remoteName;
1409
+ socklen_t size = sizeof(struct sockaddr_in);
1410
+ if( getpeername(fd, (struct sockaddr*)&remoteName, &size) ) return 0;
1411
+ return inet_ntoa(remoteName.sin_addr);
1412
+#endif
1413
+}
13771414
13781415
/*
13791416
** This routine handles a single HTTP request which is coming in on
13801417
** g.httpIn and which replies on g.httpOut
13811418
**
@@ -1386,12 +1423,10 @@
13861423
** and subsequent code handles the actual generation of the webpage.
13871424
*/
13881425
void cgi_handle_http_request(const char *zIpAddr){
13891426
char *z, *zToken;
13901427
int i;
1391
- struct sockaddr_in remoteName;
1392
- socklen_t size = sizeof(struct sockaddr_in);
13931428
char zLine[2000]; /* A single line of input. */
13941429
g.fullHttpReply = 1;
13951430
if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
13961431
malformed_request("missing HTTP header");
13971432
}
@@ -1415,15 +1450,12 @@
14151450
cgi_setenv("SCRIPT_NAME", "");
14161451
for(i=0; zToken[i] && zToken[i]!='?'; i++){}
14171452
if( zToken[i] ) zToken[i++] = 0;
14181453
cgi_setenv("PATH_INFO", zToken);
14191454
cgi_setenv("QUERY_STRING", &zToken[i]);
1420
- if( zIpAddr==0 &&
1421
- getpeername(fileno(g.httpIn), (struct sockaddr*)&remoteName,
1422
- &size)>=0
1423
- ){
1424
- zIpAddr = inet_ntoa(remoteName.sin_addr);
1455
+ if( zIpAddr==0 ){
1456
+ zIpAddr = cgi_remote_ip(fileno(g.httpIn));
14251457
}
14261458
if( zIpAddr ){
14271459
cgi_setenv("REMOTE_ADDR", zIpAddr);
14281460
g.zIpAddr = mprintf("%s", zIpAddr);
14291461
}
@@ -1782,10 +1814,11 @@
17821814
/* Use win32_http_server() instead */
17831815
fossil_exit(1);
17841816
#else
17851817
int listener = -1; /* The server socket */
17861818
int connection; /* A socket for each individual connection */
1819
+ int nRequest = 0; /* Number of requests handled so far */
17871820
fd_set readfds; /* Set of file descriptors for select() */
17881821
socklen_t lenaddr; /* Length of the inaddr structure */
17891822
int child; /* PID of the child process */
17901823
int nchildren = 0; /* Number of child processes */
17911824
struct timeval delay; /* How long to wait inside select() */
@@ -1869,27 +1902,31 @@
18691902
lenaddr = sizeof(inaddr);
18701903
connection = accept(listener, (struct sockaddr*)&inaddr, &lenaddr);
18711904
if( connection>=0 ){
18721905
child = fork();
18731906
if( child!=0 ){
1874
- if( child>0 ) nchildren++;
1907
+ if( child>0 ){
1908
+ nchildren++;
1909
+ nRequest++;
1910
+ }
18751911
close(connection);
18761912
}else{
18771913
int nErr = 0, fd;
18781914
close(0);
18791915
fd = dup(connection);
18801916
if( fd!=0 ) nErr++;
18811917
close(1);
18821918
fd = dup(connection);
18831919
if( fd!=1 ) nErr++;
1884
- if( !g.fAnyTrace ){
1920
+ if( 0 && !g.fAnyTrace ){
18851921
close(2);
18861922
fd = dup(connection);
18871923
if( fd!=2 ) nErr++;
18881924
}
18891925
close(connection);
18901926
g.nPendingRequest = nchildren+1;
1927
+ g.nRequest = nRequest+1;
18911928
return nErr;
18921929
}
18931930
}
18941931
}
18951932
/* Bury dead children */
@@ -1896,10 +1933,14 @@
18961933
if( nchildren ){
18971934
while(1){
18981935
int iStatus = 0;
18991936
pid_t x = waitpid(-1, &iStatus, WNOHANG);
19001937
if( x<=0 ) break;
1938
+ if( WIFSIGNALED(iStatus) && g.fAnyTrace ){
1939
+ fprintf(stderr, "/***** Child %d exited on signal %d (%s) *****/\n",
1940
+ x, WTERMSIG(iStatus), strsignal(WTERMSIG(iStatus)));
1941
+ }
19011942
nchildren--;
19021943
}
19031944
}
19041945
}
19051946
/* NOT REACHED */
@@ -1921,12 +1962,12 @@
19211962
19221963
19231964
/*
19241965
** Returns an RFC822-formatted time string suitable for HTTP headers.
19251966
** The timezone is always GMT. The value returned is always a
1926
-** string obtained from mprintf() and must be freed using free() to
1927
-** avoid a memory leak.
1967
+** string obtained from mprintf() and must be freed using fossil_free()
1968
+** to avoid a memory leak.
19281969
**
19291970
** See http://www.faqs.org/rfcs/rfc822.html, section 5
19301971
** and http://www.faqs.org/rfcs/rfc2616.html, section 3.3.
19311972
*/
19321973
char *cgi_rfc822_datestamp(time_t now){
@@ -1933,11 +1974,11 @@
19331974
struct tm *pTm;
19341975
pTm = gmtime(&now);
19351976
if( pTm==0 ){
19361977
return mprintf("");
19371978
}else{
1938
- return mprintf("%s, %d %s %02d %02d:%02d:%02d GMT",
1979
+ return mprintf("%s, %d %s %02d %02d:%02d:%02d +0000",
19391980
azDays[pTm->tm_wday], pTm->tm_mday, azMonths[pTm->tm_mon],
19401981
pTm->tm_year+1900, pTm->tm_hour, pTm->tm_min, pTm->tm_sec);
19411982
}
19421983
}
19431984
@@ -1947,55 +1988,42 @@
19471988
**
19481989
** Note that this won't handle all the _allowed_ HTTP formats, just the
19491990
** most popular one (the one generated by cgi_rfc822_datestamp(), actually).
19501991
*/
19511992
time_t cgi_rfc822_parsedate(const char *zDate){
1952
- struct tm t;
1953
- char zIgnore[16];
1954
- char zMonth[16];
1955
-
1956
- memset(&t, 0, sizeof(t));
1957
- if( 7==sscanf(zDate, "%12[A-Za-z,] %d %12[A-Za-z] %d %d:%d:%d", zIgnore,
1958
- &t.tm_mday, zMonth, &t.tm_year, &t.tm_hour, &t.tm_min,
1959
- &t.tm_sec)){
1960
-
1961
- if( t.tm_year > 1900 ) t.tm_year -= 1900;
1962
- for(t.tm_mon=0; azMonths[t.tm_mon]; t.tm_mon++){
1963
- if( !fossil_strnicmp( azMonths[t.tm_mon], zMonth, 3 )){
1964
- return mkgmtime(&t);
1965
- }
1966
- }
1967
- }
1968
-
1969
- return 0;
1970
-}
1971
-
1972
-/*
1973
-** Convert a struct tm* that represents a moment in UTC into the number
1974
-** of seconds in 1970, UTC.
1975
-*/
1976
-time_t mkgmtime(struct tm *p){
1977
- time_t t;
1978
- int nDay;
1979
- int isLeapYr;
1980
- /* Days in each month: 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 */
1981
- static int priorDays[] = { 0, 31, 59, 90,120,151,181,212,243,273,304,334 };
1982
- if( p->tm_mon<0 ){
1983
- int nYear = (11 - p->tm_mon)/12;
1984
- p->tm_year -= nYear;
1985
- p->tm_mon += nYear*12;
1986
- }else if( p->tm_mon>11 ){
1987
- p->tm_year += p->tm_mon/12;
1988
- p->tm_mon %= 12;
1989
- }
1990
- isLeapYr = p->tm_year%4==0 && (p->tm_year%100!=0 || (p->tm_year+300)%400==0);
1991
- p->tm_yday = priorDays[p->tm_mon] + p->tm_mday - 1;
1992
- if( isLeapYr && p->tm_mon>1 ) p->tm_yday++;
1993
- nDay = (p->tm_year-70)*365 + (p->tm_year-69)/4 -p->tm_year/100 +
1994
- (p->tm_year+300)/400 + p->tm_yday;
1995
- t = ((nDay*24 + p->tm_hour)*60 + p->tm_min)*60 + p->tm_sec;
1996
- return t;
1993
+ int mday, mon, year, yday, hour, min, sec;
1994
+ char zIgnore[4];
1995
+ char zMonth[4];
1996
+ static const char *const azMonths[] =
1997
+ {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
1998
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 0};
1999
+ if( 7==sscanf(zDate, "%3[A-Za-z], %d %3[A-Za-z] %d %d:%d:%d", zIgnore,
2000
+ &mday, zMonth, &year, &hour, &min, &sec)){
2001
+ if( year > 1900 ) year -= 1900;
2002
+ for(mon=0; azMonths[mon]; mon++){
2003
+ if( !strncmp( azMonths[mon], zMonth, 3 )){
2004
+ int nDay;
2005
+ int isLeapYr;
2006
+ static int priorDays[] =
2007
+ { 0, 31, 59, 90,120,151,181,212,243,273,304,334 };
2008
+ if( mon<0 ){
2009
+ int nYear = (11 - mon)/12;
2010
+ year -= nYear;
2011
+ mon += nYear*12;
2012
+ }else if( mon>11 ){
2013
+ year += mon/12;
2014
+ mon %= 12;
2015
+ }
2016
+ isLeapYr = year%4==0 && (year%100!=0 || (year+300)%400==0);
2017
+ yday = priorDays[mon] + mday - 1;
2018
+ if( isLeapYr && mon>1 ) yday++;
2019
+ nDay = (year-70)*365 + (year-69)/4 - year/100 + (year+300)/400 + yday;
2020
+ return ((time_t)(nDay*24 + hour)*60 + min)*60 + sec;
2021
+ }
2022
+ }
2023
+ }
2024
+ return 0;
19972025
}
19982026
19992027
/*
20002028
** Check the objectTime against the If-Modified-Since request header. If the
20012029
** object time isn't any newer than the header, we immediately send back
@@ -2026,5 +2054,14 @@
20262054
return zSshClient;
20272055
}
20282056
}
20292057
return zDefault;
20302058
}
2059
+
2060
+/*
2061
+** Return true if information is coming from the loopback network.
2062
+*/
2063
+int cgi_is_loopback(const char *zIpAddr){
2064
+ return fossil_strcmp(zIpAddr, "127.0.0.1")==0 ||
2065
+ fossil_strcmp(zIpAddr, "::ffff:127.0.0.1")==0 ||
2066
+ fossil_strcmp(zIpAddr, "::1")==0;
2067
+}
20312068
--- src/cgi.c
+++ src/cgi.c
@@ -56,10 +56,12 @@
56 #define P(x) cgi_parameter((x),0)
57 #define PD(x,y) cgi_parameter((x),(y))
58 #define PT(x) cgi_parameter_trimmed((x),0)
59 #define PDT(x,y) cgi_parameter_trimmed((x),(y))
60 #define PB(x) cgi_parameter_boolean(x)
 
 
61
62
63 /*
64 ** Destinations for output text.
65 */
@@ -198,97 +200,47 @@
198 void cgi_append_header(const char *zLine){
199 blob_append(&extraHeader, zLine, -1);
200 }
201
202 /*
203 ** Set a cookie.
 
204 **
205 ** Zero lifetime implies a session cookie.
206 */
207 void cgi_set_cookie(
208 const char *zName, /* Name of the cookie */
209 const char *zValue, /* Value of the cookie. Automatically escaped */
210 const char *zPath, /* Path cookie applies to. NULL means "/" */
211 int lifetime /* Expiration of the cookie in seconds from now */
212 ){
213 char *zSecure = "";
214 if( zPath==0 ){
 
215 zPath = g.zTop;
216 if( zPath[0]==0 ) zPath = "/";
217 }
218 if( g.zBaseURL!=0 && strncmp(g.zBaseURL, "https:", 6)==0 ){
219 zSecure = " secure;";
220 }
221 if( lifetime>0 ){
222 lifetime += (int)time(0);
223 blob_appendf(&extraHeader,
224 "Set-Cookie: %s=%t; Path=%s; expires=%z; HttpOnly;%s Version=1\r\n",
225 zName, zValue, zPath, cgi_rfc822_datestamp(lifetime), zSecure);
226 }else{
227 blob_appendf(&extraHeader,
228 "Set-Cookie: %s=%t; Path=%s; HttpOnly;%s Version=1\r\n",
229 zName, zValue, zPath, zSecure);
230 }
231 }
232
233 #if 0
234 /*
235 ** Add an ETag header line
236 */
237 static char *cgi_add_etag(char *zTxt, int nLen){
238 MD5Context ctx;
239 unsigned char digest[16];
240 int i, j;
241 char zETag[64];
242
243 MD5Init(&ctx);
244 MD5Update(&ctx,zTxt,nLen);
245 MD5Final(digest,&ctx);
246 for(j=i=0; i<16; i++,j+=2){
247 bprintf(&zETag[j],sizeof(zETag)-j,"%02x",(int)digest[i]);
248 }
249 blob_appendf(&extraHeader, "ETag: %s\r\n", zETag);
250 return fossil_strdup(zETag);
251 }
252
253 /*
254 ** Do some cache control stuff. First, we generate an ETag and include it in
255 ** the response headers. Second, we do whatever is necessary to determine if
256 ** the request was asking about caching and whether we need to send back the
257 ** response body. If we shouldn't send a body, return non-zero.
258 **
259 ** Currently, we just check the ETag against any If-None-Match header.
260 **
261 ** FIXME: In some cases (attachments, file contents) we could check
262 ** If-Modified-Since headers and always include Last-Modified in responses.
263 */
264 static int check_cache_control(void){
265 /* FIXME: there's some gotchas wth cookies and some headers. */
266 char *zETag = cgi_add_etag(blob_buffer(&cgiContent),blob_size(&cgiContent));
267 char *zMatch = P("HTTP_IF_NONE_MATCH");
268
269 if( zETag!=0 && zMatch!=0 ) {
270 char *zBuf = fossil_strdup(zMatch);
271 if( zBuf!=0 ){
272 char *zTok = 0;
273 char *zPos;
274 for( zTok = strtok_r(zBuf, ",\"",&zPos);
275 zTok && fossil_stricmp(zTok,zETag);
276 zTok = strtok_r(0, ",\"",&zPos)){}
277 fossil_free(zBuf);
278 if(zTok) return 1;
279 }
280 }
281
282 return 0;
283 }
284 #endif
285
286 /*
287 ** Return true if the response should be sent with Content-Encoding: gzip.
288 */
289 static int is_gzippable(void){
 
290 if( strstr(PD("HTTP_ACCEPT_ENCODING", ""), "gzip")==0 ) return 0;
291 return strncmp(zContentType, "text/", 5)==0
292 || sqlite3_strglob("application/*xml", zContentType)==0
293 || sqlite3_strglob("application/*javascript", zContentType)==0;
294 }
@@ -301,29 +253,32 @@
301 if( iReplyStatus<=0 ){
302 iReplyStatus = 200;
303 zReplyStatus = "OK";
304 }
305
306 #if 0
307 if( iReplyStatus==200 && check_cache_control() ) {
308 /* change the status to "unchanged" and we can skip sending the
309 ** actual response body. Obviously we only do this when we _have_ a
310 ** body (code 200).
311 */
312 iReplyStatus = 304;
313 zReplyStatus = "Not Modified";
314 }
315 #endif
316
317 if( g.fullHttpReply ){
318 fprintf(g.httpOut, "HTTP/1.0 %d %s\r\n", iReplyStatus, zReplyStatus);
319 fprintf(g.httpOut, "Date: %s\r\n", cgi_rfc822_datestamp(time(0)));
320 fprintf(g.httpOut, "Connection: close\r\n");
321 fprintf(g.httpOut, "X-UA-Compatible: IE=edge\r\n");
322 }else{
323 fprintf(g.httpOut, "Status: %d %s\r\n", iReplyStatus, zReplyStatus);
324 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
325
326 if( blob_size(&extraHeader)>0 ){
327 fprintf(g.httpOut, "%s", blob_buffer(&extraHeader));
328 }
329
@@ -343,24 +298,10 @@
343 **
344 ** These headers are probably best added by the web server hosting fossil as
345 ** a CGI script.
346 */
347
348 if( g.isConst ){
349 /* constant means that the input URL will _never_ generate anything
350 ** else. In the case of attachments, the contents won't change because
351 ** an attempt to change them generates a new attachment number. In the
352 ** case of most /getfile calls for specific versions, the only way the
353 ** content changes is if someone breaks the SCM. And if that happens, a
354 ** stale cache is the least of the problem. So we provide an Expires
355 ** header set to a reasonable period (default: one week).
356 */
357 fprintf(g.httpOut, "Cache-control: max-age=28800\r\n");
358 }else{
359 fprintf(g.httpOut, "Cache-control: no-cache\r\n");
360 }
361
362 /* Content intended for logged in users should only be cached in
363 ** the browser, not some shared location.
364 */
365 fprintf(g.httpOut, "Content-Type: %s; charset=utf-8\r\n", zContentType);
366 if( fossil_strcmp(zContentType,"application/x-fossil")==0 ){
@@ -398,10 +339,18 @@
398 }
399 }
400 }
401 fflush(g.httpOut);
402 CGIDEBUG(("DONE\n"));
 
 
 
 
 
 
 
 
403 }
404
405 /*
406 ** Do a redirect request to the URL given in the argument.
407 **
@@ -456,10 +405,33 @@
456 zRef = P("HTTP_REFERER");
457 if( zRef==0 ) zRef = zDefault;
458 }
459 return zRef;
460 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
461
462 /*
463 ** Information about all query parameters and cookies are stored
464 ** in these variables.
465 */
@@ -596,10 +568,15 @@
596 ** are ignored.
597 **
598 ** * it is impossible for a cookie or query parameter to
599 ** override the value of an environment variable since
600 ** environment variables always have uppercase names.
 
 
 
 
 
601 **
602 ** Parameters are separated by the "terminator" character. Whitespace
603 ** before the NAME is ignored.
604 **
605 ** The input string "z" is modified but no copies is made. "z"
@@ -626,11 +603,11 @@
626 dehttpize(zValue);
627 }else{
628 if( *z ){ *z++ = 0; }
629 zValue = "";
630 }
631 if( fossil_islower(zName[0]) ){
632 cgi_set_parameter_nocopy(zName, zValue, isQP);
633 }
634 #ifdef FOSSIL_ENABLE_JSON
635 json_setenv( zName, cson_value_new_string(zValue,strlen(zValue)) );
636 #endif /* FOSSIL_ENABLE_JSON */
@@ -912,13 +889,17 @@
912 pLog = 0;
913 return;
914 }
915 if( pLog==0 ){
916 char zFile[50];
 
917 unsigned r;
918 sqlite3_randomness(sizeof(r), &r);
919 sqlite3_snprintf(sizeof(zFile), zFile, "httplog-%08x.txt", r);
 
 
 
920 pLog = fossil_fopen(zFile, "wb");
921 if( pLog ){
922 fprintf(stderr, "# open log on %s\n", zFile);
923 }else{
924 fprintf(stderr, "# failed to open %s\n", zFile);
@@ -1137,10 +1118,11 @@
1137 const char *zIn;
1138 char *zOut;
1139 int i;
1140 zIn = cgi_parameter(zName, 0);
1141 if( zIn==0 ) zIn = zDefault;
 
1142 while( fossil_isspace(zIn[0]) ) zIn++;
1143 zOut = fossil_strdup(zIn);
1144 for(i=0; zOut[i]; i++){}
1145 while( i>0 && fossil_isspace(zOut[i-1]) ) zOut[--i] = 0;
1146 return zOut;
@@ -1153,10 +1135,33 @@
1153 int cgi_parameter_boolean(const char *zName){
1154 const char *zIn = cgi_parameter(zName, 0);
1155 if( zIn==0 ) return 0;
1156 return zIn[0]==0 || is_truth(zIn);
1157 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1158
1159 /*
1160 ** Return the name of the i-th CGI parameter. Return NULL if there
1161 ** are fewer than i registered CGI parameters.
1162 */
@@ -1217,20 +1222,24 @@
1217 ** Print all query parameters on standard output. Format the
1218 ** parameters as HTML. This is used for testing and debugging.
1219 **
1220 ** Omit the values of the cookies unless showAll is true.
1221 */
1222 void cgi_print_all(int showAll){
1223 int i;
1224 cgi_parameter("",""); /* Force the parameters into sorted order */
1225 for(i=0; i<nUsedQP; i++){
1226 const char *zName = aParamQP[i].zName;
1227 if( !showAll ){
1228 if( fossil_stricmp("HTTP_COOKIE",zName)==0 ) continue;
1229 if( fossil_strnicmp("fossil-",zName,7)==0 ) continue;
1230 }
1231 cgi_printf("%h = %h <br />\n", zName, aParamQP[i].zValue);
 
 
 
 
1232 }
1233 }
1234
1235 /*
1236 ** Export all untagged query parameters (but not cookies or environment
@@ -1343,12 +1352,14 @@
1343 ** NULL pointer to stick with the IP address previously computed and
1344 ** loaded into g.zIpAddr.
1345 */
1346 static const char *cgi_accept_forwarded_for(const char *z){
1347 int i;
1348 if( fossil_strcmp(g.zIpAddr, "127.0.0.1")!=0 ) return 0;
1349
 
 
1350 i = strlen(z)-1;
1351 while( i>=0 && z[i]!=',' && !fossil_isspace(z[i]) ) i--;
1352 return &z[++i];
1353 }
1354
@@ -1372,10 +1383,36 @@
1372 while( fossil_isspace(*zInput) ){ zInput++; }
1373 }
1374 if( zLeftOver ){ *zLeftOver = zInput; }
1375 return zResult;
1376 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1377
1378 /*
1379 ** This routine handles a single HTTP request which is coming in on
1380 ** g.httpIn and which replies on g.httpOut
1381 **
@@ -1386,12 +1423,10 @@
1386 ** and subsequent code handles the actual generation of the webpage.
1387 */
1388 void cgi_handle_http_request(const char *zIpAddr){
1389 char *z, *zToken;
1390 int i;
1391 struct sockaddr_in remoteName;
1392 socklen_t size = sizeof(struct sockaddr_in);
1393 char zLine[2000]; /* A single line of input. */
1394 g.fullHttpReply = 1;
1395 if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1396 malformed_request("missing HTTP header");
1397 }
@@ -1415,15 +1450,12 @@
1415 cgi_setenv("SCRIPT_NAME", "");
1416 for(i=0; zToken[i] && zToken[i]!='?'; i++){}
1417 if( zToken[i] ) zToken[i++] = 0;
1418 cgi_setenv("PATH_INFO", zToken);
1419 cgi_setenv("QUERY_STRING", &zToken[i]);
1420 if( zIpAddr==0 &&
1421 getpeername(fileno(g.httpIn), (struct sockaddr*)&remoteName,
1422 &size)>=0
1423 ){
1424 zIpAddr = inet_ntoa(remoteName.sin_addr);
1425 }
1426 if( zIpAddr ){
1427 cgi_setenv("REMOTE_ADDR", zIpAddr);
1428 g.zIpAddr = mprintf("%s", zIpAddr);
1429 }
@@ -1782,10 +1814,11 @@
1782 /* Use win32_http_server() instead */
1783 fossil_exit(1);
1784 #else
1785 int listener = -1; /* The server socket */
1786 int connection; /* A socket for each individual connection */
 
1787 fd_set readfds; /* Set of file descriptors for select() */
1788 socklen_t lenaddr; /* Length of the inaddr structure */
1789 int child; /* PID of the child process */
1790 int nchildren = 0; /* Number of child processes */
1791 struct timeval delay; /* How long to wait inside select() */
@@ -1869,27 +1902,31 @@
1869 lenaddr = sizeof(inaddr);
1870 connection = accept(listener, (struct sockaddr*)&inaddr, &lenaddr);
1871 if( connection>=0 ){
1872 child = fork();
1873 if( child!=0 ){
1874 if( child>0 ) nchildren++;
 
 
 
1875 close(connection);
1876 }else{
1877 int nErr = 0, fd;
1878 close(0);
1879 fd = dup(connection);
1880 if( fd!=0 ) nErr++;
1881 close(1);
1882 fd = dup(connection);
1883 if( fd!=1 ) nErr++;
1884 if( !g.fAnyTrace ){
1885 close(2);
1886 fd = dup(connection);
1887 if( fd!=2 ) nErr++;
1888 }
1889 close(connection);
1890 g.nPendingRequest = nchildren+1;
 
1891 return nErr;
1892 }
1893 }
1894 }
1895 /* Bury dead children */
@@ -1896,10 +1933,14 @@
1896 if( nchildren ){
1897 while(1){
1898 int iStatus = 0;
1899 pid_t x = waitpid(-1, &iStatus, WNOHANG);
1900 if( x<=0 ) break;
 
 
 
 
1901 nchildren--;
1902 }
1903 }
1904 }
1905 /* NOT REACHED */
@@ -1921,12 +1962,12 @@
1921
1922
1923 /*
1924 ** Returns an RFC822-formatted time string suitable for HTTP headers.
1925 ** The timezone is always GMT. The value returned is always a
1926 ** string obtained from mprintf() and must be freed using free() to
1927 ** avoid a memory leak.
1928 **
1929 ** See http://www.faqs.org/rfcs/rfc822.html, section 5
1930 ** and http://www.faqs.org/rfcs/rfc2616.html, section 3.3.
1931 */
1932 char *cgi_rfc822_datestamp(time_t now){
@@ -1933,11 +1974,11 @@
1933 struct tm *pTm;
1934 pTm = gmtime(&now);
1935 if( pTm==0 ){
1936 return mprintf("");
1937 }else{
1938 return mprintf("%s, %d %s %02d %02d:%02d:%02d GMT",
1939 azDays[pTm->tm_wday], pTm->tm_mday, azMonths[pTm->tm_mon],
1940 pTm->tm_year+1900, pTm->tm_hour, pTm->tm_min, pTm->tm_sec);
1941 }
1942 }
1943
@@ -1947,55 +1988,42 @@
1947 **
1948 ** Note that this won't handle all the _allowed_ HTTP formats, just the
1949 ** most popular one (the one generated by cgi_rfc822_datestamp(), actually).
1950 */
1951 time_t cgi_rfc822_parsedate(const char *zDate){
1952 struct tm t;
1953 char zIgnore[16];
1954 char zMonth[16];
1955
1956 memset(&t, 0, sizeof(t));
1957 if( 7==sscanf(zDate, "%12[A-Za-z,] %d %12[A-Za-z] %d %d:%d:%d", zIgnore,
1958 &t.tm_mday, zMonth, &t.tm_year, &t.tm_hour, &t.tm_min,
1959 &t.tm_sec)){
1960
1961 if( t.tm_year > 1900 ) t.tm_year -= 1900;
1962 for(t.tm_mon=0; azMonths[t.tm_mon]; t.tm_mon++){
1963 if( !fossil_strnicmp( azMonths[t.tm_mon], zMonth, 3 )){
1964 return mkgmtime(&t);
1965 }
1966 }
1967 }
1968
1969 return 0;
1970 }
1971
1972 /*
1973 ** Convert a struct tm* that represents a moment in UTC into the number
1974 ** of seconds in 1970, UTC.
1975 */
1976 time_t mkgmtime(struct tm *p){
1977 time_t t;
1978 int nDay;
1979 int isLeapYr;
1980 /* Days in each month: 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 */
1981 static int priorDays[] = { 0, 31, 59, 90,120,151,181,212,243,273,304,334 };
1982 if( p->tm_mon<0 ){
1983 int nYear = (11 - p->tm_mon)/12;
1984 p->tm_year -= nYear;
1985 p->tm_mon += nYear*12;
1986 }else if( p->tm_mon>11 ){
1987 p->tm_year += p->tm_mon/12;
1988 p->tm_mon %= 12;
1989 }
1990 isLeapYr = p->tm_year%4==0 && (p->tm_year%100!=0 || (p->tm_year+300)%400==0);
1991 p->tm_yday = priorDays[p->tm_mon] + p->tm_mday - 1;
1992 if( isLeapYr && p->tm_mon>1 ) p->tm_yday++;
1993 nDay = (p->tm_year-70)*365 + (p->tm_year-69)/4 -p->tm_year/100 +
1994 (p->tm_year+300)/400 + p->tm_yday;
1995 t = ((nDay*24 + p->tm_hour)*60 + p->tm_min)*60 + p->tm_sec;
1996 return t;
1997 }
1998
1999 /*
2000 ** Check the objectTime against the If-Modified-Since request header. If the
2001 ** object time isn't any newer than the header, we immediately send back
@@ -2026,5 +2054,14 @@
2026 return zSshClient;
2027 }
2028 }
2029 return zDefault;
2030 }
 
 
 
 
 
 
 
 
 
2031
--- src/cgi.c
+++ src/cgi.c
@@ -56,10 +56,12 @@
56 #define P(x) cgi_parameter((x),0)
57 #define PD(x,y) cgi_parameter((x),(y))
58 #define PT(x) cgi_parameter_trimmed((x),0)
59 #define PDT(x,y) cgi_parameter_trimmed((x),(y))
60 #define PB(x) cgi_parameter_boolean(x)
61 #define PCK(x) cgi_parameter_checked(x,1)
62 #define PIF(x,y) cgi_parameter_checked(x,y)
63
64
65 /*
66 ** Destinations for output text.
67 */
@@ -198,97 +200,47 @@
200 void cgi_append_header(const char *zLine){
201 blob_append(&extraHeader, zLine, -1);
202 }
203
204 /*
205 ** Set a cookie by queuing up the appropriate HTTP header output. If
206 ** !g.isHTTP, this is a no-op.
207 **
208 ** Zero lifetime implies a session cookie.
209 */
210 void cgi_set_cookie(
211 const char *zName, /* Name of the cookie */
212 const char *zValue, /* Value of the cookie. Automatically escaped */
213 const char *zPath, /* Path cookie applies to. NULL means "/" */
214 int lifetime /* Expiration of the cookie in seconds from now */
215 ){
216 char const *zSecure = "";
217 if(!g.isHTTP) return /* e.g. JSON CLI mode, where g.zTop is not set */;
218 else if( zPath==0 ){
219 zPath = g.zTop;
220 if( zPath[0]==0 ) zPath = "/";
221 }
222 if( g.zBaseURL!=0 && strncmp(g.zBaseURL, "https:", 6)==0 ){
223 zSecure = " secure;";
224 }
225 if( lifetime>0 ){
 
226 blob_appendf(&extraHeader,
227 "Set-Cookie: %s=%t; Path=%s; max-age=%d; HttpOnly;%s Version=1\r\n",
228 zName, zValue, zPath, lifetime, zSecure);
229 }else{
230 blob_appendf(&extraHeader,
231 "Set-Cookie: %s=%t; Path=%s; HttpOnly;%s Version=1\r\n",
232 zName, zValue, zPath, zSecure);
233 }
234 }
235
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236
237 /*
238 ** Return true if the response should be sent with Content-Encoding: gzip.
239 */
240 static int is_gzippable(void){
241 if( g.fNoHttpCompress ) return 0;
242 if( strstr(PD("HTTP_ACCEPT_ENCODING", ""), "gzip")==0 ) return 0;
243 return strncmp(zContentType, "text/", 5)==0
244 || sqlite3_strglob("application/*xml", zContentType)==0
245 || sqlite3_strglob("application/*javascript", zContentType)==0;
246 }
@@ -301,29 +253,32 @@
253 if( iReplyStatus<=0 ){
254 iReplyStatus = 200;
255 zReplyStatus = "OK";
256 }
257
 
 
 
 
 
 
 
 
 
 
 
258 if( g.fullHttpReply ){
259 fprintf(g.httpOut, "HTTP/1.0 %d %s\r\n", iReplyStatus, zReplyStatus);
260 fprintf(g.httpOut, "Date: %s\r\n", cgi_rfc822_datestamp(time(0)));
261 fprintf(g.httpOut, "Connection: close\r\n");
262 fprintf(g.httpOut, "X-UA-Compatible: IE=edge\r\n");
263 }else{
264 fprintf(g.httpOut, "Status: %d %s\r\n", iReplyStatus, zReplyStatus);
265 }
266 if( g.isConst ){
267 /* isConst means that the reply is guaranteed to be invariant, even
268 ** after configuration changes and/or Fossil binary recompiles. */
269 fprintf(g.httpOut, "Cache-Control: max-age=31536000\r\n");
270 }else if( etag_tag()!=0 ){
271 fprintf(g.httpOut, "ETag: %s\r\n", etag_tag());
272 fprintf(g.httpOut, "Cache-Control: max-age=%d\r\n", etag_maxage());
273 }else{
274 fprintf(g.httpOut, "Cache-control: no-cache\r\n");
275 }
276 if( etag_mtime()>0 ){
277 fprintf(g.httpOut, "Last-Modified: %s\r\n",
278 cgi_rfc822_datestamp(etag_mtime()));
279 }
280
281 if( blob_size(&extraHeader)>0 ){
282 fprintf(g.httpOut, "%s", blob_buffer(&extraHeader));
283 }
284
@@ -343,24 +298,10 @@
298 **
299 ** These headers are probably best added by the web server hosting fossil as
300 ** a CGI script.
301 */
302
 
 
 
 
 
 
 
 
 
 
 
 
 
 
303 /* Content intended for logged in users should only be cached in
304 ** the browser, not some shared location.
305 */
306 fprintf(g.httpOut, "Content-Type: %s; charset=utf-8\r\n", zContentType);
307 if( fossil_strcmp(zContentType,"application/x-fossil")==0 ){
@@ -398,10 +339,18 @@
339 }
340 }
341 }
342 fflush(g.httpOut);
343 CGIDEBUG(("DONE\n"));
344
345 /* After the webpage has been sent, do any useful background
346 ** processing.
347 */
348 if( g.db!=0 && db_repository_has_changed() ){
349 if( g.fAnyTrace ) fprintf(stderr, "-- repository changes have occurred\n");
350 backoffice_run();
351 }
352 }
353
354 /*
355 ** Do a redirect request to the URL given in the argument.
356 **
@@ -456,10 +405,33 @@
405 zRef = P("HTTP_REFERER");
406 if( zRef==0 ) zRef = zDefault;
407 }
408 return zRef;
409 }
410
411 /*
412 ** Return true if the current request appears to be safe from a
413 ** Cross-Site Request Forgery (CSRF) attack. Conditions that must
414 ** be met:
415 **
416 ** * The HTTP_REFERER must have the same origin
417 ** * The REQUEST_METHOD must be POST - or requirePost==0
418 */
419 int cgi_csrf_safe(int requirePost){
420 const char *zRef = P("HTTP_REFERER");
421 int nBase;
422 if( zRef==0 ) return 0;
423 if( requirePost ){
424 const char *zMethod = P("REQUEST_METHOD");
425 if( zMethod==0 ) return 0;
426 if( strcmp(zMethod,"POST")!=0 ) return 0;
427 }
428 nBase = (int)strlen(g.zBaseURL);
429 if( strncmp(g.zBaseURL,zRef,nBase)!=0 ) return 0;
430 if( zRef[nBase]!=0 && zRef[nBase]!='/' ) return 0;
431 return 1;
432 }
433
434 /*
435 ** Information about all query parameters and cookies are stored
436 ** in these variables.
437 */
@@ -596,10 +568,15 @@
568 ** are ignored.
569 **
570 ** * it is impossible for a cookie or query parameter to
571 ** override the value of an environment variable since
572 ** environment variables always have uppercase names.
573 **
574 ** 2018-03-29: Also ignore the entry if NAME that contains any characters
575 ** other than [a-zA-Z0-9_]. There are no known exploits involving unusual
576 ** names that contain characters outside that set, but it never hurts to
577 ** be extra cautious when sanitizing inputs.
578 **
579 ** Parameters are separated by the "terminator" character. Whitespace
580 ** before the NAME is ignored.
581 **
582 ** The input string "z" is modified but no copies is made. "z"
@@ -626,11 +603,11 @@
603 dehttpize(zValue);
604 }else{
605 if( *z ){ *z++ = 0; }
606 zValue = "";
607 }
608 if( fossil_islower(zName[0]) && fossil_no_strange_characters(zName+1) ){
609 cgi_set_parameter_nocopy(zName, zValue, isQP);
610 }
611 #ifdef FOSSIL_ENABLE_JSON
612 json_setenv( zName, cson_value_new_string(zValue,strlen(zValue)) );
613 #endif /* FOSSIL_ENABLE_JSON */
@@ -912,13 +889,17 @@
889 pLog = 0;
890 return;
891 }
892 if( pLog==0 ){
893 char zFile[50];
894 #if defined(_WIN32)
895 unsigned r;
896 sqlite3_randomness(sizeof(r), &r);
897 sqlite3_snprintf(sizeof(zFile), zFile, "httplog-%08x.txt", r);
898 #else
899 sqlite3_snprintf(sizeof(zFile), zFile, "httplog-%05d.txt", getpid());
900 #endif
901 pLog = fossil_fopen(zFile, "wb");
902 if( pLog ){
903 fprintf(stderr, "# open log on %s\n", zFile);
904 }else{
905 fprintf(stderr, "# failed to open %s\n", zFile);
@@ -1137,10 +1118,11 @@
1118 const char *zIn;
1119 char *zOut;
1120 int i;
1121 zIn = cgi_parameter(zName, 0);
1122 if( zIn==0 ) zIn = zDefault;
1123 if( zIn==0 ) return 0;
1124 while( fossil_isspace(zIn[0]) ) zIn++;
1125 zOut = fossil_strdup(zIn);
1126 for(i=0; zOut[i]; i++){}
1127 while( i>0 && fossil_isspace(zOut[i-1]) ) zOut[--i] = 0;
1128 return zOut;
@@ -1153,10 +1135,33 @@
1135 int cgi_parameter_boolean(const char *zName){
1136 const char *zIn = cgi_parameter(zName, 0);
1137 if( zIn==0 ) return 0;
1138 return zIn[0]==0 || is_truth(zIn);
1139 }
1140
1141 /*
1142 ** Return either an empty string "" or the string "checked" depending
1143 ** on whether or not parameter zName has value iValue. If parameter
1144 ** zName does not exist, that is assumed to be the same as value 0.
1145 **
1146 ** This routine implements the PCK(x) and PIF(x,y) macros. The PIF(x,y)
1147 ** macro generateds " checked" if the value of parameter x equals integer y.
1148 ** PCK(x) is the same as PIF(x,1). These macros are used to generate
1149 ** the "checked" attribute on checkbox and radio controls of forms.
1150 */
1151 const char *cgi_parameter_checked(const char *zName, int iValue){
1152 const char *zIn = cgi_parameter(zName,0);
1153 int x;
1154 if( zIn==0 ){
1155 x = 0;
1156 }else if( !fossil_isdigit(zIn[0]) ){
1157 x = is_truth(zIn);
1158 }else{
1159 x = atoi(zIn);
1160 }
1161 return x==iValue ? "checked" : "";
1162 }
1163
1164 /*
1165 ** Return the name of the i-th CGI parameter. Return NULL if there
1166 ** are fewer than i registered CGI parameters.
1167 */
@@ -1217,20 +1222,24 @@
1222 ** Print all query parameters on standard output. Format the
1223 ** parameters as HTML. This is used for testing and debugging.
1224 **
1225 ** Omit the values of the cookies unless showAll is true.
1226 */
1227 void cgi_print_all(int showAll, int onConsole){
1228 int i;
1229 cgi_parameter("",""); /* Force the parameters into sorted order */
1230 for(i=0; i<nUsedQP; i++){
1231 const char *zName = aParamQP[i].zName;
1232 if( !showAll ){
1233 if( fossil_stricmp("HTTP_COOKIE",zName)==0 ) continue;
1234 if( fossil_strnicmp("fossil-",zName,7)==0 ) continue;
1235 }
1236 if( onConsole ){
1237 fossil_trace("%s = %s\n", zName, aParamQP[i].zValue);
1238 }else{
1239 cgi_printf("%h = %h <br />\n", zName, aParamQP[i].zValue);
1240 }
1241 }
1242 }
1243
1244 /*
1245 ** Export all untagged query parameters (but not cookies or environment
@@ -1343,12 +1352,14 @@
1352 ** NULL pointer to stick with the IP address previously computed and
1353 ** loaded into g.zIpAddr.
1354 */
1355 static const char *cgi_accept_forwarded_for(const char *z){
1356 int i;
1357 if( !cgi_is_loopback(g.zIpAddr) ){
1358 /* Only accept X-FORWARDED-FOR if input coming from the local machine */
1359 return 0;
1360 }
1361 i = strlen(z)-1;
1362 while( i>=0 && z[i]!=',' && !fossil_isspace(z[i]) ) i--;
1363 return &z[++i];
1364 }
1365
@@ -1372,10 +1383,36 @@
1383 while( fossil_isspace(*zInput) ){ zInput++; }
1384 }
1385 if( zLeftOver ){ *zLeftOver = zInput; }
1386 return zResult;
1387 }
1388
1389 /*
1390 ** Determine the IP address on the other side of a connection.
1391 ** Return a pointer to a string. Or return 0 if unable.
1392 **
1393 ** The string is held in a static buffer that is overwritten on
1394 ** each call.
1395 */
1396 char *cgi_remote_ip(int fd){
1397 #if 0
1398 static char zIp[100];
1399 struct sockaddr_in6 addr;
1400 socklen_t sz = sizeof(addr);
1401 if( getpeername(fd, &addr, &sz) ) return 0;
1402 zIp[0] = 0;
1403 if( inet_ntop(AF_INET6, &addr, zIp, sizeof(zIp))==0 ){
1404 return 0;
1405 }
1406 return zIp;
1407 #else
1408 struct sockaddr_in remoteName;
1409 socklen_t size = sizeof(struct sockaddr_in);
1410 if( getpeername(fd, (struct sockaddr*)&remoteName, &size) ) return 0;
1411 return inet_ntoa(remoteName.sin_addr);
1412 #endif
1413 }
1414
1415 /*
1416 ** This routine handles a single HTTP request which is coming in on
1417 ** g.httpIn and which replies on g.httpOut
1418 **
@@ -1386,12 +1423,10 @@
1423 ** and subsequent code handles the actual generation of the webpage.
1424 */
1425 void cgi_handle_http_request(const char *zIpAddr){
1426 char *z, *zToken;
1427 int i;
 
 
1428 char zLine[2000]; /* A single line of input. */
1429 g.fullHttpReply = 1;
1430 if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){
1431 malformed_request("missing HTTP header");
1432 }
@@ -1415,15 +1450,12 @@
1450 cgi_setenv("SCRIPT_NAME", "");
1451 for(i=0; zToken[i] && zToken[i]!='?'; i++){}
1452 if( zToken[i] ) zToken[i++] = 0;
1453 cgi_setenv("PATH_INFO", zToken);
1454 cgi_setenv("QUERY_STRING", &zToken[i]);
1455 if( zIpAddr==0 ){
1456 zIpAddr = cgi_remote_ip(fileno(g.httpIn));
 
 
 
1457 }
1458 if( zIpAddr ){
1459 cgi_setenv("REMOTE_ADDR", zIpAddr);
1460 g.zIpAddr = mprintf("%s", zIpAddr);
1461 }
@@ -1782,10 +1814,11 @@
1814 /* Use win32_http_server() instead */
1815 fossil_exit(1);
1816 #else
1817 int listener = -1; /* The server socket */
1818 int connection; /* A socket for each individual connection */
1819 int nRequest = 0; /* Number of requests handled so far */
1820 fd_set readfds; /* Set of file descriptors for select() */
1821 socklen_t lenaddr; /* Length of the inaddr structure */
1822 int child; /* PID of the child process */
1823 int nchildren = 0; /* Number of child processes */
1824 struct timeval delay; /* How long to wait inside select() */
@@ -1869,27 +1902,31 @@
1902 lenaddr = sizeof(inaddr);
1903 connection = accept(listener, (struct sockaddr*)&inaddr, &lenaddr);
1904 if( connection>=0 ){
1905 child = fork();
1906 if( child!=0 ){
1907 if( child>0 ){
1908 nchildren++;
1909 nRequest++;
1910 }
1911 close(connection);
1912 }else{
1913 int nErr = 0, fd;
1914 close(0);
1915 fd = dup(connection);
1916 if( fd!=0 ) nErr++;
1917 close(1);
1918 fd = dup(connection);
1919 if( fd!=1 ) nErr++;
1920 if( 0 && !g.fAnyTrace ){
1921 close(2);
1922 fd = dup(connection);
1923 if( fd!=2 ) nErr++;
1924 }
1925 close(connection);
1926 g.nPendingRequest = nchildren+1;
1927 g.nRequest = nRequest+1;
1928 return nErr;
1929 }
1930 }
1931 }
1932 /* Bury dead children */
@@ -1896,10 +1933,14 @@
1933 if( nchildren ){
1934 while(1){
1935 int iStatus = 0;
1936 pid_t x = waitpid(-1, &iStatus, WNOHANG);
1937 if( x<=0 ) break;
1938 if( WIFSIGNALED(iStatus) && g.fAnyTrace ){
1939 fprintf(stderr, "/***** Child %d exited on signal %d (%s) *****/\n",
1940 x, WTERMSIG(iStatus), strsignal(WTERMSIG(iStatus)));
1941 }
1942 nchildren--;
1943 }
1944 }
1945 }
1946 /* NOT REACHED */
@@ -1921,12 +1962,12 @@
1962
1963
1964 /*
1965 ** Returns an RFC822-formatted time string suitable for HTTP headers.
1966 ** The timezone is always GMT. The value returned is always a
1967 ** string obtained from mprintf() and must be freed using fossil_free()
1968 ** to avoid a memory leak.
1969 **
1970 ** See http://www.faqs.org/rfcs/rfc822.html, section 5
1971 ** and http://www.faqs.org/rfcs/rfc2616.html, section 3.3.
1972 */
1973 char *cgi_rfc822_datestamp(time_t now){
@@ -1933,11 +1974,11 @@
1974 struct tm *pTm;
1975 pTm = gmtime(&now);
1976 if( pTm==0 ){
1977 return mprintf("");
1978 }else{
1979 return mprintf("%s, %d %s %02d %02d:%02d:%02d +0000",
1980 azDays[pTm->tm_wday], pTm->tm_mday, azMonths[pTm->tm_mon],
1981 pTm->tm_year+1900, pTm->tm_hour, pTm->tm_min, pTm->tm_sec);
1982 }
1983 }
1984
@@ -1947,55 +1988,42 @@
1988 **
1989 ** Note that this won't handle all the _allowed_ HTTP formats, just the
1990 ** most popular one (the one generated by cgi_rfc822_datestamp(), actually).
1991 */
1992 time_t cgi_rfc822_parsedate(const char *zDate){
1993 int mday, mon, year, yday, hour, min, sec;
1994 char zIgnore[4];
1995 char zMonth[4];
1996 static const char *const azMonths[] =
1997 {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
1998 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", 0};
1999 if( 7==sscanf(zDate, "%3[A-Za-z], %d %3[A-Za-z] %d %d:%d:%d", zIgnore,
2000 &mday, zMonth, &year, &hour, &min, &sec)){
2001 if( year > 1900 ) year -= 1900;
2002 for(mon=0; azMonths[mon]; mon++){
2003 if( !strncmp( azMonths[mon], zMonth, 3 )){
2004 int nDay;
2005 int isLeapYr;
2006 static int priorDays[] =
2007 { 0, 31, 59, 90,120,151,181,212,243,273,304,334 };
2008 if( mon<0 ){
2009 int nYear = (11 - mon)/12;
2010 year -= nYear;
2011 mon += nYear*12;
2012 }else if( mon>11 ){
2013 year += mon/12;
2014 mon %= 12;
2015 }
2016 isLeapYr = year%4==0 && (year%100!=0 || (year+300)%400==0);
2017 yday = priorDays[mon] + mday - 1;
2018 if( isLeapYr && mon>1 ) yday++;
2019 nDay = (year-70)*365 + (year-69)/4 - year/100 + (year+300)/400 + yday;
2020 return ((time_t)(nDay*24 + hour)*60 + min)*60 + sec;
2021 }
2022 }
2023 }
2024 return 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
2025 }
2026
2027 /*
2028 ** Check the objectTime against the If-Modified-Since request header. If the
2029 ** object time isn't any newer than the header, we immediately send back
@@ -2026,5 +2054,14 @@
2054 return zSshClient;
2055 }
2056 }
2057 return zDefault;
2058 }
2059
2060 /*
2061 ** Return true if information is coming from the loopback network.
2062 */
2063 int cgi_is_loopback(const char *zIpAddr){
2064 return fossil_strcmp(zIpAddr, "127.0.0.1")==0 ||
2065 fossil_strcmp(zIpAddr, "::ffff:127.0.0.1")==0 ||
2066 fossil_strcmp(zIpAddr, "::1")==0;
2067 }
2068
+16 -11
--- src/checkin.c
+++ src/checkin.c
@@ -472,10 +472,12 @@
472472
const char *zIgnoreFlag = find_option("ignore", 0, 1);
473473
unsigned scanFlags = 0;
474474
unsigned flags = 0;
475475
int vid, i;
476476
477
+ fossil_pledge("stdio rpath wpath cpath fattr id flock tty chown");
478
+
477479
/* Load affirmative flag options. */
478480
for( i=0; i<count(flagDefs); ++i ){
479481
if( (command==CHANGES || !(flagDefs[i].mask & C_CLASSIFY))
480482
&& find_option(flagDefs[i].option, 0, 0) ){
481483
flags |= flagDefs[i].mask;
@@ -555,12 +557,13 @@
555557
/* Find and print all requested changes. */
556558
blob_zero(&report);
557559
status_report(&report, flags);
558560
if( blob_size(&report) ){
559561
if( showHdr ){
560
- fossil_print("Changes for %s at %s:\n", db_get("project-name", "???"),
561
- g.zLocalRoot);
562
+ fossil_print(
563
+ "Changes for %s at %s:\n", db_get("project-name", "<unnamed>"),
564
+ g.zLocalRoot);
562565
}
563566
blob_write_to_file(&report, "-");
564567
}else if( verboseFlag ){
565568
fossil_print(" (none)\n");
566569
}
@@ -863,11 +866,11 @@
863866
864867
blob_zero(&report);
865868
status_report(&report, flags);
866869
if( blob_size(&report) ){
867870
if( showHdr ){
868
- fossil_print("Extras for %s at %s:\n", db_get("project-name","???"),
871
+ fossil_print("Extras for %s at %s:\n", db_get("project-name","<unnamed>"),
869872
g.zLocalRoot);
870873
}
871874
blob_write_to_file(&report, "-");
872875
}
873876
blob_reset(&report);
@@ -1189,17 +1192,19 @@
11891192
blob_add_cr(pPrompt);
11901193
#endif
11911194
}
11921195
#endif
11931196
if( zEditor==0 ){
1194
- blob_append(pPrompt,
1195
- "#\n"
1196
- "# Since no default text editor is set using EDITOR or VISUAL\n"
1197
- "# environment variables or the \"fossil set editor\" command,\n"
1198
- "# and because no comment was specified using the \"-m\" or \"-M\"\n"
1199
- "# command-line options, you will need to enter the comment below.\n"
1200
- "# Type \".\" on a line by itself when you are done:\n", -1);
1197
+ if( blob_size(pPrompt)>0 ){
1198
+ blob_append(pPrompt,
1199
+ "#\n"
1200
+ "# Since no default text editor is set using EDITOR or VISUAL\n"
1201
+ "# environment variables or the \"fossil set editor\" command,\n"
1202
+ "# and because no comment was specified using the \"-m\" or \"-M\"\n"
1203
+ "# command-line options, you will need to enter the comment below.\n"
1204
+ "# Type \".\" on a line by itself when you are done:\n", -1);
1205
+ }
12011206
zFile = mprintf("-");
12021207
}else{
12031208
Blob fname;
12041209
blob_zero(&fname);
12051210
if( g.zLocalRoot!=0 ){
@@ -1213,11 +1218,11 @@
12131218
blob_reset(&fname);
12141219
}
12151220
#if defined(_WIN32)
12161221
blob_add_cr(pPrompt);
12171222
#endif
1218
- blob_write_to_file(pPrompt, zFile);
1223
+ if( blob_size(pPrompt)>0 ) blob_write_to_file(pPrompt, zFile);
12191224
if( zEditor ){
12201225
zCmd = mprintf("%s \"%s\"", zEditor, zFile);
12211226
fossil_print("%s\n", zCmd);
12221227
if( fossil_system(zCmd) ){
12231228
fossil_fatal("editor aborted: \"%s\"", zCmd);
12241229
--- src/checkin.c
+++ src/checkin.c
@@ -472,10 +472,12 @@
472 const char *zIgnoreFlag = find_option("ignore", 0, 1);
473 unsigned scanFlags = 0;
474 unsigned flags = 0;
475 int vid, i;
476
 
 
477 /* Load affirmative flag options. */
478 for( i=0; i<count(flagDefs); ++i ){
479 if( (command==CHANGES || !(flagDefs[i].mask & C_CLASSIFY))
480 && find_option(flagDefs[i].option, 0, 0) ){
481 flags |= flagDefs[i].mask;
@@ -555,12 +557,13 @@
555 /* Find and print all requested changes. */
556 blob_zero(&report);
557 status_report(&report, flags);
558 if( blob_size(&report) ){
559 if( showHdr ){
560 fossil_print("Changes for %s at %s:\n", db_get("project-name", "???"),
561 g.zLocalRoot);
 
562 }
563 blob_write_to_file(&report, "-");
564 }else if( verboseFlag ){
565 fossil_print(" (none)\n");
566 }
@@ -863,11 +866,11 @@
863
864 blob_zero(&report);
865 status_report(&report, flags);
866 if( blob_size(&report) ){
867 if( showHdr ){
868 fossil_print("Extras for %s at %s:\n", db_get("project-name","???"),
869 g.zLocalRoot);
870 }
871 blob_write_to_file(&report, "-");
872 }
873 blob_reset(&report);
@@ -1189,17 +1192,19 @@
1189 blob_add_cr(pPrompt);
1190 #endif
1191 }
1192 #endif
1193 if( zEditor==0 ){
1194 blob_append(pPrompt,
1195 "#\n"
1196 "# Since no default text editor is set using EDITOR or VISUAL\n"
1197 "# environment variables or the \"fossil set editor\" command,\n"
1198 "# and because no comment was specified using the \"-m\" or \"-M\"\n"
1199 "# command-line options, you will need to enter the comment below.\n"
1200 "# Type \".\" on a line by itself when you are done:\n", -1);
 
 
1201 zFile = mprintf("-");
1202 }else{
1203 Blob fname;
1204 blob_zero(&fname);
1205 if( g.zLocalRoot!=0 ){
@@ -1213,11 +1218,11 @@
1213 blob_reset(&fname);
1214 }
1215 #if defined(_WIN32)
1216 blob_add_cr(pPrompt);
1217 #endif
1218 blob_write_to_file(pPrompt, zFile);
1219 if( zEditor ){
1220 zCmd = mprintf("%s \"%s\"", zEditor, zFile);
1221 fossil_print("%s\n", zCmd);
1222 if( fossil_system(zCmd) ){
1223 fossil_fatal("editor aborted: \"%s\"", zCmd);
1224
--- src/checkin.c
+++ src/checkin.c
@@ -472,10 +472,12 @@
472 const char *zIgnoreFlag = find_option("ignore", 0, 1);
473 unsigned scanFlags = 0;
474 unsigned flags = 0;
475 int vid, i;
476
477 fossil_pledge("stdio rpath wpath cpath fattr id flock tty chown");
478
479 /* Load affirmative flag options. */
480 for( i=0; i<count(flagDefs); ++i ){
481 if( (command==CHANGES || !(flagDefs[i].mask & C_CLASSIFY))
482 && find_option(flagDefs[i].option, 0, 0) ){
483 flags |= flagDefs[i].mask;
@@ -555,12 +557,13 @@
557 /* Find and print all requested changes. */
558 blob_zero(&report);
559 status_report(&report, flags);
560 if( blob_size(&report) ){
561 if( showHdr ){
562 fossil_print(
563 "Changes for %s at %s:\n", db_get("project-name", "<unnamed>"),
564 g.zLocalRoot);
565 }
566 blob_write_to_file(&report, "-");
567 }else if( verboseFlag ){
568 fossil_print(" (none)\n");
569 }
@@ -863,11 +866,11 @@
866
867 blob_zero(&report);
868 status_report(&report, flags);
869 if( blob_size(&report) ){
870 if( showHdr ){
871 fossil_print("Extras for %s at %s:\n", db_get("project-name","<unnamed>"),
872 g.zLocalRoot);
873 }
874 blob_write_to_file(&report, "-");
875 }
876 blob_reset(&report);
@@ -1189,17 +1192,19 @@
1192 blob_add_cr(pPrompt);
1193 #endif
1194 }
1195 #endif
1196 if( zEditor==0 ){
1197 if( blob_size(pPrompt)>0 ){
1198 blob_append(pPrompt,
1199 "#\n"
1200 "# Since no default text editor is set using EDITOR or VISUAL\n"
1201 "# environment variables or the \"fossil set editor\" command,\n"
1202 "# and because no comment was specified using the \"-m\" or \"-M\"\n"
1203 "# command-line options, you will need to enter the comment below.\n"
1204 "# Type \".\" on a line by itself when you are done:\n", -1);
1205 }
1206 zFile = mprintf("-");
1207 }else{
1208 Blob fname;
1209 blob_zero(&fname);
1210 if( g.zLocalRoot!=0 ){
@@ -1213,11 +1218,11 @@
1218 blob_reset(&fname);
1219 }
1220 #if defined(_WIN32)
1221 blob_add_cr(pPrompt);
1222 #endif
1223 if( blob_size(pPrompt)>0 ) blob_write_to_file(pPrompt, zFile);
1224 if( zEditor ){
1225 zCmd = mprintf("%s \"%s\"", zEditor, zFile);
1226 fossil_print("%s\n", zCmd);
1227 if( fossil_system(zCmd) ){
1228 fossil_fatal("editor aborted: \"%s\"", zCmd);
1229
--- src/checkout.c
+++ src/checkout.c
@@ -291,10 +291,11 @@
291291
}else{
292292
zVers = g.argv[2];
293293
}
294294
vid = load_vfile(zVers, forceMissingFlag);
295295
if( prior==vid ){
296
+ db_end_transaction(0);
296297
return;
297298
}
298299
if( !keepFlag ){
299300
uncheckout(prior);
300301
}
301302
--- src/checkout.c
+++ src/checkout.c
@@ -291,10 +291,11 @@
291 }else{
292 zVers = g.argv[2];
293 }
294 vid = load_vfile(zVers, forceMissingFlag);
295 if( prior==vid ){
 
296 return;
297 }
298 if( !keepFlag ){
299 uncheckout(prior);
300 }
301
--- src/checkout.c
+++ src/checkout.c
@@ -291,10 +291,11 @@
291 }else{
292 zVers = g.argv[2];
293 }
294 vid = load_vfile(zVers, forceMissingFlag);
295 if( prior==vid ){
296 db_end_transaction(0);
297 return;
298 }
299 if( !keepFlag ){
300 uncheckout(prior);
301 }
302
+12 -5
--- src/clone.c
+++ src/clone.c
@@ -109,10 +109,11 @@
109109
** admin user. This can be overridden using the -A|--admin-user
110110
** parameter.
111111
**
112112
** Options:
113113
** --admin-user|-A USERNAME Make USERNAME the administrator
114
+** --nocompress Omit extra delta compression
114115
** --once Don't remember the URI.
115116
** --private Also clone private branches
116117
** --ssl-identity FILENAME Use the SSL identity if requested by the server
117118
** --ssh-command|-c SSH Use SSH as the "ssh" command
118119
** --httpauth|-B USER:PASS Add HTTP Basic Authorization to requests
@@ -126,10 +127,11 @@
126127
const char *zDefaultUser; /* Optional name of the default user */
127128
const char *zHttpAuth; /* HTTP Authorization user:pass information */
128129
int nErr = 0;
129130
int urlFlags = URL_PROMPT_PW | URL_REMEMBER;
130131
int syncFlags = SYNC_CLONE;
132
+ int noCompress = find_option("nocompress",0,0)!=0;
131133
132134
/* Also clone private branches */
133135
if( find_option("private",0,0)!=0 ) syncFlags |= SYNC_PRIVATE;
134136
if( find_option("once",0,0)!=0) urlFlags &= ~URL_REMEMBER;
135137
if( find_option("verbose","v",0)!=0) syncFlags |= SYNC_VERBOSE;
@@ -211,14 +213,17 @@
211213
db_open_repository(g.argv[3]);
212214
}
213215
db_begin_transaction();
214216
fossil_print("Rebuilding repository meta-data...\n");
215217
rebuild_db(0, 1, 0);
216
- fossil_print("Extra delta compression... "); fflush(stdout);
217
- extra_deltification();
218
+ if( !noCompress ){
219
+ fossil_print("Extra delta compression... "); fflush(stdout);
220
+ extra_deltification();
221
+ fossil_print("\n");
222
+ }
218223
db_end_transaction(0);
219
- fossil_print("\nVacuuming the database... "); fflush(stdout);
224
+ fossil_print("Vacuuming the database... "); fflush(stdout);
220225
if( db_int(0, "PRAGMA page_count")>1000
221226
&& db_int(0, "PRAGMA page_size")<8192 ){
222227
db_multi_exec("PRAGMA page_size=8192;");
223228
}
224229
db_multi_exec("VACUUM");
@@ -317,14 +322,16 @@
317322
@ you "Download Zip" privileges.
318323
}
319324
}else{
320325
const char *zDLTag = db_get("download-tag","trunk");
321326
const char *zNm = db_get("short-project-name","download");
322
- char *zUrl = href("%R/zip/%t.zip?uuid=%t", zNm, zDLTag);
327
+ char *zUrl = href("%R/zip/%t/%t.zip", zDLTag, zNm);
323328
@ <p>ZIP Archive: %z(zUrl)%h(zNm).zip</a>
324
- zUrl = href("%R/tarball/%t.tar.gz?uuid=%t", zNm, zDLTag);
329
+ zUrl = href("%R/tarball/%t/%t.tar.gz", zDLTag, zNm);
325330
@ <p>Tarball: %z(zUrl)%h(zNm).tar.gz</a>
331
+ zUrl = href("%R/sqlar/%t/%t.sqlar", zDLTag, zNm);
332
+ @ <p>SQLite Archive: %z(zUrl)%h(zNm).sqlar</a>
326333
}
327334
if( !g.perm.Clone ){
328335
@ <p>You are not authorized to clone this repository.
329336
if( g.zLogin==0 || g.zLogin[0]==0 ){
330337
@ Maybe you would be able to clone if you
331338
--- src/clone.c
+++ src/clone.c
@@ -109,10 +109,11 @@
109 ** admin user. This can be overridden using the -A|--admin-user
110 ** parameter.
111 **
112 ** Options:
113 ** --admin-user|-A USERNAME Make USERNAME the administrator
 
114 ** --once Don't remember the URI.
115 ** --private Also clone private branches
116 ** --ssl-identity FILENAME Use the SSL identity if requested by the server
117 ** --ssh-command|-c SSH Use SSH as the "ssh" command
118 ** --httpauth|-B USER:PASS Add HTTP Basic Authorization to requests
@@ -126,10 +127,11 @@
126 const char *zDefaultUser; /* Optional name of the default user */
127 const char *zHttpAuth; /* HTTP Authorization user:pass information */
128 int nErr = 0;
129 int urlFlags = URL_PROMPT_PW | URL_REMEMBER;
130 int syncFlags = SYNC_CLONE;
 
131
132 /* Also clone private branches */
133 if( find_option("private",0,0)!=0 ) syncFlags |= SYNC_PRIVATE;
134 if( find_option("once",0,0)!=0) urlFlags &= ~URL_REMEMBER;
135 if( find_option("verbose","v",0)!=0) syncFlags |= SYNC_VERBOSE;
@@ -211,14 +213,17 @@
211 db_open_repository(g.argv[3]);
212 }
213 db_begin_transaction();
214 fossil_print("Rebuilding repository meta-data...\n");
215 rebuild_db(0, 1, 0);
216 fossil_print("Extra delta compression... "); fflush(stdout);
217 extra_deltification();
 
 
 
218 db_end_transaction(0);
219 fossil_print("\nVacuuming the database... "); fflush(stdout);
220 if( db_int(0, "PRAGMA page_count")>1000
221 && db_int(0, "PRAGMA page_size")<8192 ){
222 db_multi_exec("PRAGMA page_size=8192;");
223 }
224 db_multi_exec("VACUUM");
@@ -317,14 +322,16 @@
317 @ you "Download Zip" privileges.
318 }
319 }else{
320 const char *zDLTag = db_get("download-tag","trunk");
321 const char *zNm = db_get("short-project-name","download");
322 char *zUrl = href("%R/zip/%t.zip?uuid=%t", zNm, zDLTag);
323 @ <p>ZIP Archive: %z(zUrl)%h(zNm).zip</a>
324 zUrl = href("%R/tarball/%t.tar.gz?uuid=%t", zNm, zDLTag);
325 @ <p>Tarball: %z(zUrl)%h(zNm).tar.gz</a>
 
 
326 }
327 if( !g.perm.Clone ){
328 @ <p>You are not authorized to clone this repository.
329 if( g.zLogin==0 || g.zLogin[0]==0 ){
330 @ Maybe you would be able to clone if you
331
--- src/clone.c
+++ src/clone.c
@@ -109,10 +109,11 @@
109 ** admin user. This can be overridden using the -A|--admin-user
110 ** parameter.
111 **
112 ** Options:
113 ** --admin-user|-A USERNAME Make USERNAME the administrator
114 ** --nocompress Omit extra delta compression
115 ** --once Don't remember the URI.
116 ** --private Also clone private branches
117 ** --ssl-identity FILENAME Use the SSL identity if requested by the server
118 ** --ssh-command|-c SSH Use SSH as the "ssh" command
119 ** --httpauth|-B USER:PASS Add HTTP Basic Authorization to requests
@@ -126,10 +127,11 @@
127 const char *zDefaultUser; /* Optional name of the default user */
128 const char *zHttpAuth; /* HTTP Authorization user:pass information */
129 int nErr = 0;
130 int urlFlags = URL_PROMPT_PW | URL_REMEMBER;
131 int syncFlags = SYNC_CLONE;
132 int noCompress = find_option("nocompress",0,0)!=0;
133
134 /* Also clone private branches */
135 if( find_option("private",0,0)!=0 ) syncFlags |= SYNC_PRIVATE;
136 if( find_option("once",0,0)!=0) urlFlags &= ~URL_REMEMBER;
137 if( find_option("verbose","v",0)!=0) syncFlags |= SYNC_VERBOSE;
@@ -211,14 +213,17 @@
213 db_open_repository(g.argv[3]);
214 }
215 db_begin_transaction();
216 fossil_print("Rebuilding repository meta-data...\n");
217 rebuild_db(0, 1, 0);
218 if( !noCompress ){
219 fossil_print("Extra delta compression... "); fflush(stdout);
220 extra_deltification();
221 fossil_print("\n");
222 }
223 db_end_transaction(0);
224 fossil_print("Vacuuming the database... "); fflush(stdout);
225 if( db_int(0, "PRAGMA page_count")>1000
226 && db_int(0, "PRAGMA page_size")<8192 ){
227 db_multi_exec("PRAGMA page_size=8192;");
228 }
229 db_multi_exec("VACUUM");
@@ -317,14 +322,16 @@
322 @ you "Download Zip" privileges.
323 }
324 }else{
325 const char *zDLTag = db_get("download-tag","trunk");
326 const char *zNm = db_get("short-project-name","download");
327 char *zUrl = href("%R/zip/%t/%t.zip", zDLTag, zNm);
328 @ <p>ZIP Archive: %z(zUrl)%h(zNm).zip</a>
329 zUrl = href("%R/tarball/%t/%t.tar.gz", zDLTag, zNm);
330 @ <p>Tarball: %z(zUrl)%h(zNm).tar.gz</a>
331 zUrl = href("%R/sqlar/%t/%t.sqlar", zDLTag, zNm);
332 @ <p>SQLite Archive: %z(zUrl)%h(zNm).sqlar</a>
333 }
334 if( !g.perm.Clone ){
335 @ <p>You are not authorized to clone this repository.
336 if( g.zLogin==0 || g.zLogin[0]==0 ){
337 @ Maybe you would be able to clone if you
338
+124 -46
--- src/codecheck1.c
+++ src/codecheck1.c
@@ -39,10 +39,15 @@
3939
#include <stdlib.h>
4040
#include <ctype.h>
4141
#include <string.h>
4242
#include <assert.h>
4343
44
+/*
45
+** Debugging switch
46
+*/
47
+static int eVerbose = 0;
48
+
4449
/*
4550
** Malloc, aborting if it fails.
4651
*/
4752
void *safe_malloc(int nByte){
4853
void *x = malloc(nByte);
@@ -141,10 +146,14 @@
141146
(*pLN)++;
142147
i++;
143148
}
144149
*pType = TK_SPACE;
145150
return i;
151
+ }
152
+ if( z[0]=='\\' && (z[1]=='\n' || (z[1]=='\r' && z[2]=='\n')) ){
153
+ *pType = TK_SPACE;
154
+ return 1;
146155
}
147156
*pType = TK_OTHER;
148157
return 1;
149158
}
150159
@@ -194,17 +203,40 @@
194203
*/
195204
static const char *skip_space(const char *z){
196205
while( isspace(z[0]) ){ z++; }
197206
return z;
198207
}
208
+
209
+/*
210
+** Remove excess whitespace and nested "()" from string z.
211
+*/
212
+static char *simplify_expr(char *z){
213
+ int n = (int)strlen(z);
214
+ while( n>0 ){
215
+ if( isspace(z[0]) ){
216
+ z++;
217
+ n--;
218
+ continue;
219
+ }
220
+ if( z[0]=='(' && z[n-1]==')' ){
221
+ z++;
222
+ n -= 2;
223
+ continue;
224
+ }
225
+ break;
226
+ }
227
+ z[n] = 0;
228
+ return z;
229
+}
199230
200231
/*
201232
** Return true if the input is a string literal.
202233
*/
203234
static int is_string_lit(const char *z){
204235
int nu1, nu2;
205236
z = next_non_whitespace(z, &nu1, &nu2);
237
+ if( strcmp(z, "NULL")==0 ) return 1;
206238
return z[0]=='"';
207239
}
208240
209241
/*
210242
** Return true if the input is an expression of string literals:
@@ -263,11 +295,11 @@
263295
264296
/*
265297
** Return true if the input is an argument that is safe to use with %s
266298
** while building an SQL statement.
267299
*/
268
-static int is_s_safe(const char *z){
300
+static int is_sql_safe(const char *z){
269301
int len, eType;
270302
int i;
271303
272304
/* A string literal is safe for use with %s */
273305
if( is_string_lit(z) ) return 1;
@@ -292,15 +324,33 @@
292324
** let it through */
293325
if( strstr(z, "/*safe-for-%s*/")!=0 ) return 1;
294326
295327
return 0;
296328
}
329
+
330
+/*
331
+** Return true if the input is an argument that is never safe for use
332
+** with %s.
333
+*/
334
+static int never_safe(const char *z){
335
+ if( strstr(z,"/*safe-for-%s*/")!=0 ) return 0;
336
+ if( z[0]=='P' ){
337
+ if( strncmp(z,"PIF(",4)==0 ) return 0;
338
+ if( strncmp(z,"PCK(",4)==0 ) return 0;
339
+ return 1;
340
+ }
341
+ if( strncmp(z,"cgi_param",9)==0 ) return 1;
342
+ return 0;
343
+}
297344
298345
/*
299346
** Processing flags
300347
*/
301
-#define FMT_NO_S 0x00001 /* Do not allow %s substitutions */
348
+#define FMT_SQL 0x00001 /* Generates SQL text */
349
+#define FMT_HTML 0x00002 /* Generates HTML text */
350
+#define FMT_URL 0x00004 /* Generates URLs */
351
+#define FMT_SAFE 0x00008 /* Always safe for %s */
302352
303353
/*
304354
** A list of internal Fossil interfaces that take a printf-style format
305355
** string.
306356
*/
@@ -308,45 +358,55 @@
308358
const char *zFName; /* Name of the function */
309359
int iFmtArg; /* Index of format argument. Leftmost is 1. */
310360
unsigned fmtFlags; /* Processing flags */
311361
} aFmtFunc[] = {
312362
{ "admin_log", 1, 0 },
313
- { "blob_append_sql", 2, FMT_NO_S },
363
+ { "blob_append_sql", 2, FMT_SQL },
314364
{ "blob_appendf", 2, 0 },
315
- { "cgi_panic", 1, 0 },
316
- { "cgi_redirectf", 1, 0 },
317
- { "chref", 2, 0 },
318
- { "db_blob", 2, FMT_NO_S },
319
- { "db_double", 2, FMT_NO_S },
365
+ { "cgi_debug", 1, FMT_SAFE },
366
+ { "cgi_panic", 1, FMT_SAFE },
367
+ { "cgi_printf", 1, FMT_HTML },
368
+ { "cgi_redirectf", 1, FMT_URL },
369
+ { "chref", 2, FMT_URL },
370
+ { "db_blob", 2, FMT_SQL },
371
+ { "db_debug", 1, FMT_SQL },
372
+ { "db_double", 2, FMT_SQL },
320373
{ "db_err", 1, 0 },
321
- { "db_exists", 1, FMT_NO_S },
322
- { "db_int", 2, FMT_NO_S },
323
- { "db_int64", 2, FMT_NO_S },
324
- { "db_multi_exec", 1, FMT_NO_S },
325
- { "db_optional_sql", 2, FMT_NO_S },
326
- { "db_prepare", 2, FMT_NO_S },
327
- { "db_prepare_ignore_error", 2, FMT_NO_S },
328
- { "db_static_prepare", 2, FMT_NO_S },
329
- { "db_text", 2, FMT_NO_S },
330
- { "form_begin", 2, 0 },
331
- { "fossil_error", 2, 0 },
332
- { "fossil_errorlog", 1, 0 },
333
- { "fossil_fatal", 1, 0 },
334
- { "fossil_fatal_recursive", 1, 0 },
335
- { "fossil_panic", 1, 0 },
336
- { "fossil_print", 1, 0 },
337
- { "fossil_trace", 1, 0 },
338
- { "fossil_warning", 1, 0 },
339
- { "href", 1, 0 },
374
+ { "db_exists", 1, FMT_SQL },
375
+ { "db_get_mprintf", 2, 0 },
376
+ { "db_int", 2, FMT_SQL },
377
+ { "db_int64", 2, FMT_SQL },
378
+ { "db_multi_exec", 1, FMT_SQL },
379
+ { "db_optional_sql", 2, FMT_SQL },
380
+ { "db_prepare", 2, FMT_SQL },
381
+ { "db_prepare_ignore_error", 2, FMT_SQL },
382
+ { "db_set_mprintf", 3, 0 },
383
+ { "db_static_prepare", 2, FMT_SQL },
384
+ { "db_text", 2, FMT_SQL },
385
+ { "db_unset_mprintf", 2, 0 },
386
+ { "form_begin", 2, FMT_URL },
387
+ { "fossil_error", 2, FMT_SAFE },
388
+ { "fossil_errorlog", 1, FMT_SAFE },
389
+ { "fossil_fatal", 1, FMT_SAFE },
390
+ { "fossil_fatal_recursive", 1, FMT_SAFE },
391
+ { "fossil_panic", 1, FMT_SAFE },
392
+ { "fossil_print", 1, FMT_SAFE },
393
+ { "fossil_trace", 1, FMT_SAFE },
394
+ { "fossil_warning", 1, FMT_SAFE },
395
+ { "href", 1, FMT_URL },
340396
{ "json_new_string_f", 1, 0 },
397
+ { "json_set_err", 2, 0 },
398
+ { "json_warn", 2, 0 },
341399
{ "mprintf", 1, 0 },
342400
{ "socket_set_errmsg", 1, 0 },
343401
{ "ssl_set_errmsg", 1, 0 },
344
- { "style_header", 1, 0 },
345
- { "style_set_current_page", 1, 0 },
346
- { "webpage_error", 1, 0 },
347
- { "xhref", 2, 0 },
402
+ { "style_header", 1, FMT_HTML },
403
+ { "style_set_current_page", 1, FMT_URL },
404
+ { "style_submenu_element", 2, FMT_URL },
405
+ { "style_submenu_sql", 3, FMT_SQL },
406
+ { "webpage_error", 1, FMT_SAFE },
407
+ { "xhref", 2, FMT_URL },
348408
};
349409
350410
/*
351411
** Determine if the indentifier zIdent of length nIndent is a Fossil
352412
** internal interface that uses a printf-style argument. Return zero if not.
@@ -449,16 +509,17 @@
449509
zCopy[len] = 0;
450510
azArg = 0;
451511
nArg = 0;
452512
z = zCopy;
453513
while( z[0] ){
514
+ char cEnd;
454515
len = distance_to(z, ',');
455
- azArg = safe_realloc((char*)azArg, (sizeof(azArg[0])+1)*(nArg+1));
456
- azArg[nArg++] = skip_space(z);
457
- if( z[len]==0 ) break;
516
+ cEnd = z[len];
458517
z[len] = 0;
459
- for(i=len-1; i>0 && isspace(z[i]); i--){ z[i] = 0; }
518
+ azArg = safe_realloc((char*)azArg, (sizeof(azArg[0])+1)*(nArg+1));
519
+ azArg[nArg++] = simplify_expr(z);
520
+ if( cEnd==0 ) break;
460521
z += len + 1;
461522
}
462523
acType = (char*)&azArg[nArg];
463524
if( fmtArg>nArg ){
464525
printf("%s:%d: too few arguments to %.*s()\n",
@@ -467,38 +528,47 @@
467528
}else{
468529
const char *zFmt = azArg[fmtArg-1];
469530
const char *zOverride = strstr(zFmt, "/*works-like:");
470531
if( zOverride ) zFmt = zOverride + sizeof("/*works-like:")-1;
471532
if( !is_string_lit(zFmt) ){
472
- printf("%s:%d: %.*s() has non-constant format string\n",
473
- zFilename, lnFCall, szFName, zFCall);
533
+ printf("%s:%d: %.*s() has non-constant format on arg[%d]\n",
534
+ zFilename, lnFCall, szFName, zFCall, fmtArg-1);
474535
nErr++;
475536
}else if( (k = formatArgCount(zFmt, nArg, acType))>=0
476537
&& nArg!=fmtArg+k ){
477538
printf("%s:%d: too %s arguments to %.*s() "
478539
"- got %d and expected %d\n",
479540
zFilename, lnFCall, (nArg<fmtArg+k ? "few" : "many"),
480541
szFName, zFCall, nArg, fmtArg+k);
481542
nErr++;
482
- }else if( fmtFlags & FMT_NO_S ){
543
+ }else if( (fmtFlags & FMT_SAFE)==0 ){
483544
for(i=0; i<nArg && i<k; i++){
484
- if( (acType[i]=='s' || acType[i]=='z' || acType[i]=='b')
485
- && !is_s_safe(azArg[fmtArg+i])
486
- ){
487
- printf("%s:%d: Argument %d to %.*s() not safe for SQL\n",
488
- zFilename, lnFCall, i+fmtArg, szFName, zFCall);
489
- nErr++;
545
+ if( (acType[i]=='s' || acType[i]=='z' || acType[i]=='b') ){
546
+ const char *zExpr = azArg[fmtArg+i];
547
+ if( never_safe(zExpr) ){
548
+ printf("%s:%d: Argument %d to %.*s() is not safe for"
549
+ " a query parameter\n",
550
+ zFilename, lnFCall, i+fmtArg, szFName, zFCall);
551
+ nErr++;
552
+
553
+ }else if( (fmtFlags & FMT_SQL)!=0 && !is_sql_safe(zExpr) ){
554
+ printf("%s:%d: Argument %d to %.*s() not safe for SQL\n",
555
+ zFilename, lnFCall, i+fmtArg, szFName, zFCall);
556
+ nErr++;
557
+ }
490558
}
491559
}
492560
}
493561
}
494562
if( nErr ){
495563
for(i=0; i<nArg; i++){
496564
printf(" arg[%d]: %s\n", i, azArg[i]);
497565
}
566
+ }else if( eVerbose>1 ){
567
+ printf("%s:%d: %.*s() ok for %d arguments\n",
568
+ zFilename, lnFCall, szFName, zFCall, nArg);
498569
}
499
-
500570
free((char*)azArg);
501571
free(zCopy);
502572
return nErr;
503573
}
504574
@@ -548,16 +618,24 @@
548618
}
549619
550620
/*
551621
** Check for format-string design rule violations on all files listed
552622
** on the command-line.
623
+**
624
+** The eVerbose global variable is incremented with each "-v" argument.
553625
*/
554626
int main(int argc, char **argv){
555627
int i;
556628
int nErr = 0;
557629
for(i=1; i<argc; i++){
558
- char *zFile = read_file(argv[i]);
630
+ char *zFile;
631
+ if( strcmp(argv[i],"-v")==0 ){
632
+ eVerbose++;
633
+ continue;
634
+ }
635
+ if( eVerbose>0 ) printf("Processing %s...\n", argv[i]);
636
+ zFile = read_file(argv[i]);
559637
nErr += scan_file(argv[i], zFile);
560638
free(zFile);
561639
}
562640
return nErr;
563641
}
564642
--- src/codecheck1.c
+++ src/codecheck1.c
@@ -39,10 +39,15 @@
39 #include <stdlib.h>
40 #include <ctype.h>
41 #include <string.h>
42 #include <assert.h>
43
 
 
 
 
 
44 /*
45 ** Malloc, aborting if it fails.
46 */
47 void *safe_malloc(int nByte){
48 void *x = malloc(nByte);
@@ -141,10 +146,14 @@
141 (*pLN)++;
142 i++;
143 }
144 *pType = TK_SPACE;
145 return i;
 
 
 
 
146 }
147 *pType = TK_OTHER;
148 return 1;
149 }
150
@@ -194,17 +203,40 @@
194 */
195 static const char *skip_space(const char *z){
196 while( isspace(z[0]) ){ z++; }
197 return z;
198 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
200 /*
201 ** Return true if the input is a string literal.
202 */
203 static int is_string_lit(const char *z){
204 int nu1, nu2;
205 z = next_non_whitespace(z, &nu1, &nu2);
 
206 return z[0]=='"';
207 }
208
209 /*
210 ** Return true if the input is an expression of string literals:
@@ -263,11 +295,11 @@
263
264 /*
265 ** Return true if the input is an argument that is safe to use with %s
266 ** while building an SQL statement.
267 */
268 static int is_s_safe(const char *z){
269 int len, eType;
270 int i;
271
272 /* A string literal is safe for use with %s */
273 if( is_string_lit(z) ) return 1;
@@ -292,15 +324,33 @@
292 ** let it through */
293 if( strstr(z, "/*safe-for-%s*/")!=0 ) return 1;
294
295 return 0;
296 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
297
298 /*
299 ** Processing flags
300 */
301 #define FMT_NO_S 0x00001 /* Do not allow %s substitutions */
 
 
 
302
303 /*
304 ** A list of internal Fossil interfaces that take a printf-style format
305 ** string.
306 */
@@ -308,45 +358,55 @@
308 const char *zFName; /* Name of the function */
309 int iFmtArg; /* Index of format argument. Leftmost is 1. */
310 unsigned fmtFlags; /* Processing flags */
311 } aFmtFunc[] = {
312 { "admin_log", 1, 0 },
313 { "blob_append_sql", 2, FMT_NO_S },
314 { "blob_appendf", 2, 0 },
315 { "cgi_panic", 1, 0 },
316 { "cgi_redirectf", 1, 0 },
317 { "chref", 2, 0 },
318 { "db_blob", 2, FMT_NO_S },
319 { "db_double", 2, FMT_NO_S },
 
 
 
320 { "db_err", 1, 0 },
321 { "db_exists", 1, FMT_NO_S },
322 { "db_int", 2, FMT_NO_S },
323 { "db_int64", 2, FMT_NO_S },
324 { "db_multi_exec", 1, FMT_NO_S },
325 { "db_optional_sql", 2, FMT_NO_S },
326 { "db_prepare", 2, FMT_NO_S },
327 { "db_prepare_ignore_error", 2, FMT_NO_S },
328 { "db_static_prepare", 2, FMT_NO_S },
329 { "db_text", 2, FMT_NO_S },
330 { "form_begin", 2, 0 },
331 { "fossil_error", 2, 0 },
332 { "fossil_errorlog", 1, 0 },
333 { "fossil_fatal", 1, 0 },
334 { "fossil_fatal_recursive", 1, 0 },
335 { "fossil_panic", 1, 0 },
336 { "fossil_print", 1, 0 },
337 { "fossil_trace", 1, 0 },
338 { "fossil_warning", 1, 0 },
339 { "href", 1, 0 },
 
 
 
340 { "json_new_string_f", 1, 0 },
 
 
341 { "mprintf", 1, 0 },
342 { "socket_set_errmsg", 1, 0 },
343 { "ssl_set_errmsg", 1, 0 },
344 { "style_header", 1, 0 },
345 { "style_set_current_page", 1, 0 },
346 { "webpage_error", 1, 0 },
347 { "xhref", 2, 0 },
 
 
348 };
349
350 /*
351 ** Determine if the indentifier zIdent of length nIndent is a Fossil
352 ** internal interface that uses a printf-style argument. Return zero if not.
@@ -449,16 +509,17 @@
449 zCopy[len] = 0;
450 azArg = 0;
451 nArg = 0;
452 z = zCopy;
453 while( z[0] ){
 
454 len = distance_to(z, ',');
455 azArg = safe_realloc((char*)azArg, (sizeof(azArg[0])+1)*(nArg+1));
456 azArg[nArg++] = skip_space(z);
457 if( z[len]==0 ) break;
458 z[len] = 0;
459 for(i=len-1; i>0 && isspace(z[i]); i--){ z[i] = 0; }
 
 
460 z += len + 1;
461 }
462 acType = (char*)&azArg[nArg];
463 if( fmtArg>nArg ){
464 printf("%s:%d: too few arguments to %.*s()\n",
@@ -467,38 +528,47 @@
467 }else{
468 const char *zFmt = azArg[fmtArg-1];
469 const char *zOverride = strstr(zFmt, "/*works-like:");
470 if( zOverride ) zFmt = zOverride + sizeof("/*works-like:")-1;
471 if( !is_string_lit(zFmt) ){
472 printf("%s:%d: %.*s() has non-constant format string\n",
473 zFilename, lnFCall, szFName, zFCall);
474 nErr++;
475 }else if( (k = formatArgCount(zFmt, nArg, acType))>=0
476 && nArg!=fmtArg+k ){
477 printf("%s:%d: too %s arguments to %.*s() "
478 "- got %d and expected %d\n",
479 zFilename, lnFCall, (nArg<fmtArg+k ? "few" : "many"),
480 szFName, zFCall, nArg, fmtArg+k);
481 nErr++;
482 }else if( fmtFlags & FMT_NO_S ){
483 for(i=0; i<nArg && i<k; i++){
484 if( (acType[i]=='s' || acType[i]=='z' || acType[i]=='b')
485 && !is_s_safe(azArg[fmtArg+i])
486 ){
487 printf("%s:%d: Argument %d to %.*s() not safe for SQL\n",
488 zFilename, lnFCall, i+fmtArg, szFName, zFCall);
489 nErr++;
 
 
 
 
 
 
 
490 }
491 }
492 }
493 }
494 if( nErr ){
495 for(i=0; i<nArg; i++){
496 printf(" arg[%d]: %s\n", i, azArg[i]);
497 }
 
 
 
498 }
499
500 free((char*)azArg);
501 free(zCopy);
502 return nErr;
503 }
504
@@ -548,16 +618,24 @@
548 }
549
550 /*
551 ** Check for format-string design rule violations on all files listed
552 ** on the command-line.
 
 
553 */
554 int main(int argc, char **argv){
555 int i;
556 int nErr = 0;
557 for(i=1; i<argc; i++){
558 char *zFile = read_file(argv[i]);
 
 
 
 
 
 
559 nErr += scan_file(argv[i], zFile);
560 free(zFile);
561 }
562 return nErr;
563 }
564
--- src/codecheck1.c
+++ src/codecheck1.c
@@ -39,10 +39,15 @@
39 #include <stdlib.h>
40 #include <ctype.h>
41 #include <string.h>
42 #include <assert.h>
43
44 /*
45 ** Debugging switch
46 */
47 static int eVerbose = 0;
48
49 /*
50 ** Malloc, aborting if it fails.
51 */
52 void *safe_malloc(int nByte){
53 void *x = malloc(nByte);
@@ -141,10 +146,14 @@
146 (*pLN)++;
147 i++;
148 }
149 *pType = TK_SPACE;
150 return i;
151 }
152 if( z[0]=='\\' && (z[1]=='\n' || (z[1]=='\r' && z[2]=='\n')) ){
153 *pType = TK_SPACE;
154 return 1;
155 }
156 *pType = TK_OTHER;
157 return 1;
158 }
159
@@ -194,17 +203,40 @@
203 */
204 static const char *skip_space(const char *z){
205 while( isspace(z[0]) ){ z++; }
206 return z;
207 }
208
209 /*
210 ** Remove excess whitespace and nested "()" from string z.
211 */
212 static char *simplify_expr(char *z){
213 int n = (int)strlen(z);
214 while( n>0 ){
215 if( isspace(z[0]) ){
216 z++;
217 n--;
218 continue;
219 }
220 if( z[0]=='(' && z[n-1]==')' ){
221 z++;
222 n -= 2;
223 continue;
224 }
225 break;
226 }
227 z[n] = 0;
228 return z;
229 }
230
231 /*
232 ** Return true if the input is a string literal.
233 */
234 static int is_string_lit(const char *z){
235 int nu1, nu2;
236 z = next_non_whitespace(z, &nu1, &nu2);
237 if( strcmp(z, "NULL")==0 ) return 1;
238 return z[0]=='"';
239 }
240
241 /*
242 ** Return true if the input is an expression of string literals:
@@ -263,11 +295,11 @@
295
296 /*
297 ** Return true if the input is an argument that is safe to use with %s
298 ** while building an SQL statement.
299 */
300 static int is_sql_safe(const char *z){
301 int len, eType;
302 int i;
303
304 /* A string literal is safe for use with %s */
305 if( is_string_lit(z) ) return 1;
@@ -292,15 +324,33 @@
324 ** let it through */
325 if( strstr(z, "/*safe-for-%s*/")!=0 ) return 1;
326
327 return 0;
328 }
329
330 /*
331 ** Return true if the input is an argument that is never safe for use
332 ** with %s.
333 */
334 static int never_safe(const char *z){
335 if( strstr(z,"/*safe-for-%s*/")!=0 ) return 0;
336 if( z[0]=='P' ){
337 if( strncmp(z,"PIF(",4)==0 ) return 0;
338 if( strncmp(z,"PCK(",4)==0 ) return 0;
339 return 1;
340 }
341 if( strncmp(z,"cgi_param",9)==0 ) return 1;
342 return 0;
343 }
344
345 /*
346 ** Processing flags
347 */
348 #define FMT_SQL 0x00001 /* Generates SQL text */
349 #define FMT_HTML 0x00002 /* Generates HTML text */
350 #define FMT_URL 0x00004 /* Generates URLs */
351 #define FMT_SAFE 0x00008 /* Always safe for %s */
352
353 /*
354 ** A list of internal Fossil interfaces that take a printf-style format
355 ** string.
356 */
@@ -308,45 +358,55 @@
358 const char *zFName; /* Name of the function */
359 int iFmtArg; /* Index of format argument. Leftmost is 1. */
360 unsigned fmtFlags; /* Processing flags */
361 } aFmtFunc[] = {
362 { "admin_log", 1, 0 },
363 { "blob_append_sql", 2, FMT_SQL },
364 { "blob_appendf", 2, 0 },
365 { "cgi_debug", 1, FMT_SAFE },
366 { "cgi_panic", 1, FMT_SAFE },
367 { "cgi_printf", 1, FMT_HTML },
368 { "cgi_redirectf", 1, FMT_URL },
369 { "chref", 2, FMT_URL },
370 { "db_blob", 2, FMT_SQL },
371 { "db_debug", 1, FMT_SQL },
372 { "db_double", 2, FMT_SQL },
373 { "db_err", 1, 0 },
374 { "db_exists", 1, FMT_SQL },
375 { "db_get_mprintf", 2, 0 },
376 { "db_int", 2, FMT_SQL },
377 { "db_int64", 2, FMT_SQL },
378 { "db_multi_exec", 1, FMT_SQL },
379 { "db_optional_sql", 2, FMT_SQL },
380 { "db_prepare", 2, FMT_SQL },
381 { "db_prepare_ignore_error", 2, FMT_SQL },
382 { "db_set_mprintf", 3, 0 },
383 { "db_static_prepare", 2, FMT_SQL },
384 { "db_text", 2, FMT_SQL },
385 { "db_unset_mprintf", 2, 0 },
386 { "form_begin", 2, FMT_URL },
387 { "fossil_error", 2, FMT_SAFE },
388 { "fossil_errorlog", 1, FMT_SAFE },
389 { "fossil_fatal", 1, FMT_SAFE },
390 { "fossil_fatal_recursive", 1, FMT_SAFE },
391 { "fossil_panic", 1, FMT_SAFE },
392 { "fossil_print", 1, FMT_SAFE },
393 { "fossil_trace", 1, FMT_SAFE },
394 { "fossil_warning", 1, FMT_SAFE },
395 { "href", 1, FMT_URL },
396 { "json_new_string_f", 1, 0 },
397 { "json_set_err", 2, 0 },
398 { "json_warn", 2, 0 },
399 { "mprintf", 1, 0 },
400 { "socket_set_errmsg", 1, 0 },
401 { "ssl_set_errmsg", 1, 0 },
402 { "style_header", 1, FMT_HTML },
403 { "style_set_current_page", 1, FMT_URL },
404 { "style_submenu_element", 2, FMT_URL },
405 { "style_submenu_sql", 3, FMT_SQL },
406 { "webpage_error", 1, FMT_SAFE },
407 { "xhref", 2, FMT_URL },
408 };
409
410 /*
411 ** Determine if the indentifier zIdent of length nIndent is a Fossil
412 ** internal interface that uses a printf-style argument. Return zero if not.
@@ -449,16 +509,17 @@
509 zCopy[len] = 0;
510 azArg = 0;
511 nArg = 0;
512 z = zCopy;
513 while( z[0] ){
514 char cEnd;
515 len = distance_to(z, ',');
516 cEnd = z[len];
 
 
517 z[len] = 0;
518 azArg = safe_realloc((char*)azArg, (sizeof(azArg[0])+1)*(nArg+1));
519 azArg[nArg++] = simplify_expr(z);
520 if( cEnd==0 ) break;
521 z += len + 1;
522 }
523 acType = (char*)&azArg[nArg];
524 if( fmtArg>nArg ){
525 printf("%s:%d: too few arguments to %.*s()\n",
@@ -467,38 +528,47 @@
528 }else{
529 const char *zFmt = azArg[fmtArg-1];
530 const char *zOverride = strstr(zFmt, "/*works-like:");
531 if( zOverride ) zFmt = zOverride + sizeof("/*works-like:")-1;
532 if( !is_string_lit(zFmt) ){
533 printf("%s:%d: %.*s() has non-constant format on arg[%d]\n",
534 zFilename, lnFCall, szFName, zFCall, fmtArg-1);
535 nErr++;
536 }else if( (k = formatArgCount(zFmt, nArg, acType))>=0
537 && nArg!=fmtArg+k ){
538 printf("%s:%d: too %s arguments to %.*s() "
539 "- got %d and expected %d\n",
540 zFilename, lnFCall, (nArg<fmtArg+k ? "few" : "many"),
541 szFName, zFCall, nArg, fmtArg+k);
542 nErr++;
543 }else if( (fmtFlags & FMT_SAFE)==0 ){
544 for(i=0; i<nArg && i<k; i++){
545 if( (acType[i]=='s' || acType[i]=='z' || acType[i]=='b') ){
546 const char *zExpr = azArg[fmtArg+i];
547 if( never_safe(zExpr) ){
548 printf("%s:%d: Argument %d to %.*s() is not safe for"
549 " a query parameter\n",
550 zFilename, lnFCall, i+fmtArg, szFName, zFCall);
551 nErr++;
552
553 }else if( (fmtFlags & FMT_SQL)!=0 && !is_sql_safe(zExpr) ){
554 printf("%s:%d: Argument %d to %.*s() not safe for SQL\n",
555 zFilename, lnFCall, i+fmtArg, szFName, zFCall);
556 nErr++;
557 }
558 }
559 }
560 }
561 }
562 if( nErr ){
563 for(i=0; i<nArg; i++){
564 printf(" arg[%d]: %s\n", i, azArg[i]);
565 }
566 }else if( eVerbose>1 ){
567 printf("%s:%d: %.*s() ok for %d arguments\n",
568 zFilename, lnFCall, szFName, zFCall, nArg);
569 }
 
570 free((char*)azArg);
571 free(zCopy);
572 return nErr;
573 }
574
@@ -548,16 +618,24 @@
618 }
619
620 /*
621 ** Check for format-string design rule violations on all files listed
622 ** on the command-line.
623 **
624 ** The eVerbose global variable is incremented with each "-v" argument.
625 */
626 int main(int argc, char **argv){
627 int i;
628 int nErr = 0;
629 for(i=1; i<argc; i++){
630 char *zFile;
631 if( strcmp(argv[i],"-v")==0 ){
632 eVerbose++;
633 continue;
634 }
635 if( eVerbose>0 ) printf("Processing %s...\n", argv[i]);
636 zFile = read_file(argv[i]);
637 nErr += scan_file(argv[i], zFile);
638 free(zFile);
639 }
640 return nErr;
641 }
642
+10
--- src/config.h
+++ src/config.h
@@ -253,7 +253,17 @@
253253
254254
/*
255255
** Number of elements in an array
256256
*/
257257
#define count(X) (sizeof(X)/sizeof(X[0]))
258
+
259
+/*
260
+** The pledge() interface is currently only available on OpenBSD 5.9
261
+** and later. Make calls to fossil_pledge() no-ops on all platforms
262
+** that omit the HAVE_PLEDGE configuration parameter.
263
+*/
264
+#if !defined(HAVE_PLEDGE)
265
+# define fossil_pledge(A)
266
+#endif
267
+
258268
259269
#endif /* _RC_COMPILE_ */
260270
--- src/config.h
+++ src/config.h
@@ -253,7 +253,17 @@
253
254 /*
255 ** Number of elements in an array
256 */
257 #define count(X) (sizeof(X)/sizeof(X[0]))
 
 
 
 
 
 
 
 
 
 
258
259 #endif /* _RC_COMPILE_ */
260
--- src/config.h
+++ src/config.h
@@ -253,7 +253,17 @@
253
254 /*
255 ** Number of elements in an array
256 */
257 #define count(X) (sizeof(X)/sizeof(X[0]))
258
259 /*
260 ** The pledge() interface is currently only available on OpenBSD 5.9
261 ** and later. Make calls to fossil_pledge() no-ops on all platforms
262 ** that omit the HAVE_PLEDGE configuration parameter.
263 */
264 #if !defined(HAVE_PLEDGE)
265 # define fossil_pledge(A)
266 #endif
267
268
269 #endif /* _RC_COMPILE_ */
270
+97 -47
--- src/configure.c
+++ src/configure.c
@@ -36,12 +36,14 @@
3636
#define CONFIGSET_SHUN 0x000010 /* Shun settings */
3737
#define CONFIGSET_USER 0x000020 /* The USER table */
3838
#define CONFIGSET_ADDR 0x000040 /* The CONCEALED table */
3939
#define CONFIGSET_XFER 0x000080 /* Transfer configuration */
4040
#define CONFIGSET_ALIAS 0x000100 /* URL Aliases */
41
+#define CONFIGSET_SCRIBER 0x000200 /* Email subscribers */
42
+#define CONFIGSET_FORUM 0x000400 /* Forum posts */
4143
42
-#define CONFIGSET_ALL 0x0001ff /* Everything */
44
+#define CONFIGSET_ALL 0x0007ff /* Everything */
4345
4446
#define CONFIGSET_OVERWRITE 0x100000 /* Causes overwrite instead of merge */
4547
4648
/*
4749
** This mask is used for the common TH1 configuration settings (i.e. those
@@ -58,21 +60,23 @@
5860
static struct {
5961
const char *zName; /* Name of the configuration set */
6062
int groupMask; /* Mask for that configuration set */
6163
const char *zHelp; /* What it does */
6264
} aGroupName[] = {
63
- { "/email", CONFIGSET_ADDR, "Concealed email addresses in tickets" },
64
- { "/project", CONFIGSET_PROJ, "Project name and description" },
65
- { "/skin", CONFIGSET_SKIN | CONFIGSET_CSS,
66
- "Web interface appearance settings" },
67
- { "/css", CONFIGSET_CSS, "Style sheet" },
68
- { "/shun", CONFIGSET_SHUN, "List of shunned artifacts" },
69
- { "/ticket", CONFIGSET_TKT, "Ticket setup", },
70
- { "/user", CONFIGSET_USER, "Users and privilege settings" },
71
- { "/xfer", CONFIGSET_XFER, "Transfer setup", },
72
- { "/alias", CONFIGSET_ALIAS, "URL Aliases", },
73
- { "/all", CONFIGSET_ALL, "All of the above" },
65
+ { "/email", CONFIGSET_ADDR, "Concealed email addresses in tickets" },
66
+ { "/project", CONFIGSET_PROJ, "Project name and description" },
67
+ { "/skin", CONFIGSET_SKIN | CONFIGSET_CSS,
68
+ "Web interface appearance settings" },
69
+ { "/css", CONFIGSET_CSS, "Style sheet" },
70
+ { "/shun", CONFIGSET_SHUN, "List of shunned artifacts" },
71
+ { "/ticket", CONFIGSET_TKT, "Ticket setup", },
72
+ { "/user", CONFIGSET_USER, "Users and privilege settings" },
73
+ { "/xfer", CONFIGSET_XFER, "Transfer setup", },
74
+ { "/alias", CONFIGSET_ALIAS, "URL Aliases", },
75
+ { "/subscriber", CONFIGSET_SCRIBER,"Email notification subscriber list" },
76
+/*{ "/forum", CONFIGSET_FORUM, "Forum posts", },*/
77
+ { "/all", CONFIGSET_ALL, "All of the above" },
7478
};
7579
7680
7781
/*
7882
** The following is a list of settings that we are willing to
@@ -157,10 +161,12 @@
157161
158162
{ "@shun", CONFIGSET_SHUN },
159163
160164
{ "@alias", CONFIGSET_ALIAS },
161165
166
+ { "@subscriber", CONFIGSET_SCRIBER },
167
+
162168
{ "xfer-common-script", CONFIGSET_XFER },
163169
{ "xfer-push-script", CONFIGSET_XFER },
164170
{ "xfer-commit-script", CONFIGSET_XFER },
165171
{ "xfer-ticket-script", CONFIGSET_XFER },
166172
@@ -213,11 +219,11 @@
213219
214220
/*
215221
** Return the mask for the named configuration parameter if it can be
216222
** safely exported. Return 0 if the parameter is not safe to export.
217223
**
218
-** "Safe" in the previous paragraph means the permission is created to
224
+** "Safe" in the previous paragraph means the permission is granted to
219225
** export the property. In other words, the requesting side has presented
220226
** login credentials and has sufficient capabilities to access the requested
221227
** information.
222228
*/
223229
int configure_is_exportable(const char *zName){
@@ -229,11 +235,14 @@
229235
}
230236
for(i=0; i<count(aConfig); i++){
231237
if( strncmp(zName, aConfig[i].zName, n)==0 && aConfig[i].zName[n]==0 ){
232238
int m = aConfig[i].groupMask;
233239
if( !g.perm.Admin ){
234
- m &= ~CONFIGSET_USER;
240
+ m &= ~(CONFIGSET_USER|CONFIGSET_SCRIBER);
241
+ }
242
+ if( !g.perm.RdForum ){
243
+ m &= ~(CONFIGSET_FORUM);
235244
}
236245
if( !g.perm.RdAddr ){
237246
m &= ~CONFIGSET_ADDR;
238247
}
239248
return m;
@@ -312,11 +321,15 @@
312321
** Mask consists of one or more CONFIGSET_* values ORed together, to
313322
** designate what types of configuration we are allowed to receive.
314323
**
315324
** NEW FORMAT:
316325
**
317
-** zName is one of "/config", "/user", "/shun", "/reportfmt", or "/concealed".
326
+** zName is one of:
327
+**
328
+** "/config", "/user", "/shun", "/reportfmt", "/concealed",
329
+** "/subscriber",
330
+**
318331
** zName indicates the table that holds the configuration information being
319332
** transferred. pContent is a string that consist of alternating Fossil
320333
** and SQL tokens. The First token is a timestamp in seconds since 1970.
321334
** The second token is a primary key for the table identified by zName. If
322335
** The entry with the corresponding primary key exists and has a more recent
@@ -332,53 +345,44 @@
332345
** /config $MTIME $NAME value $VALUE
333346
** /user $MTIME $LOGIN pw $VALUE cap $VALUE info $VALUE photo $VALUE
334347
** /shun $MTIME $UUID scom $VALUE
335348
** /reportfmt $MTIME $TITLE owner $VALUE cols $VALUE sqlcode $VALUE
336349
** /concealed $MTIME $HASH content $VALUE
337
-**
338
-** OLD FORMAT:
339
-**
340
-** The old format is retained for backwards compatibility, but is deprecated.
341
-** The cutover from old format to new was on 2011-04-25. After sufficient
342
-** time has passed, support for the old format will be removed.
343
-** Update: Support for the old format was removed on 2017-09-20.
344
-**
345
-** zName is either the NAME of an element of the CONFIG table, or else
346
-** one of the special names "@shun", "@reportfmt", "@user", or "@concealed".
347
-** If zName is a CONFIG table name, then CONTENT replaces (overwrites) the
348
-** element in the CONFIG table. For one of the @-labels, CONTENT is raw
349
-** SQL that is evaluated. Note that the raw SQL in CONTENT might not
350
-** insert directly into the target table but might instead use a proxy
351
-** table like _fer_reportfmt or _xfer_user. Such tables must be created
352
-** ahead of time using configure_prepare_to_receive(). Then after multiple
353
-** calls to this routine, configure_finalize_receive() to transfer the
354
-** information received into the true target table.
350
+** /subscriber $SMTIME $SEMAIL suname $V ...
355351
*/
356352
void configure_receive(const char *zName, Blob *pContent, int groupMask){
353
+ int checkMask; /* Masks for which we must first check existance of tables */
354
+
355
+ checkMask = CONFIGSET_SCRIBER;
357356
if( zName[0]=='/' ){
358357
/* The new format */
359
- char *azToken[12];
358
+ char *azToken[24];
360359
int nToken = 0;
361360
int ii, jj;
362361
int thisMask;
363362
Blob name, value, sql;
364363
static const struct receiveType {
365
- const char *zName;
366
- const char *zPrimKey;
367
- int nField;
368
- const char *azField[4];
364
+ const char *zName; /* Configuration key for this table */
365
+ const char *zPrimKey; /* Primary key column */
366
+ int nField; /* Number of data fields */
367
+ const char *azField[6]; /* Names of the data fields */
369368
} aType[] = {
370
- { "/config", "name", 1, { "value", 0, 0, 0 } },
371
- { "@user", "login", 4, { "pw", "cap", "info", "photo" } },
372
- { "@shun", "uuid", 1, { "scom", 0, 0, 0 } },
373
- { "@reportfmt", "title", 3, { "owner", "cols", "sqlcode", 0 } },
374
- { "@concealed", "hash", 1, { "content", 0, 0, 0 } },
369
+ { "/config", "name", 1, { "value", 0,0,0,0,0 } },
370
+ { "@user", "login", 4, { "pw","cap","info","photo",0,0} },
371
+ { "@shun", "uuid", 1, { "scom", 0,0,0,0,0} },
372
+ { "@reportfmt", "title", 3, { "owner","cols","sqlcode",0,0,0}},
373
+ { "@concealed", "hash", 1, { "content", 0,0,0,0,0 } },
374
+ { "@subscriber","semail",6,
375
+ { "suname","sdigest","sdonotcall","ssub","sctime","smip"} },
375376
};
377
+
378
+ /* Locate the receiveType in aType[ii] */
376379
for(ii=0; ii<count(aType); ii++){
377380
if( fossil_strcmp(&aType[ii].zName[1],&zName[1])==0 ) break;
378381
}
379382
if( ii>=count(aType) ) return;
383
+
380384
while( blob_token(pContent, &name) && blob_sqltoken(pContent, &value) ){
381385
char *z = blob_terminate(&name);
382386
if( !safeSql(z) ) return;
383387
if( nToken>0 ){
384388
for(jj=0; jj<aType[ii].nField; jj++){
@@ -389,19 +393,25 @@
389393
if( !safeInt(z) ) return;
390394
}
391395
azToken[nToken++] = z;
392396
azToken[nToken++] = z = blob_terminate(&value);
393397
if( !safeSql(z) ) return;
394
- if( nToken>=count(azToken) ) break;
398
+ if( nToken>=count(azToken)-1 ) break;
395399
}
396400
if( nToken<2 ) return;
397401
if( aType[ii].zName[0]=='/' ){
398402
thisMask = configure_is_exportable(azToken[1]);
399403
}else{
400404
thisMask = configure_is_exportable(aType[ii].zName);
401405
}
402406
if( (thisMask & groupMask)==0 ) return;
407
+ if( (thisMask & checkMask)!=0 ){
408
+ if( (thisMask & CONFIGSET_SCRIBER)!=0 ){
409
+ email_schema(1);
410
+ }
411
+ checkMask &= ~thisMask;
412
+ }
403413
404414
blob_zero(&sql);
405415
if( groupMask & CONFIGSET_OVERWRITE ){
406416
if( (thisMask & configHasBeenReset)==0 && aType[ii].zName[0]!='/' ){
407417
db_multi_exec("DELETE FROM \"%w\"", &aType[ii].zName[1]);
@@ -409,16 +419,17 @@
409419
}
410420
blob_append_sql(&sql, "REPLACE INTO ");
411421
}else{
412422
blob_append_sql(&sql, "INSERT OR IGNORE INTO ");
413423
}
414
- blob_append_sql(&sql, "\"%w\"(\"%w\", mtime", &zName[1], aType[ii].zPrimKey);
424
+ blob_append_sql(&sql, "\"%w\"(\"%w\",mtime",
425
+ &zName[1], aType[ii].zPrimKey);
415426
for(jj=2; jj<nToken; jj+=2){
416427
blob_append_sql(&sql, ",\"%w\"", azToken[jj]);
417428
}
418429
blob_append_sql(&sql,") VALUES(%s,%s",
419
- azToken[1] /*safe-for-%s*/, azToken[0] /*safe-for-%s*/);
430
+ azToken[1] /*safe-for-%s*/, azToken[0]/*safe-for-%s*/);
420431
for(jj=2; jj<nToken; jj+=2){
421432
blob_append_sql(&sql, ",%s", azToken[jj+1] /*safe-for-%s*/);
422433
}
423434
db_multi_exec("%s)", blob_sql_text(&sql));
424435
if( db_changes()==0 ){
@@ -573,10 +584,39 @@
573584
blob_size(&rec), blob_str(&rec));
574585
nCard++;
575586
blob_reset(&rec);
576587
}
577588
db_finalize(&q);
589
+ }
590
+ if( (groupMask & CONFIGSET_SCRIBER)!=0
591
+ && db_table_exists("repository","subscriber")
592
+ ){
593
+ db_prepare(&q, "SELECT mtime, quote(semail),"
594
+ " quote(suname), quote(sdigest),"
595
+ " quote(sdonotcall), quote(ssub),"
596
+ " quote(sctime), quote(smip)"
597
+ " FROM subscriber WHERE sverified"
598
+ " AND mtime>=%lld", iStart);
599
+ while( db_step(&q)==SQLITE_ROW ){
600
+ blob_appendf(&rec,
601
+ "%lld %s suname %s sdigest %s sdonotcall %s ssub %s"
602
+ " sctime %s smip %s",
603
+ db_column_int64(&q, 0), /* mtime */
604
+ db_column_text(&q, 1), /* semail (PK) */
605
+ db_column_text(&q, 2), /* suname */
606
+ db_column_text(&q, 3), /* sdigest */
607
+ db_column_text(&q, 4), /* sdonotcall */
608
+ db_column_text(&q, 5), /* ssub */
609
+ db_column_text(&q, 6), /* sctime */
610
+ db_column_text(&q, 7) /* smip */
611
+ );
612
+ blob_appendf(pOut, "config /subscriber %d\n%s\n",
613
+ blob_size(&rec), blob_str(&rec));
614
+ nCard++;
615
+ blob_reset(&rec);
616
+ }
617
+ db_finalize(&q);
578618
}
579619
db_prepare(&q, "SELECT mtime, quote(name), quote(value) FROM config"
580620
" WHERE name=:name AND mtime>=%lld", iStart);
581621
for(ii=0; ii<count(aConfig); ii++){
582622
if( (aConfig[ii].groupMask & groupMask)!=0 && aConfig[ii].zName[0]!='@' ){
@@ -612,11 +652,12 @@
612652
}
613653
}
614654
if( notFoundIsFatal ){
615655
fossil_print("Available configuration areas:\n");
616656
for(i=0; i<count(aGroupName); i++){
617
- fossil_print(" %-10s %s\n", &aGroupName[i].zName[1], aGroupName[i].zHelp);
657
+ fossil_print(" %-13s %s\n",
658
+ &aGroupName[i].zName[1], aGroupName[i].zHelp);
618659
}
619660
fossil_fatal("no such configuration area: \"%s\"", z);
620661
}
621662
return 0;
622663
}
@@ -792,10 +833,19 @@
792833
db_create_default_users(0, 0);
793834
}else if( fossil_strcmp(zName,"@concealed")==0 ){
794835
db_multi_exec("DELETE FROM concealed");
795836
}else if( fossil_strcmp(zName,"@shun")==0 ){
796837
db_multi_exec("DELETE FROM shun");
838
+ }else if( fossil_strcmp(zName,"@subscriber")==0 ){
839
+ if( db_table_exists("repository","subscriber") ){
840
+ db_multi_exec("DELETE FROM subscriber");
841
+ }
842
+ }else if( fossil_strcmp(zName,"@forum")==0 ){
843
+ if( db_table_exists("repository","forumpost") ){
844
+ db_multi_exec("DELETE FROM forumpost");
845
+ db_multi_exec("DELETE FROM forumthread");
846
+ }
797847
}else if( fossil_strcmp(zName,"@reportfmt")==0 ){
798848
db_multi_exec("DELETE FROM reportfmt");
799849
assert( strchr(zRepositorySchemaDefaultReports,'%')==0 );
800850
db_multi_exec(zRepositorySchemaDefaultReports /*works-like:""*/);
801851
}
802852
--- src/configure.c
+++ src/configure.c
@@ -36,12 +36,14 @@
36 #define CONFIGSET_SHUN 0x000010 /* Shun settings */
37 #define CONFIGSET_USER 0x000020 /* The USER table */
38 #define CONFIGSET_ADDR 0x000040 /* The CONCEALED table */
39 #define CONFIGSET_XFER 0x000080 /* Transfer configuration */
40 #define CONFIGSET_ALIAS 0x000100 /* URL Aliases */
 
 
41
42 #define CONFIGSET_ALL 0x0001ff /* Everything */
43
44 #define CONFIGSET_OVERWRITE 0x100000 /* Causes overwrite instead of merge */
45
46 /*
47 ** This mask is used for the common TH1 configuration settings (i.e. those
@@ -58,21 +60,23 @@
58 static struct {
59 const char *zName; /* Name of the configuration set */
60 int groupMask; /* Mask for that configuration set */
61 const char *zHelp; /* What it does */
62 } aGroupName[] = {
63 { "/email", CONFIGSET_ADDR, "Concealed email addresses in tickets" },
64 { "/project", CONFIGSET_PROJ, "Project name and description" },
65 { "/skin", CONFIGSET_SKIN | CONFIGSET_CSS,
66 "Web interface appearance settings" },
67 { "/css", CONFIGSET_CSS, "Style sheet" },
68 { "/shun", CONFIGSET_SHUN, "List of shunned artifacts" },
69 { "/ticket", CONFIGSET_TKT, "Ticket setup", },
70 { "/user", CONFIGSET_USER, "Users and privilege settings" },
71 { "/xfer", CONFIGSET_XFER, "Transfer setup", },
72 { "/alias", CONFIGSET_ALIAS, "URL Aliases", },
73 { "/all", CONFIGSET_ALL, "All of the above" },
 
 
74 };
75
76
77 /*
78 ** The following is a list of settings that we are willing to
@@ -157,10 +161,12 @@
157
158 { "@shun", CONFIGSET_SHUN },
159
160 { "@alias", CONFIGSET_ALIAS },
161
 
 
162 { "xfer-common-script", CONFIGSET_XFER },
163 { "xfer-push-script", CONFIGSET_XFER },
164 { "xfer-commit-script", CONFIGSET_XFER },
165 { "xfer-ticket-script", CONFIGSET_XFER },
166
@@ -213,11 +219,11 @@
213
214 /*
215 ** Return the mask for the named configuration parameter if it can be
216 ** safely exported. Return 0 if the parameter is not safe to export.
217 **
218 ** "Safe" in the previous paragraph means the permission is created to
219 ** export the property. In other words, the requesting side has presented
220 ** login credentials and has sufficient capabilities to access the requested
221 ** information.
222 */
223 int configure_is_exportable(const char *zName){
@@ -229,11 +235,14 @@
229 }
230 for(i=0; i<count(aConfig); i++){
231 if( strncmp(zName, aConfig[i].zName, n)==0 && aConfig[i].zName[n]==0 ){
232 int m = aConfig[i].groupMask;
233 if( !g.perm.Admin ){
234 m &= ~CONFIGSET_USER;
 
 
 
235 }
236 if( !g.perm.RdAddr ){
237 m &= ~CONFIGSET_ADDR;
238 }
239 return m;
@@ -312,11 +321,15 @@
312 ** Mask consists of one or more CONFIGSET_* values ORed together, to
313 ** designate what types of configuration we are allowed to receive.
314 **
315 ** NEW FORMAT:
316 **
317 ** zName is one of "/config", "/user", "/shun", "/reportfmt", or "/concealed".
 
 
 
 
318 ** zName indicates the table that holds the configuration information being
319 ** transferred. pContent is a string that consist of alternating Fossil
320 ** and SQL tokens. The First token is a timestamp in seconds since 1970.
321 ** The second token is a primary key for the table identified by zName. If
322 ** The entry with the corresponding primary key exists and has a more recent
@@ -332,53 +345,44 @@
332 ** /config $MTIME $NAME value $VALUE
333 ** /user $MTIME $LOGIN pw $VALUE cap $VALUE info $VALUE photo $VALUE
334 ** /shun $MTIME $UUID scom $VALUE
335 ** /reportfmt $MTIME $TITLE owner $VALUE cols $VALUE sqlcode $VALUE
336 ** /concealed $MTIME $HASH content $VALUE
337 **
338 ** OLD FORMAT:
339 **
340 ** The old format is retained for backwards compatibility, but is deprecated.
341 ** The cutover from old format to new was on 2011-04-25. After sufficient
342 ** time has passed, support for the old format will be removed.
343 ** Update: Support for the old format was removed on 2017-09-20.
344 **
345 ** zName is either the NAME of an element of the CONFIG table, or else
346 ** one of the special names "@shun", "@reportfmt", "@user", or "@concealed".
347 ** If zName is a CONFIG table name, then CONTENT replaces (overwrites) the
348 ** element in the CONFIG table. For one of the @-labels, CONTENT is raw
349 ** SQL that is evaluated. Note that the raw SQL in CONTENT might not
350 ** insert directly into the target table but might instead use a proxy
351 ** table like _fer_reportfmt or _xfer_user. Such tables must be created
352 ** ahead of time using configure_prepare_to_receive(). Then after multiple
353 ** calls to this routine, configure_finalize_receive() to transfer the
354 ** information received into the true target table.
355 */
356 void configure_receive(const char *zName, Blob *pContent, int groupMask){
 
 
 
357 if( zName[0]=='/' ){
358 /* The new format */
359 char *azToken[12];
360 int nToken = 0;
361 int ii, jj;
362 int thisMask;
363 Blob name, value, sql;
364 static const struct receiveType {
365 const char *zName;
366 const char *zPrimKey;
367 int nField;
368 const char *azField[4];
369 } aType[] = {
370 { "/config", "name", 1, { "value", 0, 0, 0 } },
371 { "@user", "login", 4, { "pw", "cap", "info", "photo" } },
372 { "@shun", "uuid", 1, { "scom", 0, 0, 0 } },
373 { "@reportfmt", "title", 3, { "owner", "cols", "sqlcode", 0 } },
374 { "@concealed", "hash", 1, { "content", 0, 0, 0 } },
 
 
375 };
 
 
376 for(ii=0; ii<count(aType); ii++){
377 if( fossil_strcmp(&aType[ii].zName[1],&zName[1])==0 ) break;
378 }
379 if( ii>=count(aType) ) return;
 
380 while( blob_token(pContent, &name) && blob_sqltoken(pContent, &value) ){
381 char *z = blob_terminate(&name);
382 if( !safeSql(z) ) return;
383 if( nToken>0 ){
384 for(jj=0; jj<aType[ii].nField; jj++){
@@ -389,19 +393,25 @@
389 if( !safeInt(z) ) return;
390 }
391 azToken[nToken++] = z;
392 azToken[nToken++] = z = blob_terminate(&value);
393 if( !safeSql(z) ) return;
394 if( nToken>=count(azToken) ) break;
395 }
396 if( nToken<2 ) return;
397 if( aType[ii].zName[0]=='/' ){
398 thisMask = configure_is_exportable(azToken[1]);
399 }else{
400 thisMask = configure_is_exportable(aType[ii].zName);
401 }
402 if( (thisMask & groupMask)==0 ) return;
 
 
 
 
 
 
403
404 blob_zero(&sql);
405 if( groupMask & CONFIGSET_OVERWRITE ){
406 if( (thisMask & configHasBeenReset)==0 && aType[ii].zName[0]!='/' ){
407 db_multi_exec("DELETE FROM \"%w\"", &aType[ii].zName[1]);
@@ -409,16 +419,17 @@
409 }
410 blob_append_sql(&sql, "REPLACE INTO ");
411 }else{
412 blob_append_sql(&sql, "INSERT OR IGNORE INTO ");
413 }
414 blob_append_sql(&sql, "\"%w\"(\"%w\", mtime", &zName[1], aType[ii].zPrimKey);
 
415 for(jj=2; jj<nToken; jj+=2){
416 blob_append_sql(&sql, ",\"%w\"", azToken[jj]);
417 }
418 blob_append_sql(&sql,") VALUES(%s,%s",
419 azToken[1] /*safe-for-%s*/, azToken[0] /*safe-for-%s*/);
420 for(jj=2; jj<nToken; jj+=2){
421 blob_append_sql(&sql, ",%s", azToken[jj+1] /*safe-for-%s*/);
422 }
423 db_multi_exec("%s)", blob_sql_text(&sql));
424 if( db_changes()==0 ){
@@ -573,10 +584,39 @@
573 blob_size(&rec), blob_str(&rec));
574 nCard++;
575 blob_reset(&rec);
576 }
577 db_finalize(&q);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
578 }
579 db_prepare(&q, "SELECT mtime, quote(name), quote(value) FROM config"
580 " WHERE name=:name AND mtime>=%lld", iStart);
581 for(ii=0; ii<count(aConfig); ii++){
582 if( (aConfig[ii].groupMask & groupMask)!=0 && aConfig[ii].zName[0]!='@' ){
@@ -612,11 +652,12 @@
612 }
613 }
614 if( notFoundIsFatal ){
615 fossil_print("Available configuration areas:\n");
616 for(i=0; i<count(aGroupName); i++){
617 fossil_print(" %-10s %s\n", &aGroupName[i].zName[1], aGroupName[i].zHelp);
 
618 }
619 fossil_fatal("no such configuration area: \"%s\"", z);
620 }
621 return 0;
622 }
@@ -792,10 +833,19 @@
792 db_create_default_users(0, 0);
793 }else if( fossil_strcmp(zName,"@concealed")==0 ){
794 db_multi_exec("DELETE FROM concealed");
795 }else if( fossil_strcmp(zName,"@shun")==0 ){
796 db_multi_exec("DELETE FROM shun");
 
 
 
 
 
 
 
 
 
797 }else if( fossil_strcmp(zName,"@reportfmt")==0 ){
798 db_multi_exec("DELETE FROM reportfmt");
799 assert( strchr(zRepositorySchemaDefaultReports,'%')==0 );
800 db_multi_exec(zRepositorySchemaDefaultReports /*works-like:""*/);
801 }
802
--- src/configure.c
+++ src/configure.c
@@ -36,12 +36,14 @@
36 #define CONFIGSET_SHUN 0x000010 /* Shun settings */
37 #define CONFIGSET_USER 0x000020 /* The USER table */
38 #define CONFIGSET_ADDR 0x000040 /* The CONCEALED table */
39 #define CONFIGSET_XFER 0x000080 /* Transfer configuration */
40 #define CONFIGSET_ALIAS 0x000100 /* URL Aliases */
41 #define CONFIGSET_SCRIBER 0x000200 /* Email subscribers */
42 #define CONFIGSET_FORUM 0x000400 /* Forum posts */
43
44 #define CONFIGSET_ALL 0x0007ff /* Everything */
45
46 #define CONFIGSET_OVERWRITE 0x100000 /* Causes overwrite instead of merge */
47
48 /*
49 ** This mask is used for the common TH1 configuration settings (i.e. those
@@ -58,21 +60,23 @@
60 static struct {
61 const char *zName; /* Name of the configuration set */
62 int groupMask; /* Mask for that configuration set */
63 const char *zHelp; /* What it does */
64 } aGroupName[] = {
65 { "/email", CONFIGSET_ADDR, "Concealed email addresses in tickets" },
66 { "/project", CONFIGSET_PROJ, "Project name and description" },
67 { "/skin", CONFIGSET_SKIN | CONFIGSET_CSS,
68 "Web interface appearance settings" },
69 { "/css", CONFIGSET_CSS, "Style sheet" },
70 { "/shun", CONFIGSET_SHUN, "List of shunned artifacts" },
71 { "/ticket", CONFIGSET_TKT, "Ticket setup", },
72 { "/user", CONFIGSET_USER, "Users and privilege settings" },
73 { "/xfer", CONFIGSET_XFER, "Transfer setup", },
74 { "/alias", CONFIGSET_ALIAS, "URL Aliases", },
75 { "/subscriber", CONFIGSET_SCRIBER,"Email notification subscriber list" },
76 /*{ "/forum", CONFIGSET_FORUM, "Forum posts", },*/
77 { "/all", CONFIGSET_ALL, "All of the above" },
78 };
79
80
81 /*
82 ** The following is a list of settings that we are willing to
@@ -157,10 +161,12 @@
161
162 { "@shun", CONFIGSET_SHUN },
163
164 { "@alias", CONFIGSET_ALIAS },
165
166 { "@subscriber", CONFIGSET_SCRIBER },
167
168 { "xfer-common-script", CONFIGSET_XFER },
169 { "xfer-push-script", CONFIGSET_XFER },
170 { "xfer-commit-script", CONFIGSET_XFER },
171 { "xfer-ticket-script", CONFIGSET_XFER },
172
@@ -213,11 +219,11 @@
219
220 /*
221 ** Return the mask for the named configuration parameter if it can be
222 ** safely exported. Return 0 if the parameter is not safe to export.
223 **
224 ** "Safe" in the previous paragraph means the permission is granted to
225 ** export the property. In other words, the requesting side has presented
226 ** login credentials and has sufficient capabilities to access the requested
227 ** information.
228 */
229 int configure_is_exportable(const char *zName){
@@ -229,11 +235,14 @@
235 }
236 for(i=0; i<count(aConfig); i++){
237 if( strncmp(zName, aConfig[i].zName, n)==0 && aConfig[i].zName[n]==0 ){
238 int m = aConfig[i].groupMask;
239 if( !g.perm.Admin ){
240 m &= ~(CONFIGSET_USER|CONFIGSET_SCRIBER);
241 }
242 if( !g.perm.RdForum ){
243 m &= ~(CONFIGSET_FORUM);
244 }
245 if( !g.perm.RdAddr ){
246 m &= ~CONFIGSET_ADDR;
247 }
248 return m;
@@ -312,11 +321,15 @@
321 ** Mask consists of one or more CONFIGSET_* values ORed together, to
322 ** designate what types of configuration we are allowed to receive.
323 **
324 ** NEW FORMAT:
325 **
326 ** zName is one of:
327 **
328 ** "/config", "/user", "/shun", "/reportfmt", "/concealed",
329 ** "/subscriber",
330 **
331 ** zName indicates the table that holds the configuration information being
332 ** transferred. pContent is a string that consist of alternating Fossil
333 ** and SQL tokens. The First token is a timestamp in seconds since 1970.
334 ** The second token is a primary key for the table identified by zName. If
335 ** The entry with the corresponding primary key exists and has a more recent
@@ -332,53 +345,44 @@
345 ** /config $MTIME $NAME value $VALUE
346 ** /user $MTIME $LOGIN pw $VALUE cap $VALUE info $VALUE photo $VALUE
347 ** /shun $MTIME $UUID scom $VALUE
348 ** /reportfmt $MTIME $TITLE owner $VALUE cols $VALUE sqlcode $VALUE
349 ** /concealed $MTIME $HASH content $VALUE
350 ** /subscriber $SMTIME $SEMAIL suname $V ...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
351 */
352 void configure_receive(const char *zName, Blob *pContent, int groupMask){
353 int checkMask; /* Masks for which we must first check existance of tables */
354
355 checkMask = CONFIGSET_SCRIBER;
356 if( zName[0]=='/' ){
357 /* The new format */
358 char *azToken[24];
359 int nToken = 0;
360 int ii, jj;
361 int thisMask;
362 Blob name, value, sql;
363 static const struct receiveType {
364 const char *zName; /* Configuration key for this table */
365 const char *zPrimKey; /* Primary key column */
366 int nField; /* Number of data fields */
367 const char *azField[6]; /* Names of the data fields */
368 } aType[] = {
369 { "/config", "name", 1, { "value", 0,0,0,0,0 } },
370 { "@user", "login", 4, { "pw","cap","info","photo",0,0} },
371 { "@shun", "uuid", 1, { "scom", 0,0,0,0,0} },
372 { "@reportfmt", "title", 3, { "owner","cols","sqlcode",0,0,0}},
373 { "@concealed", "hash", 1, { "content", 0,0,0,0,0 } },
374 { "@subscriber","semail",6,
375 { "suname","sdigest","sdonotcall","ssub","sctime","smip"} },
376 };
377
378 /* Locate the receiveType in aType[ii] */
379 for(ii=0; ii<count(aType); ii++){
380 if( fossil_strcmp(&aType[ii].zName[1],&zName[1])==0 ) break;
381 }
382 if( ii>=count(aType) ) return;
383
384 while( blob_token(pContent, &name) && blob_sqltoken(pContent, &value) ){
385 char *z = blob_terminate(&name);
386 if( !safeSql(z) ) return;
387 if( nToken>0 ){
388 for(jj=0; jj<aType[ii].nField; jj++){
@@ -389,19 +393,25 @@
393 if( !safeInt(z) ) return;
394 }
395 azToken[nToken++] = z;
396 azToken[nToken++] = z = blob_terminate(&value);
397 if( !safeSql(z) ) return;
398 if( nToken>=count(azToken)-1 ) break;
399 }
400 if( nToken<2 ) return;
401 if( aType[ii].zName[0]=='/' ){
402 thisMask = configure_is_exportable(azToken[1]);
403 }else{
404 thisMask = configure_is_exportable(aType[ii].zName);
405 }
406 if( (thisMask & groupMask)==0 ) return;
407 if( (thisMask & checkMask)!=0 ){
408 if( (thisMask & CONFIGSET_SCRIBER)!=0 ){
409 email_schema(1);
410 }
411 checkMask &= ~thisMask;
412 }
413
414 blob_zero(&sql);
415 if( groupMask & CONFIGSET_OVERWRITE ){
416 if( (thisMask & configHasBeenReset)==0 && aType[ii].zName[0]!='/' ){
417 db_multi_exec("DELETE FROM \"%w\"", &aType[ii].zName[1]);
@@ -409,16 +419,17 @@
419 }
420 blob_append_sql(&sql, "REPLACE INTO ");
421 }else{
422 blob_append_sql(&sql, "INSERT OR IGNORE INTO ");
423 }
424 blob_append_sql(&sql, "\"%w\"(\"%w\",mtime",
425 &zName[1], aType[ii].zPrimKey);
426 for(jj=2; jj<nToken; jj+=2){
427 blob_append_sql(&sql, ",\"%w\"", azToken[jj]);
428 }
429 blob_append_sql(&sql,") VALUES(%s,%s",
430 azToken[1] /*safe-for-%s*/, azToken[0]/*safe-for-%s*/);
431 for(jj=2; jj<nToken; jj+=2){
432 blob_append_sql(&sql, ",%s", azToken[jj+1] /*safe-for-%s*/);
433 }
434 db_multi_exec("%s)", blob_sql_text(&sql));
435 if( db_changes()==0 ){
@@ -573,10 +584,39 @@
584 blob_size(&rec), blob_str(&rec));
585 nCard++;
586 blob_reset(&rec);
587 }
588 db_finalize(&q);
589 }
590 if( (groupMask & CONFIGSET_SCRIBER)!=0
591 && db_table_exists("repository","subscriber")
592 ){
593 db_prepare(&q, "SELECT mtime, quote(semail),"
594 " quote(suname), quote(sdigest),"
595 " quote(sdonotcall), quote(ssub),"
596 " quote(sctime), quote(smip)"
597 " FROM subscriber WHERE sverified"
598 " AND mtime>=%lld", iStart);
599 while( db_step(&q)==SQLITE_ROW ){
600 blob_appendf(&rec,
601 "%lld %s suname %s sdigest %s sdonotcall %s ssub %s"
602 " sctime %s smip %s",
603 db_column_int64(&q, 0), /* mtime */
604 db_column_text(&q, 1), /* semail (PK) */
605 db_column_text(&q, 2), /* suname */
606 db_column_text(&q, 3), /* sdigest */
607 db_column_text(&q, 4), /* sdonotcall */
608 db_column_text(&q, 5), /* ssub */
609 db_column_text(&q, 6), /* sctime */
610 db_column_text(&q, 7) /* smip */
611 );
612 blob_appendf(pOut, "config /subscriber %d\n%s\n",
613 blob_size(&rec), blob_str(&rec));
614 nCard++;
615 blob_reset(&rec);
616 }
617 db_finalize(&q);
618 }
619 db_prepare(&q, "SELECT mtime, quote(name), quote(value) FROM config"
620 " WHERE name=:name AND mtime>=%lld", iStart);
621 for(ii=0; ii<count(aConfig); ii++){
622 if( (aConfig[ii].groupMask & groupMask)!=0 && aConfig[ii].zName[0]!='@' ){
@@ -612,11 +652,12 @@
652 }
653 }
654 if( notFoundIsFatal ){
655 fossil_print("Available configuration areas:\n");
656 for(i=0; i<count(aGroupName); i++){
657 fossil_print(" %-13s %s\n",
658 &aGroupName[i].zName[1], aGroupName[i].zHelp);
659 }
660 fossil_fatal("no such configuration area: \"%s\"", z);
661 }
662 return 0;
663 }
@@ -792,10 +833,19 @@
833 db_create_default_users(0, 0);
834 }else if( fossil_strcmp(zName,"@concealed")==0 ){
835 db_multi_exec("DELETE FROM concealed");
836 }else if( fossil_strcmp(zName,"@shun")==0 ){
837 db_multi_exec("DELETE FROM shun");
838 }else if( fossil_strcmp(zName,"@subscriber")==0 ){
839 if( db_table_exists("repository","subscriber") ){
840 db_multi_exec("DELETE FROM subscriber");
841 }
842 }else if( fossil_strcmp(zName,"@forum")==0 ){
843 if( db_table_exists("repository","forumpost") ){
844 db_multi_exec("DELETE FROM forumpost");
845 db_multi_exec("DELETE FROM forumthread");
846 }
847 }else if( fossil_strcmp(zName,"@reportfmt")==0 ){
848 db_multi_exec("DELETE FROM reportfmt");
849 assert( strchr(zRepositorySchemaDefaultReports,'%')==0 );
850 db_multi_exec(zRepositorySchemaDefaultReports /*works-like:""*/);
851 }
852
+1 -1
--- src/cookies.c
+++ src/cookies.c
@@ -173,11 +173,11 @@
173173
174174
/* Update the user preferences cookie, if necessary, and shut down this
175175
** module
176176
*/
177177
void cookie_render(void){
178
- if( cookies.bChanged ){
178
+ if( cookies.bChanged && P("udc")!=0 ){
179179
Blob new;
180180
int i;
181181
blob_init(&new, 0, 0);
182182
for(i=0;i<cookies.nParam;i++){
183183
if( i>0 ) blob_append(&new, ",", 1);
184184
--- src/cookies.c
+++ src/cookies.c
@@ -173,11 +173,11 @@
173
174 /* Update the user preferences cookie, if necessary, and shut down this
175 ** module
176 */
177 void cookie_render(void){
178 if( cookies.bChanged ){
179 Blob new;
180 int i;
181 blob_init(&new, 0, 0);
182 for(i=0;i<cookies.nParam;i++){
183 if( i>0 ) blob_append(&new, ",", 1);
184
--- src/cookies.c
+++ src/cookies.c
@@ -173,11 +173,11 @@
173
174 /* Update the user preferences cookie, if necessary, and shut down this
175 ** module
176 */
177 void cookie_render(void){
178 if( cookies.bChanged && P("udc")!=0 ){
179 Blob new;
180 int i;
181 blob_init(&new, 0, 0);
182 for(i=0;i<cookies.nParam;i++){
183 if( i>0 ) blob_append(&new, ",", 1);
184
--- src/cson_amalgamation.c
+++ src/cson_amalgamation.c
@@ -19,14 +19,14 @@
1919
# define JSON_PARSER_DLL_API __declspec(dllexport)
2020
# else
2121
# define JSON_PARSER_DLL_API __declspec(dllimport)
2222
# endif
2323
# else
24
-# define JSON_PARSER_DLL_API
24
+# define JSON_PARSER_DLL_API
2525
# endif
2626
#else
27
-# define JSON_PARSER_DLL_API
27
+# define JSON_PARSER_DLL_API
2828
#endif
2929
3030
/* Determine the integer type use to parse non-floating point numbers */
3131
#ifdef _WIN32
3232
typedef __int64 JSON_int_t;
@@ -34,22 +34,22 @@
3434
#define JSON_PARSER_INTEGER_SPRINTF_TOKEN "%I64d"
3535
#elif (__STDC_VERSION__ >= 199901L) || (HAVE_LONG_LONG == 1)
3636
typedef long long JSON_int_t;
3737
#define JSON_PARSER_INTEGER_SSCANF_TOKEN "%lld"
3838
#define JSON_PARSER_INTEGER_SPRINTF_TOKEN "%lld"
39
-#else
39
+#else
4040
typedef long JSON_int_t;
4141
#define JSON_PARSER_INTEGER_SSCANF_TOKEN "%ld"
4242
#define JSON_PARSER_INTEGER_SPRINTF_TOKEN "%ld"
4343
#endif
4444
4545
4646
#ifdef __cplusplus
4747
extern "C" {
48
-#endif
48
+#endif
4949
50
-typedef enum
50
+typedef enum
5151
{
5252
JSON_E_NONE = 0,
5353
JSON_E_INVALID_CHAR,
5454
JSON_E_INVALID_KEYWORD,
5555
JSON_E_INVALID_ESCAPE_SEQUENCE,
@@ -60,11 +60,11 @@
6060
JSON_E_EXPECTED_KEY,
6161
JSON_E_EXPECTED_COLON,
6262
JSON_E_OUT_OF_MEMORY
6363
} JSON_error;
6464
65
-typedef enum
65
+typedef enum
6666
{
6767
JSON_T_NONE = 0,
6868
JSON_T_ARRAY_BEGIN,
6969
JSON_T_ARRAY_END,
7070
JSON_T_OBJECT_BEGIN,
@@ -80,33 +80,33 @@
8080
} JSON_type;
8181
8282
typedef struct JSON_value_struct {
8383
union {
8484
JSON_int_t integer_value;
85
-
85
+
8686
double float_value;
87
-
87
+
8888
struct {
8989
const char* value;
9090
size_t length;
9191
} str;
9292
} vu;
9393
} JSON_value;
9494
9595
typedef struct JSON_parser_struct* JSON_parser;
9696
97
-/*! \brief JSON parser callback
97
+/*! \brief JSON parser callback
9898
9999
\param ctx The pointer passed to new_JSON_parser.
100
- \param type An element of JSON_type but not JSON_T_NONE.
100
+ \param type An element of JSON_type but not JSON_T_NONE.
101101
\param value A representation of the parsed value. This parameter is NULL for
102102
JSON_T_ARRAY_BEGIN, JSON_T_ARRAY_END, JSON_T_OBJECT_BEGIN, JSON_T_OBJECT_END,
103103
JSON_T_NULL, JSON_T_TRUE, and JSON_T_FALSE. String values are always returned
104104
as zero-terminated C strings.
105105
106106
\return Non-zero if parsing should continue, else zero.
107
-*/
107
+*/
108108
typedef int (*JSON_parser_callback)(void* ctx, int type, const JSON_value* value);
109109
110110
111111
/**
112112
A typedef for allocator functions semantically compatible with malloc().
@@ -115,11 +115,11 @@
115115
/**
116116
A typedef for deallocator functions semantically compatible with free().
117117
*/
118118
typedef void (*JSON_free_t)(void* mem);
119119
120
-/*! \brief The structure used to configure a JSON parser object
120
+/*! \brief The structure used to configure a JSON parser object
121121
*/
122122
typedef struct {
123123
/** Pointer to a callback, called when the parser has something to tell
124124
the user. This parameter may be NULL. In this case the input is
125125
merely checked for validity.
@@ -176,17 +176,17 @@
176176
177177
\param config. Used to configure the parser.
178178
*/
179179
JSON_PARSER_DLL_API void init_JSON_config(JSON_config * config);
180180
181
-/*! \brief Create a JSON parser object
181
+/*! \brief Create a JSON parser object
182182
183183
\param config. Used to configure the parser. Set to NULL to use
184184
the default configuration. See init_JSON_config. Its contents are
185185
copied by this function, so it need not outlive the returned
186186
object.
187
-
187
+
188188
\return The parser object, which is owned by the caller and must eventually
189189
be freed by calling delete_JSON_parser().
190190
*/
191191
JSON_PARSER_DLL_API JSON_parser new_JSON_parser(JSON_config const* config);
192192
@@ -200,16 +200,16 @@
200200
JSON_PARSER_DLL_API int JSON_parser_char(JSON_parser jc, int next_char);
201201
202202
/*! \brief Finalize parsing.
203203
204204
Call this method once after all input characters have been consumed.
205
-
205
+
206206
\return Non-zero, if all parsed characters are valid JSON, zero otherwise.
207207
*/
208208
JSON_PARSER_DLL_API int JSON_parser_done(JSON_parser jc);
209209
210
-/*! \brief Determine if a given string is valid JSON white space
210
+/*! \brief Determine if a given string is valid JSON white space
211211
212212
\return Non-zero if the string is valid, zero otherwise.
213213
*/
214214
JSON_PARSER_DLL_API int JSON_parser_is_legal_white_space_string(const char* s);
215215
@@ -226,12 +226,12 @@
226226
JSON_PARSER_DLL_API int JSON_parser_reset(JSON_parser jc);
227227
228228
229229
#ifdef __cplusplus
230230
}
231
-#endif
232
-
231
+#endif
232
+
233233
234234
#endif /* JSON_PARSER_H */
235235
/* end file parser/JSON_parser.h */
236236
/* begin file parser/JSON_parser.c */
237237
/*
@@ -322,11 +322,11 @@
322322
#endif
323323
324324
325325
#define true 1
326326
#define false 0
327
-#define __ -1 /* the universal error code */
327
+#define XX -1 /* the universal error code */
328328
329329
/* values chosen so that the object size is approx equal to one page (4K) */
330330
#ifndef JSON_PARSER_STACK_SIZE
331331
# define JSON_PARSER_STACK_SIZE 128
332332
#endif
@@ -414,14 +414,14 @@
414414
/*
415415
This array maps the 128 ASCII characters into character classes.
416416
The remaining Unicode characters should be mapped to C_ETC.
417417
Non-whitespace control characters are errors.
418418
*/
419
- __, __, __, __, __, __, __, __,
420
- __, C_WHITE, C_WHITE, __, __, C_WHITE, __, __,
421
- __, __, __, __, __, __, __, __,
422
- __, __, __, __, __, __, __, __,
419
+ XX, XX, XX, XX, XX, XX, XX, XX,
420
+ XX, C_WHITE, C_WHITE, XX, XX, C_WHITE, XX, XX,
421
+ XX, XX, XX, XX, XX, XX, XX, XX,
422
+ XX, XX, XX, XX, XX, XX, XX, XX,
423423
424424
C_SPACE, C_ETC, C_QUOTE, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC,
425425
C_ETC, C_ETC, C_STAR, C_PLUS, C_COMMA, C_MINUS, C_POINT, C_SLASH,
426426
C_ZERO, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT,
427427
C_DIGIT, C_DIGIT, C_COLON, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC,
@@ -448,11 +448,11 @@
448448
KE, /* key */
449449
CO, /* colon */
450450
VA, /* value */
451451
AR, /* array */
452452
ST, /* string */
453
- ES, /* escape */
453
+ ESC, /* escape */
454454
U1, /* u1 */
455455
U2, /* u2 */
456456
U3, /* u3 */
457457
U4, /* u4 */
458458
MI, /* minus */
@@ -506,46 +506,46 @@
506506
negative number. A JSON text is accepted if at the end of the text the
507507
state is OK and if the mode is MODE_DONE.
508508
509509
white 1-9 ABCDF etc
510510
space | { } [ ] : , " \ / + - . 0 | a b c d e f l n r s t u | E | * */
511
-/*start GO*/ {GO,GO,-6,__,-5,__,__,__,__,__,CB,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
512
-/*ok OK*/ {OK,OK,__,-8,__,-7,__,-3,__,__,CB,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
513
-/*object OB*/ {OB,OB,__,-9,__,__,__,__,SB,__,CB,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
514
-/*key KE*/ {KE,KE,__,__,__,__,__,__,SB,__,CB,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
515
-/*colon CO*/ {CO,CO,__,__,__,__,-2,__,__,__,CB,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
516
-/*value VA*/ {VA,VA,-6,__,-5,__,__,__,SB,__,CB,__,MX,__,ZX,IX,__,__,__,__,__,FA,__,NU,__,__,TR,__,__,__,__,__},
517
-/*array AR*/ {AR,AR,-6,__,-5,-7,__,__,SB,__,CB,__,MX,__,ZX,IX,__,__,__,__,__,FA,__,NU,__,__,TR,__,__,__,__,__},
518
-/*string ST*/ {ST,__,ST,ST,ST,ST,ST,ST,-4,EX,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST},
519
-/*escape ES*/ {__,__,__,__,__,__,__,__,ST,ST,ST,__,__,__,__,__,__,ST,__,__,__,ST,__,ST,ST,__,ST,U1,__,__,__,__},
520
-/*u1 U1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,U2,U2,U2,U2,U2,U2,U2,U2,__,__,__,__,__,__,U2,U2,__,__},
521
-/*u2 U2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,U3,U3,U3,U3,U3,U3,U3,U3,__,__,__,__,__,__,U3,U3,__,__},
522
-/*u3 U3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,U4,U4,U4,U4,U4,U4,U4,U4,__,__,__,__,__,__,U4,U4,__,__},
523
-/*u4 U4*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,UC,UC,UC,UC,UC,UC,UC,UC,__,__,__,__,__,__,UC,UC,__,__},
524
-/*minus MI*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,ZE,IT,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
525
-/*zero ZE*/ {OK,OK,__,-8,__,-7,__,-3,__,__,CB,__,__,DF,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
526
-/*int IT*/ {OK,OK,__,-8,__,-7,__,-3,__,__,CB,__,__,DF,IT,IT,__,__,__,__,DE,__,__,__,__,__,__,__,__,DE,__,__},
527
-/*frac FR*/ {OK,OK,__,-8,__,-7,__,-3,__,__,CB,__,__,__,FR,FR,__,__,__,__,E1,__,__,__,__,__,__,__,__,E1,__,__},
528
-/*e E1*/ {__,__,__,__,__,__,__,__,__,__,__,E2,E2,__,E3,E3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
529
-/*ex E2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,E3,E3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
530
-/*exp E3*/ {OK,OK,__,-8,__,-7,__,-3,__,__,__,__,__,__,E3,E3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
531
-/*tr T1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,T2,__,__,__,__,__,__,__},
532
-/*tru T2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,T3,__,__,__,__},
533
-/*true T3*/ {__,__,__,__,__,__,__,__,__,__,CB,__,__,__,__,__,__,__,__,__,OK,__,__,__,__,__,__,__,__,__,__,__},
534
-/*fa F1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F2,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
535
-/*fal F2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F3,__,__,__,__,__,__,__,__,__},
536
-/*fals F3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F4,__,__,__,__,__,__},
537
-/*false F4*/ {__,__,__,__,__,__,__,__,__,__,CB,__,__,__,__,__,__,__,__,__,OK,__,__,__,__,__,__,__,__,__,__,__},
538
-/*nu N1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,N2,__,__,__,__},
539
-/*nul N2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,N3,__,__,__,__,__,__,__,__,__},
540
-/*null N3*/ {__,__,__,__,__,__,__,__,__,__,CB,__,__,__,__,__,__,__,__,__,__,__,OK,__,__,__,__,__,__,__,__,__},
541
-/*/ C1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,C2},
511
+/*start GO*/ {GO,GO,-6,XX,-5,XX,XX,XX,XX,XX,CB,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
512
+/*ok OK*/ {OK,OK,XX,-8,XX,-7,XX,-3,XX,XX,CB,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
513
+/*object OB*/ {OB,OB,XX,-9,XX,XX,XX,XX,SB,XX,CB,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
514
+/*key KE*/ {KE,KE,XX,XX,XX,XX,XX,XX,SB,XX,CB,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
515
+/*colon CO*/ {CO,CO,XX,XX,XX,XX,-2,XX,XX,XX,CB,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
516
+/*value VA*/ {VA,VA,-6,XX,-5,XX,XX,XX,SB,XX,CB,XX,MX,XX,ZX,IX,XX,XX,XX,XX,XX,FA,XX,NU,XX,XX,TR,XX,XX,XX,XX,XX},
517
+/*array AR*/ {AR,AR,-6,XX,-5,-7,XX,XX,SB,XX,CB,XX,MX,XX,ZX,IX,XX,XX,XX,XX,XX,FA,XX,NU,XX,XX,TR,XX,XX,XX,XX,XX},
518
+/*string ST*/ {ST,XX,ST,ST,ST,ST,ST,ST,-4,EX,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST},
519
+/*escape ES*/ {XX,XX,XX,XX,XX,XX,XX,XX,ST,ST,ST,XX,XX,XX,XX,XX,XX,ST,XX,XX,XX,ST,XX,ST,ST,XX,ST,U1,XX,XX,XX,XX},
520
+/*u1 U1*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,U2,U2,U2,U2,U2,U2,U2,U2,XX,XX,XX,XX,XX,XX,U2,U2,XX,XX},
521
+/*u2 U2*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,U3,U3,U3,U3,U3,U3,U3,U3,XX,XX,XX,XX,XX,XX,U3,U3,XX,XX},
522
+/*u3 U3*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,U4,U4,U4,U4,U4,U4,U4,U4,XX,XX,XX,XX,XX,XX,U4,U4,XX,XX},
523
+/*u4 U4*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,UC,UC,UC,UC,UC,UC,UC,UC,XX,XX,XX,XX,XX,XX,UC,UC,XX,XX},
524
+/*minus MI*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,ZE,IT,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
525
+/*zero ZE*/ {OK,OK,XX,-8,XX,-7,XX,-3,XX,XX,CB,XX,XX,DF,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
526
+/*int IT*/ {OK,OK,XX,-8,XX,-7,XX,-3,XX,XX,CB,XX,XX,DF,IT,IT,XX,XX,XX,XX,DE,XX,XX,XX,XX,XX,XX,XX,XX,DE,XX,XX},
527
+/*frac FR*/ {OK,OK,XX,-8,XX,-7,XX,-3,XX,XX,CB,XX,XX,XX,FR,FR,XX,XX,XX,XX,E1,XX,XX,XX,XX,XX,XX,XX,XX,E1,XX,XX},
528
+/*e E1*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,E2,E2,XX,E3,E3,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
529
+/*ex E2*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,E3,E3,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
530
+/*exp E3*/ {OK,OK,XX,-8,XX,-7,XX,-3,XX,XX,XX,XX,XX,XX,E3,E3,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
531
+/*tr T1*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,T2,XX,XX,XX,XX,XX,XX,XX},
532
+/*tru T2*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,T3,XX,XX,XX,XX},
533
+/*true T3*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,CB,XX,XX,XX,XX,XX,XX,XX,XX,XX,OK,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
534
+/*fa F1*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,F2,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
535
+/*fal F2*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,F3,XX,XX,XX,XX,XX,XX,XX,XX,XX},
536
+/*fals F3*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,F4,XX,XX,XX,XX,XX,XX},
537
+/*false F4*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,CB,XX,XX,XX,XX,XX,XX,XX,XX,XX,OK,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
538
+/*nu N1*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,N2,XX,XX,XX,XX},
539
+/*nul N2*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,N3,XX,XX,XX,XX,XX,XX,XX,XX,XX},
540
+/*null N3*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,CB,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,OK,XX,XX,XX,XX,XX,XX,XX,XX,XX},
541
+/*/ C1*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,C2},
542542
/*/star C2*/ {C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C3},
543543
/** C3*/ {C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,CE,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C3},
544
-/*_. FX*/ {OK,OK,__,-8,__,-7,__,-3,__,__,__,__,__,__,FR,FR,__,__,__,__,E1,__,__,__,__,__,__,__,__,E1,__,__},
545
-/*\ D1*/ {__,__,__,__,__,__,__,__,__,D2,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
546
-/*\ D2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,U1,__,__,__,__},
544
+/*_. FX*/ {OK,OK,XX,-8,XX,-7,XX,-3,XX,XX,XX,XX,XX,XX,FR,FR,XX,XX,XX,XX,E1,XX,XX,XX,XX,XX,XX,XX,XX,E1,XX,XX},
545
+/*\ D1*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,D2,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
546
+/*\ D2*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,U1,XX,XX,XX,XX},
547547
};
548548
549549
550550
/*
551551
These modes can be pushed on the stack.
@@ -1050,11 +1050,11 @@
10501050
}
10511051
if (next_char >= 128) {
10521052
next_class = C_ETC;
10531053
} else {
10541054
next_class = ascii_class[next_char];
1055
- if (next_class <= __) {
1055
+ if (next_class <= XX) {
10561056
set_error(jc);
10571057
return false;
10581058
}
10591059
}
10601060
@@ -1090,11 +1090,11 @@
10901090
}
10911091
break;
10921092
/* escaped char */
10931093
case EX:
10941094
jc->escaped = 1;
1095
- jc->state = ES;
1095
+ jc->state = ESC;
10961096
break;
10971097
/* integer detected by minus */
10981098
case MX:
10991099
jc->type = JSON_T_INTEGER;
11001100
jc->state = MI;
@@ -1401,10 +1401,14 @@
14011401
config->malloc = malloc;
14021402
config->free = free;
14031403
}
14041404
}
14051405
1406
+#undef XX
1407
+#undef COUNTOF
1408
+#undef parse_buffer_clear
1409
+#undef parse_buffer_pop_back_char
14061410
/* end file parser/JSON_parser.c */
14071411
/* begin file ./cson.c */
14081412
#include <assert.h>
14091413
#include <stdlib.h> /* malloc()/free() */
14101414
#include <string.h>
@@ -1431,11 +1435,11 @@
14311435
#if defined(__cplusplus)
14321436
extern "C" {
14331437
#endif
14341438
14351439
1436
-
1440
+
14371441
/**
14381442
This type holds the "vtbl" for type-specific operations when
14391443
working with cson_value objects.
14401444
14411445
All cson_values of a given logical type share a pointer to a single
@@ -1583,11 +1587,11 @@
15831587
*/
15841588
#define CSON_CAST(T,V) ((T*)((V)->value))
15851589
/**
15861590
Assumes V is a pointer to memory which is allocated as part of a
15871591
cson_value instance (the bytes immediately after that part).
1588
- Returns a pointer a cson_value by subtracting sizeof(cson_value)
1592
+ Returns a pointer a a cson_value by subtracting sizeof(cson_value)
15891593
from that address and casting it to a (cson_value*)
15901594
*/
15911595
#define CSON_VCAST(V) ((cson_value *)(((unsigned char *)(V))-sizeof(cson_value)))
15921596
15931597
/**
@@ -1607,11 +1611,11 @@
16071611
#define CSON_OBJ(V) CSON_CAST(cson_object,(V))
16081612
#define CSON_ARRAY(V) CSON_CAST(cson_array,(V))
16091613
16101614
/**
16111615
Holds special shared "constant" (though they are non-const)
1612
- values.
1616
+ values.
16131617
*/
16141618
static struct CSON_EMPTY_HOLDER_
16151619
{
16161620
char trueValue;
16171621
cson_string stringValue;
@@ -1620,17 +1624,17 @@
16201624
cson_string_empty_m
16211625
};
16221626
16231627
/**
16241628
Indexes into the CSON_SPECIAL_VALUES array.
1625
-
1629
+
16261630
If this enum changes in any way,
16271631
makes damned sure that CSON_SPECIAL_VALUES is updated
16281632
to match!!!
16291633
*/
16301634
enum CSON_INTERNAL_VALUES {
1631
-
1635
+
16321636
CSON_VAL_UNDEF = 0,
16331637
CSON_VAL_NULL = 1,
16341638
CSON_VAL_TRUE = 2,
16351639
CSON_VAL_FALSE = 3,
16361640
CSON_VAL_INT_0 = 4,
@@ -1642,11 +1646,11 @@
16421646
/**
16431647
Some "special" shared cson_value instances.
16441648
16451649
These values MUST be initialized in the order specified
16461650
by the CSON_INTERNAL_VALUES enum.
1647
-
1651
+
16481652
Note that they are not const because they are used as
16491653
shared-allocation objects in non-const contexts. However, the
16501654
public API provides no way to modifying them, and clients who
16511655
modify values directly are subject to The Wrath of Undefined
16521656
Behaviour.
@@ -1665,11 +1669,11 @@
16651669
16661670
/**
16671671
Returns non-0 (true) if m is one of our special
16681672
"built-in" values, e.g. from CSON_SPECIAL_VALUES and some
16691673
"empty" values.
1670
-
1674
+
16711675
If this returns true, m MUST NOT be free()d!
16721676
*/
16731677
static char cson_value_is_builtin( void const * m )
16741678
{
16751679
if((m >= (void const *)&CSON_EMPTY_HOLDER)
@@ -2183,11 +2187,11 @@
21832187
static int cson_value_list_visit( cson_value_list * self,
21842188
int (*visitor)(cson_value * obj, void * visitorState ),
21852189
void * visitorState );
21862190
#endif
21872191
#endif
2188
-
2192
+
21892193
#if 0
21902194
# define LIST_T cson_value_list
21912195
# define VALUE_T cson_value *
21922196
# define VALUE_T_IS_PTR 1
21932197
# define LIST_T cson_kvp_list
@@ -2362,11 +2366,11 @@
23622366
return cson_value_object_alloc();
23632367
}
23642368
23652369
cson_object * cson_new_object()
23662370
{
2367
-
2371
+
23682372
return cson_value_get_object( cson_value_new_object() );
23692373
}
23702374
23712375
cson_value * cson_value_new_array()
23722376
{
@@ -2608,11 +2612,11 @@
26082612
{
26092613
cson_int_t i = 0;
26102614
int rc = 0;
26112615
switch(val->api->typeID)
26122616
{
2613
- case CSON_TYPE_UNDEF:
2617
+ case CSON_TYPE_UNDEF:
26142618
case CSON_TYPE_NULL:
26152619
i = 0;
26162620
break;
26172621
case CSON_TYPE_BOOL: {
26182622
char b = 0;
@@ -2661,11 +2665,11 @@
26612665
{
26622666
cson_double_t d = 0.0;
26632667
int rc = 0;
26642668
switch(val->api->typeID)
26652669
{
2666
- case CSON_TYPE_UNDEF:
2670
+ case CSON_TYPE_UNDEF:
26672671
case CSON_TYPE_NULL:
26682672
d = 0;
26692673
break;
26702674
case CSON_TYPE_BOOL: {
26712675
char b = 0;
@@ -2791,11 +2795,11 @@
27912795
#if 0
27922796
/**
27932797
Removes and returns the last value from the given array,
27942798
shrinking its size by 1. Returns NULL if ar is NULL,
27952799
ar->list.count is 0, or the element at that index is NULL.
2796
-
2800
+
27972801
27982802
If removeRef is true then cson_value_free() is called to remove
27992803
ar's reference count for the value. In that case NULL is returned,
28002804
even if the object still has live references. If removeRef is false
28012805
then the caller takes over ownership of that reference count point.
@@ -2858,11 +2862,11 @@
28582862
#if !defined(NDEBUG) && CSON_VOID_PTR_IS_BIG
28592863
assert( sizeof(cson_int_t) <= sizeof(void *) );
28602864
#endif
28612865
if( c )
28622866
{
2863
- memcpy(CSON_INT(c), &v, sizeof(v));
2867
+ memcpy( CSON_INT(c), &v, sizeof(v) );
28642868
}
28652869
return c;
28662870
}
28672871
}
28682872
@@ -2877,11 +2881,11 @@
28772881
else
28782882
{
28792883
cson_value * c = cson_value_new(CSON_TYPE_DOUBLE,0);
28802884
if( c )
28812885
{
2882
- memcpy(CSON_DBL(c), &v, sizeof(v));
2886
+ memcpy( CSON_DBL(c), &v, sizeof(v) );
28832887
}
28842888
return c;
28852889
}
28862890
}
28872891
@@ -3068,11 +3072,11 @@
30683072
qsort( obj->kvp.list, obj->kvp.count, sizeof(cson_kvp*),
30693073
cson_kvp_cmp );
30703074
}
30713075
30723076
}
3073
-#endif
3077
+#endif
30743078
30753079
int cson_object_unset( cson_object * obj, char const * key )
30763080
{
30773081
if( ! obj || !key || !*key ) return cson_rc.ArgError;
30783082
else
@@ -3238,11 +3242,11 @@
32383242
using p->key. In any other case cson_rc.InternalError is returned.
32393243
32403244
Returns cson_rc.AllocError if an allocation fails.
32413245
32423246
Returns 0 on success. On error, parsing must be ceased immediately.
3243
-
3247
+
32443248
Ownership of val is ALWAYS TRANSFERED to this function. If this
32453249
function fails, val will be cleaned up and destroyed. (This
32463250
simplifies error handling in the core parser.)
32473251
*/
32483252
static int cson_parser_set_key( cson_parser * p, cson_value * val )
@@ -3485,11 +3489,11 @@
34853489
++p->totalKeyCount;
34863490
break;
34873491
}
34883492
case JSON_T_STRING: {
34893493
cson_value * v = cson_value_new_string( value->vu.str.value, value->vu.str.length );
3490
- rc = ( NULL == v )
3494
+ rc = ( NULL == v )
34913495
? cson_rc.AllocError
34923496
: cson_parser_push_value( p, v );
34933497
break;
34943498
}
34953499
default:
@@ -3532,11 +3536,11 @@
35323536
To properly take over ownership of the parser's root node on a
35333537
successful parse:
35343538
35353539
- Copy p->root's pointer and set p->root to NULL.
35363540
- Eventually free up p->root with cson_value_free().
3537
-
3541
+
35383542
If you do not set p->root to NULL, p->root will be freed along with
35393543
any other items inserted into it (or under it) during the parsing
35403544
process.
35413545
*/
35423546
static int cson_parser_clean( cson_parser * p )
@@ -3571,11 +3575,11 @@
35713575
int rc = 0;
35723576
unsigned int len = 1;
35733577
cson_parse_info info = info_ ? *info_ : cson_parse_info_empty;
35743578
cson_parser p = cson_parser_empty;
35753579
if( ! tgt || ! src ) return cson_rc.ArgError;
3576
-
3580
+
35773581
{
35783582
JSON_config jopt = {0};
35793583
init_JSON_config( &jopt );
35803584
jopt.allow_comments = opt.allowComments;
35813585
jopt.depth = opt.maxDepth;
@@ -3780,11 +3784,11 @@
37803784
unsigned char const * end = (unsigned char const *)(str ? (str + len) : NULL);
37813785
unsigned char const * next = NULL;
37823786
int ch;
37833787
unsigned char clen = 0;
37843788
char escChar[3] = {'\\',0,0};
3785
- enum { UBLen = 13 };
3789
+ enum { UBLen = 20 };
37863790
char ubuf[UBLen];
37873791
int rc = 0;
37883792
rc = f(state, "\"", 1 );
37893793
for( ; (pos < end) && (0 == rc); pos += clen )
37903794
{
@@ -4640,11 +4644,11 @@
46404644
#endif
46414645
#undef TRY_SHARING
46424646
cson_value_add_reference(rc);
46434647
return rc;
46444648
}
4645
-
4649
+
46464650
static cson_value * cson_value_clone_array( cson_value const * orig )
46474651
{
46484652
unsigned int i = 0;
46494653
cson_array const * asrc = cson_value_get_array( orig );
46504654
unsigned int alen = cson_array_length_get( asrc );
@@ -4680,11 +4684,11 @@
46804684
cson_value_free(cl)/*remove our artificial reference */;
46814685
}
46824686
}
46834687
return destV;
46844688
}
4685
-
4689
+
46864690
static cson_value * cson_value_clone_object( cson_value const * orig )
46874691
{
46884692
cson_object const * src = cson_value_get_object( orig );
46894693
cson_value * destV = NULL;
46904694
cson_object * dest = NULL;
@@ -4834,11 +4838,11 @@
48344838
}
48354839
case CSON_TYPE_STRING: {
48364840
cson_string const * jstr = cson_value_get_string(orig);
48374841
unsigned const int slen = cson_string_length_bytes( jstr );
48384842
assert( NULL != jstr );
4839
- v = cson_strdup( cson_string_cstr( jstr ), slen );
4843
+ v = cson_strdup( cson_string_cstr( jstr ), slen );
48404844
break;
48414845
}
48424846
case CSON_TYPE_INTEGER: {
48434847
char buf[BufSize] = {0};
48444848
if( 0 < sprintf( v, "%"CSON_INT_T_PFMT, cson_value_get_integer(orig)) )
@@ -4887,11 +4891,11 @@
48874891
}
48884892
case CSON_TYPE_STRING: {
48894893
cson_string const * jstr = cson_value_get_string(orig);
48904894
unsigned const int slen = cson_string_length_bytes( jstr );
48914895
assert( NULL != jstr );
4892
- v = cson_strdup( cson_string_cstr( jstr ), slen );
4896
+ v = cson_strdup( cson_string_cstr( jstr ), slen );
48934897
break;
48944898
}
48954899
case CSON_TYPE_INTEGER: {
48964900
char buf[BufSize] = {0};
48974901
if( 0 < sprintf( v, "%"CSON_INT_T_PFMT, cson_value_get_integer(orig)) )
@@ -5351,11 +5355,11 @@
53515355
int rc = 0;
53525356
int colCount = 0;
53535357
assert(st);
53545358
colCount = sqlite3_column_count(st);
53555359
if( colCount <= 0 ) return NULL;
5356
-
5360
+
53575361
aryV = cson_value_new_array();
53585362
if( ! aryV ) return NULL;
53595363
ary = cson_value_get_array(aryV);
53605364
assert(ary);
53615365
for( i = 0; (0 ==rc) && (i < colCount); ++i )
@@ -5491,11 +5495,11 @@
54915495
aryV = NULL;
54925496
end:
54935497
return aryV;
54945498
}
54955499
5496
-
5500
+
54975501
/**
54985502
Internal impl of cson_sqlite3_stmt_to_json() when the 'fat'
54995503
parameter is non-0.
55005504
*/
55015505
static int cson_sqlite3_stmt_to_json_fat( sqlite3_stmt * st, cson_value ** tgt )
@@ -5636,11 +5640,11 @@
56365640
int rc = sqlite3_prepare_v2( db, sql, -1, &st, NULL );
56375641
if( 0 != rc ) return cson_rc.IOError /* FIXME: Better error code? */;
56385642
rc = cson_sqlite3_stmt_to_json( st, tgt, fat );
56395643
sqlite3_finalize( st );
56405644
return rc;
5641
- }
5645
+ }
56425646
}
56435647
56445648
int cson_sqlite3_bind_value( sqlite3_stmt * st, int ndx, cson_value const * v )
56455649
{
56465650
int rc = 0;
56475651
--- src/cson_amalgamation.c
+++ src/cson_amalgamation.c
@@ -19,14 +19,14 @@
19 # define JSON_PARSER_DLL_API __declspec(dllexport)
20 # else
21 # define JSON_PARSER_DLL_API __declspec(dllimport)
22 # endif
23 # else
24 # define JSON_PARSER_DLL_API
25 # endif
26 #else
27 # define JSON_PARSER_DLL_API
28 #endif
29
30 /* Determine the integer type use to parse non-floating point numbers */
31 #ifdef _WIN32
32 typedef __int64 JSON_int_t;
@@ -34,22 +34,22 @@
34 #define JSON_PARSER_INTEGER_SPRINTF_TOKEN "%I64d"
35 #elif (__STDC_VERSION__ >= 199901L) || (HAVE_LONG_LONG == 1)
36 typedef long long JSON_int_t;
37 #define JSON_PARSER_INTEGER_SSCANF_TOKEN "%lld"
38 #define JSON_PARSER_INTEGER_SPRINTF_TOKEN "%lld"
39 #else
40 typedef long JSON_int_t;
41 #define JSON_PARSER_INTEGER_SSCANF_TOKEN "%ld"
42 #define JSON_PARSER_INTEGER_SPRINTF_TOKEN "%ld"
43 #endif
44
45
46 #ifdef __cplusplus
47 extern "C" {
48 #endif
49
50 typedef enum
51 {
52 JSON_E_NONE = 0,
53 JSON_E_INVALID_CHAR,
54 JSON_E_INVALID_KEYWORD,
55 JSON_E_INVALID_ESCAPE_SEQUENCE,
@@ -60,11 +60,11 @@
60 JSON_E_EXPECTED_KEY,
61 JSON_E_EXPECTED_COLON,
62 JSON_E_OUT_OF_MEMORY
63 } JSON_error;
64
65 typedef enum
66 {
67 JSON_T_NONE = 0,
68 JSON_T_ARRAY_BEGIN,
69 JSON_T_ARRAY_END,
70 JSON_T_OBJECT_BEGIN,
@@ -80,33 +80,33 @@
80 } JSON_type;
81
82 typedef struct JSON_value_struct {
83 union {
84 JSON_int_t integer_value;
85
86 double float_value;
87
88 struct {
89 const char* value;
90 size_t length;
91 } str;
92 } vu;
93 } JSON_value;
94
95 typedef struct JSON_parser_struct* JSON_parser;
96
97 /*! \brief JSON parser callback
98
99 \param ctx The pointer passed to new_JSON_parser.
100 \param type An element of JSON_type but not JSON_T_NONE.
101 \param value A representation of the parsed value. This parameter is NULL for
102 JSON_T_ARRAY_BEGIN, JSON_T_ARRAY_END, JSON_T_OBJECT_BEGIN, JSON_T_OBJECT_END,
103 JSON_T_NULL, JSON_T_TRUE, and JSON_T_FALSE. String values are always returned
104 as zero-terminated C strings.
105
106 \return Non-zero if parsing should continue, else zero.
107 */
108 typedef int (*JSON_parser_callback)(void* ctx, int type, const JSON_value* value);
109
110
111 /**
112 A typedef for allocator functions semantically compatible with malloc().
@@ -115,11 +115,11 @@
115 /**
116 A typedef for deallocator functions semantically compatible with free().
117 */
118 typedef void (*JSON_free_t)(void* mem);
119
120 /*! \brief The structure used to configure a JSON parser object
121 */
122 typedef struct {
123 /** Pointer to a callback, called when the parser has something to tell
124 the user. This parameter may be NULL. In this case the input is
125 merely checked for validity.
@@ -176,17 +176,17 @@
176
177 \param config. Used to configure the parser.
178 */
179 JSON_PARSER_DLL_API void init_JSON_config(JSON_config * config);
180
181 /*! \brief Create a JSON parser object
182
183 \param config. Used to configure the parser. Set to NULL to use
184 the default configuration. See init_JSON_config. Its contents are
185 copied by this function, so it need not outlive the returned
186 object.
187
188 \return The parser object, which is owned by the caller and must eventually
189 be freed by calling delete_JSON_parser().
190 */
191 JSON_PARSER_DLL_API JSON_parser new_JSON_parser(JSON_config const* config);
192
@@ -200,16 +200,16 @@
200 JSON_PARSER_DLL_API int JSON_parser_char(JSON_parser jc, int next_char);
201
202 /*! \brief Finalize parsing.
203
204 Call this method once after all input characters have been consumed.
205
206 \return Non-zero, if all parsed characters are valid JSON, zero otherwise.
207 */
208 JSON_PARSER_DLL_API int JSON_parser_done(JSON_parser jc);
209
210 /*! \brief Determine if a given string is valid JSON white space
211
212 \return Non-zero if the string is valid, zero otherwise.
213 */
214 JSON_PARSER_DLL_API int JSON_parser_is_legal_white_space_string(const char* s);
215
@@ -226,12 +226,12 @@
226 JSON_PARSER_DLL_API int JSON_parser_reset(JSON_parser jc);
227
228
229 #ifdef __cplusplus
230 }
231 #endif
232
233
234 #endif /* JSON_PARSER_H */
235 /* end file parser/JSON_parser.h */
236 /* begin file parser/JSON_parser.c */
237 /*
@@ -322,11 +322,11 @@
322 #endif
323
324
325 #define true 1
326 #define false 0
327 #define __ -1 /* the universal error code */
328
329 /* values chosen so that the object size is approx equal to one page (4K) */
330 #ifndef JSON_PARSER_STACK_SIZE
331 # define JSON_PARSER_STACK_SIZE 128
332 #endif
@@ -414,14 +414,14 @@
414 /*
415 This array maps the 128 ASCII characters into character classes.
416 The remaining Unicode characters should be mapped to C_ETC.
417 Non-whitespace control characters are errors.
418 */
419 __, __, __, __, __, __, __, __,
420 __, C_WHITE, C_WHITE, __, __, C_WHITE, __, __,
421 __, __, __, __, __, __, __, __,
422 __, __, __, __, __, __, __, __,
423
424 C_SPACE, C_ETC, C_QUOTE, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC,
425 C_ETC, C_ETC, C_STAR, C_PLUS, C_COMMA, C_MINUS, C_POINT, C_SLASH,
426 C_ZERO, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT,
427 C_DIGIT, C_DIGIT, C_COLON, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC,
@@ -448,11 +448,11 @@
448 KE, /* key */
449 CO, /* colon */
450 VA, /* value */
451 AR, /* array */
452 ST, /* string */
453 ES, /* escape */
454 U1, /* u1 */
455 U2, /* u2 */
456 U3, /* u3 */
457 U4, /* u4 */
458 MI, /* minus */
@@ -506,46 +506,46 @@
506 negative number. A JSON text is accepted if at the end of the text the
507 state is OK and if the mode is MODE_DONE.
508
509 white 1-9 ABCDF etc
510 space | { } [ ] : , " \ / + - . 0 | a b c d e f l n r s t u | E | * */
511 /*start GO*/ {GO,GO,-6,__,-5,__,__,__,__,__,CB,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
512 /*ok OK*/ {OK,OK,__,-8,__,-7,__,-3,__,__,CB,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
513 /*object OB*/ {OB,OB,__,-9,__,__,__,__,SB,__,CB,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
514 /*key KE*/ {KE,KE,__,__,__,__,__,__,SB,__,CB,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
515 /*colon CO*/ {CO,CO,__,__,__,__,-2,__,__,__,CB,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
516 /*value VA*/ {VA,VA,-6,__,-5,__,__,__,SB,__,CB,__,MX,__,ZX,IX,__,__,__,__,__,FA,__,NU,__,__,TR,__,__,__,__,__},
517 /*array AR*/ {AR,AR,-6,__,-5,-7,__,__,SB,__,CB,__,MX,__,ZX,IX,__,__,__,__,__,FA,__,NU,__,__,TR,__,__,__,__,__},
518 /*string ST*/ {ST,__,ST,ST,ST,ST,ST,ST,-4,EX,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST},
519 /*escape ES*/ {__,__,__,__,__,__,__,__,ST,ST,ST,__,__,__,__,__,__,ST,__,__,__,ST,__,ST,ST,__,ST,U1,__,__,__,__},
520 /*u1 U1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,U2,U2,U2,U2,U2,U2,U2,U2,__,__,__,__,__,__,U2,U2,__,__},
521 /*u2 U2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,U3,U3,U3,U3,U3,U3,U3,U3,__,__,__,__,__,__,U3,U3,__,__},
522 /*u3 U3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,U4,U4,U4,U4,U4,U4,U4,U4,__,__,__,__,__,__,U4,U4,__,__},
523 /*u4 U4*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,UC,UC,UC,UC,UC,UC,UC,UC,__,__,__,__,__,__,UC,UC,__,__},
524 /*minus MI*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,ZE,IT,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
525 /*zero ZE*/ {OK,OK,__,-8,__,-7,__,-3,__,__,CB,__,__,DF,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
526 /*int IT*/ {OK,OK,__,-8,__,-7,__,-3,__,__,CB,__,__,DF,IT,IT,__,__,__,__,DE,__,__,__,__,__,__,__,__,DE,__,__},
527 /*frac FR*/ {OK,OK,__,-8,__,-7,__,-3,__,__,CB,__,__,__,FR,FR,__,__,__,__,E1,__,__,__,__,__,__,__,__,E1,__,__},
528 /*e E1*/ {__,__,__,__,__,__,__,__,__,__,__,E2,E2,__,E3,E3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
529 /*ex E2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,E3,E3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
530 /*exp E3*/ {OK,OK,__,-8,__,-7,__,-3,__,__,__,__,__,__,E3,E3,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
531 /*tr T1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,T2,__,__,__,__,__,__,__},
532 /*tru T2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,T3,__,__,__,__},
533 /*true T3*/ {__,__,__,__,__,__,__,__,__,__,CB,__,__,__,__,__,__,__,__,__,OK,__,__,__,__,__,__,__,__,__,__,__},
534 /*fa F1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F2,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
535 /*fal F2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F3,__,__,__,__,__,__,__,__,__},
536 /*fals F3*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,F4,__,__,__,__,__,__},
537 /*false F4*/ {__,__,__,__,__,__,__,__,__,__,CB,__,__,__,__,__,__,__,__,__,OK,__,__,__,__,__,__,__,__,__,__,__},
538 /*nu N1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,N2,__,__,__,__},
539 /*nul N2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,N3,__,__,__,__,__,__,__,__,__},
540 /*null N3*/ {__,__,__,__,__,__,__,__,__,__,CB,__,__,__,__,__,__,__,__,__,__,__,OK,__,__,__,__,__,__,__,__,__},
541 /*/ C1*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,C2},
542 /*/star C2*/ {C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C3},
543 /** C3*/ {C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,CE,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C3},
544 /*_. FX*/ {OK,OK,__,-8,__,-7,__,-3,__,__,__,__,__,__,FR,FR,__,__,__,__,E1,__,__,__,__,__,__,__,__,E1,__,__},
545 /*\ D1*/ {__,__,__,__,__,__,__,__,__,D2,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__},
546 /*\ D2*/ {__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,__,U1,__,__,__,__},
547 };
548
549
550 /*
551 These modes can be pushed on the stack.
@@ -1050,11 +1050,11 @@
1050 }
1051 if (next_char >= 128) {
1052 next_class = C_ETC;
1053 } else {
1054 next_class = ascii_class[next_char];
1055 if (next_class <= __) {
1056 set_error(jc);
1057 return false;
1058 }
1059 }
1060
@@ -1090,11 +1090,11 @@
1090 }
1091 break;
1092 /* escaped char */
1093 case EX:
1094 jc->escaped = 1;
1095 jc->state = ES;
1096 break;
1097 /* integer detected by minus */
1098 case MX:
1099 jc->type = JSON_T_INTEGER;
1100 jc->state = MI;
@@ -1401,10 +1401,14 @@
1401 config->malloc = malloc;
1402 config->free = free;
1403 }
1404 }
1405
 
 
 
 
1406 /* end file parser/JSON_parser.c */
1407 /* begin file ./cson.c */
1408 #include <assert.h>
1409 #include <stdlib.h> /* malloc()/free() */
1410 #include <string.h>
@@ -1431,11 +1435,11 @@
1431 #if defined(__cplusplus)
1432 extern "C" {
1433 #endif
1434
1435
1436
1437 /**
1438 This type holds the "vtbl" for type-specific operations when
1439 working with cson_value objects.
1440
1441 All cson_values of a given logical type share a pointer to a single
@@ -1583,11 +1587,11 @@
1583 */
1584 #define CSON_CAST(T,V) ((T*)((V)->value))
1585 /**
1586 Assumes V is a pointer to memory which is allocated as part of a
1587 cson_value instance (the bytes immediately after that part).
1588 Returns a pointer a cson_value by subtracting sizeof(cson_value)
1589 from that address and casting it to a (cson_value*)
1590 */
1591 #define CSON_VCAST(V) ((cson_value *)(((unsigned char *)(V))-sizeof(cson_value)))
1592
1593 /**
@@ -1607,11 +1611,11 @@
1607 #define CSON_OBJ(V) CSON_CAST(cson_object,(V))
1608 #define CSON_ARRAY(V) CSON_CAST(cson_array,(V))
1609
1610 /**
1611 Holds special shared "constant" (though they are non-const)
1612 values.
1613 */
1614 static struct CSON_EMPTY_HOLDER_
1615 {
1616 char trueValue;
1617 cson_string stringValue;
@@ -1620,17 +1624,17 @@
1620 cson_string_empty_m
1621 };
1622
1623 /**
1624 Indexes into the CSON_SPECIAL_VALUES array.
1625
1626 If this enum changes in any way,
1627 makes damned sure that CSON_SPECIAL_VALUES is updated
1628 to match!!!
1629 */
1630 enum CSON_INTERNAL_VALUES {
1631
1632 CSON_VAL_UNDEF = 0,
1633 CSON_VAL_NULL = 1,
1634 CSON_VAL_TRUE = 2,
1635 CSON_VAL_FALSE = 3,
1636 CSON_VAL_INT_0 = 4,
@@ -1642,11 +1646,11 @@
1642 /**
1643 Some "special" shared cson_value instances.
1644
1645 These values MUST be initialized in the order specified
1646 by the CSON_INTERNAL_VALUES enum.
1647
1648 Note that they are not const because they are used as
1649 shared-allocation objects in non-const contexts. However, the
1650 public API provides no way to modifying them, and clients who
1651 modify values directly are subject to The Wrath of Undefined
1652 Behaviour.
@@ -1665,11 +1669,11 @@
1665
1666 /**
1667 Returns non-0 (true) if m is one of our special
1668 "built-in" values, e.g. from CSON_SPECIAL_VALUES and some
1669 "empty" values.
1670
1671 If this returns true, m MUST NOT be free()d!
1672 */
1673 static char cson_value_is_builtin( void const * m )
1674 {
1675 if((m >= (void const *)&CSON_EMPTY_HOLDER)
@@ -2183,11 +2187,11 @@
2183 static int cson_value_list_visit( cson_value_list * self,
2184 int (*visitor)(cson_value * obj, void * visitorState ),
2185 void * visitorState );
2186 #endif
2187 #endif
2188
2189 #if 0
2190 # define LIST_T cson_value_list
2191 # define VALUE_T cson_value *
2192 # define VALUE_T_IS_PTR 1
2193 # define LIST_T cson_kvp_list
@@ -2362,11 +2366,11 @@
2362 return cson_value_object_alloc();
2363 }
2364
2365 cson_object * cson_new_object()
2366 {
2367
2368 return cson_value_get_object( cson_value_new_object() );
2369 }
2370
2371 cson_value * cson_value_new_array()
2372 {
@@ -2608,11 +2612,11 @@
2608 {
2609 cson_int_t i = 0;
2610 int rc = 0;
2611 switch(val->api->typeID)
2612 {
2613 case CSON_TYPE_UNDEF:
2614 case CSON_TYPE_NULL:
2615 i = 0;
2616 break;
2617 case CSON_TYPE_BOOL: {
2618 char b = 0;
@@ -2661,11 +2665,11 @@
2661 {
2662 cson_double_t d = 0.0;
2663 int rc = 0;
2664 switch(val->api->typeID)
2665 {
2666 case CSON_TYPE_UNDEF:
2667 case CSON_TYPE_NULL:
2668 d = 0;
2669 break;
2670 case CSON_TYPE_BOOL: {
2671 char b = 0;
@@ -2791,11 +2795,11 @@
2791 #if 0
2792 /**
2793 Removes and returns the last value from the given array,
2794 shrinking its size by 1. Returns NULL if ar is NULL,
2795 ar->list.count is 0, or the element at that index is NULL.
2796
2797
2798 If removeRef is true then cson_value_free() is called to remove
2799 ar's reference count for the value. In that case NULL is returned,
2800 even if the object still has live references. If removeRef is false
2801 then the caller takes over ownership of that reference count point.
@@ -2858,11 +2862,11 @@
2858 #if !defined(NDEBUG) && CSON_VOID_PTR_IS_BIG
2859 assert( sizeof(cson_int_t) <= sizeof(void *) );
2860 #endif
2861 if( c )
2862 {
2863 memcpy(CSON_INT(c), &v, sizeof(v));
2864 }
2865 return c;
2866 }
2867 }
2868
@@ -2877,11 +2881,11 @@
2877 else
2878 {
2879 cson_value * c = cson_value_new(CSON_TYPE_DOUBLE,0);
2880 if( c )
2881 {
2882 memcpy(CSON_DBL(c), &v, sizeof(v));
2883 }
2884 return c;
2885 }
2886 }
2887
@@ -3068,11 +3072,11 @@
3068 qsort( obj->kvp.list, obj->kvp.count, sizeof(cson_kvp*),
3069 cson_kvp_cmp );
3070 }
3071
3072 }
3073 #endif
3074
3075 int cson_object_unset( cson_object * obj, char const * key )
3076 {
3077 if( ! obj || !key || !*key ) return cson_rc.ArgError;
3078 else
@@ -3238,11 +3242,11 @@
3238 using p->key. In any other case cson_rc.InternalError is returned.
3239
3240 Returns cson_rc.AllocError if an allocation fails.
3241
3242 Returns 0 on success. On error, parsing must be ceased immediately.
3243
3244 Ownership of val is ALWAYS TRANSFERED to this function. If this
3245 function fails, val will be cleaned up and destroyed. (This
3246 simplifies error handling in the core parser.)
3247 */
3248 static int cson_parser_set_key( cson_parser * p, cson_value * val )
@@ -3485,11 +3489,11 @@
3485 ++p->totalKeyCount;
3486 break;
3487 }
3488 case JSON_T_STRING: {
3489 cson_value * v = cson_value_new_string( value->vu.str.value, value->vu.str.length );
3490 rc = ( NULL == v )
3491 ? cson_rc.AllocError
3492 : cson_parser_push_value( p, v );
3493 break;
3494 }
3495 default:
@@ -3532,11 +3536,11 @@
3532 To properly take over ownership of the parser's root node on a
3533 successful parse:
3534
3535 - Copy p->root's pointer and set p->root to NULL.
3536 - Eventually free up p->root with cson_value_free().
3537
3538 If you do not set p->root to NULL, p->root will be freed along with
3539 any other items inserted into it (or under it) during the parsing
3540 process.
3541 */
3542 static int cson_parser_clean( cson_parser * p )
@@ -3571,11 +3575,11 @@
3571 int rc = 0;
3572 unsigned int len = 1;
3573 cson_parse_info info = info_ ? *info_ : cson_parse_info_empty;
3574 cson_parser p = cson_parser_empty;
3575 if( ! tgt || ! src ) return cson_rc.ArgError;
3576
3577 {
3578 JSON_config jopt = {0};
3579 init_JSON_config( &jopt );
3580 jopt.allow_comments = opt.allowComments;
3581 jopt.depth = opt.maxDepth;
@@ -3780,11 +3784,11 @@
3780 unsigned char const * end = (unsigned char const *)(str ? (str + len) : NULL);
3781 unsigned char const * next = NULL;
3782 int ch;
3783 unsigned char clen = 0;
3784 char escChar[3] = {'\\',0,0};
3785 enum { UBLen = 13 };
3786 char ubuf[UBLen];
3787 int rc = 0;
3788 rc = f(state, "\"", 1 );
3789 for( ; (pos < end) && (0 == rc); pos += clen )
3790 {
@@ -4640,11 +4644,11 @@
4640 #endif
4641 #undef TRY_SHARING
4642 cson_value_add_reference(rc);
4643 return rc;
4644 }
4645
4646 static cson_value * cson_value_clone_array( cson_value const * orig )
4647 {
4648 unsigned int i = 0;
4649 cson_array const * asrc = cson_value_get_array( orig );
4650 unsigned int alen = cson_array_length_get( asrc );
@@ -4680,11 +4684,11 @@
4680 cson_value_free(cl)/*remove our artificial reference */;
4681 }
4682 }
4683 return destV;
4684 }
4685
4686 static cson_value * cson_value_clone_object( cson_value const * orig )
4687 {
4688 cson_object const * src = cson_value_get_object( orig );
4689 cson_value * destV = NULL;
4690 cson_object * dest = NULL;
@@ -4834,11 +4838,11 @@
4834 }
4835 case CSON_TYPE_STRING: {
4836 cson_string const * jstr = cson_value_get_string(orig);
4837 unsigned const int slen = cson_string_length_bytes( jstr );
4838 assert( NULL != jstr );
4839 v = cson_strdup( cson_string_cstr( jstr ), slen );
4840 break;
4841 }
4842 case CSON_TYPE_INTEGER: {
4843 char buf[BufSize] = {0};
4844 if( 0 < sprintf( v, "%"CSON_INT_T_PFMT, cson_value_get_integer(orig)) )
@@ -4887,11 +4891,11 @@
4887 }
4888 case CSON_TYPE_STRING: {
4889 cson_string const * jstr = cson_value_get_string(orig);
4890 unsigned const int slen = cson_string_length_bytes( jstr );
4891 assert( NULL != jstr );
4892 v = cson_strdup( cson_string_cstr( jstr ), slen );
4893 break;
4894 }
4895 case CSON_TYPE_INTEGER: {
4896 char buf[BufSize] = {0};
4897 if( 0 < sprintf( v, "%"CSON_INT_T_PFMT, cson_value_get_integer(orig)) )
@@ -5351,11 +5355,11 @@
5351 int rc = 0;
5352 int colCount = 0;
5353 assert(st);
5354 colCount = sqlite3_column_count(st);
5355 if( colCount <= 0 ) return NULL;
5356
5357 aryV = cson_value_new_array();
5358 if( ! aryV ) return NULL;
5359 ary = cson_value_get_array(aryV);
5360 assert(ary);
5361 for( i = 0; (0 ==rc) && (i < colCount); ++i )
@@ -5491,11 +5495,11 @@
5491 aryV = NULL;
5492 end:
5493 return aryV;
5494 }
5495
5496
5497 /**
5498 Internal impl of cson_sqlite3_stmt_to_json() when the 'fat'
5499 parameter is non-0.
5500 */
5501 static int cson_sqlite3_stmt_to_json_fat( sqlite3_stmt * st, cson_value ** tgt )
@@ -5636,11 +5640,11 @@
5636 int rc = sqlite3_prepare_v2( db, sql, -1, &st, NULL );
5637 if( 0 != rc ) return cson_rc.IOError /* FIXME: Better error code? */;
5638 rc = cson_sqlite3_stmt_to_json( st, tgt, fat );
5639 sqlite3_finalize( st );
5640 return rc;
5641 }
5642 }
5643
5644 int cson_sqlite3_bind_value( sqlite3_stmt * st, int ndx, cson_value const * v )
5645 {
5646 int rc = 0;
5647
--- src/cson_amalgamation.c
+++ src/cson_amalgamation.c
@@ -19,14 +19,14 @@
19 # define JSON_PARSER_DLL_API __declspec(dllexport)
20 # else
21 # define JSON_PARSER_DLL_API __declspec(dllimport)
22 # endif
23 # else
24 # define JSON_PARSER_DLL_API
25 # endif
26 #else
27 # define JSON_PARSER_DLL_API
28 #endif
29
30 /* Determine the integer type use to parse non-floating point numbers */
31 #ifdef _WIN32
32 typedef __int64 JSON_int_t;
@@ -34,22 +34,22 @@
34 #define JSON_PARSER_INTEGER_SPRINTF_TOKEN "%I64d"
35 #elif (__STDC_VERSION__ >= 199901L) || (HAVE_LONG_LONG == 1)
36 typedef long long JSON_int_t;
37 #define JSON_PARSER_INTEGER_SSCANF_TOKEN "%lld"
38 #define JSON_PARSER_INTEGER_SPRINTF_TOKEN "%lld"
39 #else
40 typedef long JSON_int_t;
41 #define JSON_PARSER_INTEGER_SSCANF_TOKEN "%ld"
42 #define JSON_PARSER_INTEGER_SPRINTF_TOKEN "%ld"
43 #endif
44
45
46 #ifdef __cplusplus
47 extern "C" {
48 #endif
49
50 typedef enum
51 {
52 JSON_E_NONE = 0,
53 JSON_E_INVALID_CHAR,
54 JSON_E_INVALID_KEYWORD,
55 JSON_E_INVALID_ESCAPE_SEQUENCE,
@@ -60,11 +60,11 @@
60 JSON_E_EXPECTED_KEY,
61 JSON_E_EXPECTED_COLON,
62 JSON_E_OUT_OF_MEMORY
63 } JSON_error;
64
65 typedef enum
66 {
67 JSON_T_NONE = 0,
68 JSON_T_ARRAY_BEGIN,
69 JSON_T_ARRAY_END,
70 JSON_T_OBJECT_BEGIN,
@@ -80,33 +80,33 @@
80 } JSON_type;
81
82 typedef struct JSON_value_struct {
83 union {
84 JSON_int_t integer_value;
85
86 double float_value;
87
88 struct {
89 const char* value;
90 size_t length;
91 } str;
92 } vu;
93 } JSON_value;
94
95 typedef struct JSON_parser_struct* JSON_parser;
96
97 /*! \brief JSON parser callback
98
99 \param ctx The pointer passed to new_JSON_parser.
100 \param type An element of JSON_type but not JSON_T_NONE.
101 \param value A representation of the parsed value. This parameter is NULL for
102 JSON_T_ARRAY_BEGIN, JSON_T_ARRAY_END, JSON_T_OBJECT_BEGIN, JSON_T_OBJECT_END,
103 JSON_T_NULL, JSON_T_TRUE, and JSON_T_FALSE. String values are always returned
104 as zero-terminated C strings.
105
106 \return Non-zero if parsing should continue, else zero.
107 */
108 typedef int (*JSON_parser_callback)(void* ctx, int type, const JSON_value* value);
109
110
111 /**
112 A typedef for allocator functions semantically compatible with malloc().
@@ -115,11 +115,11 @@
115 /**
116 A typedef for deallocator functions semantically compatible with free().
117 */
118 typedef void (*JSON_free_t)(void* mem);
119
120 /*! \brief The structure used to configure a JSON parser object
121 */
122 typedef struct {
123 /** Pointer to a callback, called when the parser has something to tell
124 the user. This parameter may be NULL. In this case the input is
125 merely checked for validity.
@@ -176,17 +176,17 @@
176
177 \param config. Used to configure the parser.
178 */
179 JSON_PARSER_DLL_API void init_JSON_config(JSON_config * config);
180
181 /*! \brief Create a JSON parser object
182
183 \param config. Used to configure the parser. Set to NULL to use
184 the default configuration. See init_JSON_config. Its contents are
185 copied by this function, so it need not outlive the returned
186 object.
187
188 \return The parser object, which is owned by the caller and must eventually
189 be freed by calling delete_JSON_parser().
190 */
191 JSON_PARSER_DLL_API JSON_parser new_JSON_parser(JSON_config const* config);
192
@@ -200,16 +200,16 @@
200 JSON_PARSER_DLL_API int JSON_parser_char(JSON_parser jc, int next_char);
201
202 /*! \brief Finalize parsing.
203
204 Call this method once after all input characters have been consumed.
205
206 \return Non-zero, if all parsed characters are valid JSON, zero otherwise.
207 */
208 JSON_PARSER_DLL_API int JSON_parser_done(JSON_parser jc);
209
210 /*! \brief Determine if a given string is valid JSON white space
211
212 \return Non-zero if the string is valid, zero otherwise.
213 */
214 JSON_PARSER_DLL_API int JSON_parser_is_legal_white_space_string(const char* s);
215
@@ -226,12 +226,12 @@
226 JSON_PARSER_DLL_API int JSON_parser_reset(JSON_parser jc);
227
228
229 #ifdef __cplusplus
230 }
231 #endif
232
233
234 #endif /* JSON_PARSER_H */
235 /* end file parser/JSON_parser.h */
236 /* begin file parser/JSON_parser.c */
237 /*
@@ -322,11 +322,11 @@
322 #endif
323
324
325 #define true 1
326 #define false 0
327 #define XX -1 /* the universal error code */
328
329 /* values chosen so that the object size is approx equal to one page (4K) */
330 #ifndef JSON_PARSER_STACK_SIZE
331 # define JSON_PARSER_STACK_SIZE 128
332 #endif
@@ -414,14 +414,14 @@
414 /*
415 This array maps the 128 ASCII characters into character classes.
416 The remaining Unicode characters should be mapped to C_ETC.
417 Non-whitespace control characters are errors.
418 */
419 XX, XX, XX, XX, XX, XX, XX, XX,
420 XX, C_WHITE, C_WHITE, XX, XX, C_WHITE, XX, XX,
421 XX, XX, XX, XX, XX, XX, XX, XX,
422 XX, XX, XX, XX, XX, XX, XX, XX,
423
424 C_SPACE, C_ETC, C_QUOTE, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC,
425 C_ETC, C_ETC, C_STAR, C_PLUS, C_COMMA, C_MINUS, C_POINT, C_SLASH,
426 C_ZERO, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT,
427 C_DIGIT, C_DIGIT, C_COLON, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC,
@@ -448,11 +448,11 @@
448 KE, /* key */
449 CO, /* colon */
450 VA, /* value */
451 AR, /* array */
452 ST, /* string */
453 ESC, /* escape */
454 U1, /* u1 */
455 U2, /* u2 */
456 U3, /* u3 */
457 U4, /* u4 */
458 MI, /* minus */
@@ -506,46 +506,46 @@
506 negative number. A JSON text is accepted if at the end of the text the
507 state is OK and if the mode is MODE_DONE.
508
509 white 1-9 ABCDF etc
510 space | { } [ ] : , " \ / + - . 0 | a b c d e f l n r s t u | E | * */
511 /*start GO*/ {GO,GO,-6,XX,-5,XX,XX,XX,XX,XX,CB,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
512 /*ok OK*/ {OK,OK,XX,-8,XX,-7,XX,-3,XX,XX,CB,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
513 /*object OB*/ {OB,OB,XX,-9,XX,XX,XX,XX,SB,XX,CB,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
514 /*key KE*/ {KE,KE,XX,XX,XX,XX,XX,XX,SB,XX,CB,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
515 /*colon CO*/ {CO,CO,XX,XX,XX,XX,-2,XX,XX,XX,CB,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
516 /*value VA*/ {VA,VA,-6,XX,-5,XX,XX,XX,SB,XX,CB,XX,MX,XX,ZX,IX,XX,XX,XX,XX,XX,FA,XX,NU,XX,XX,TR,XX,XX,XX,XX,XX},
517 /*array AR*/ {AR,AR,-6,XX,-5,-7,XX,XX,SB,XX,CB,XX,MX,XX,ZX,IX,XX,XX,XX,XX,XX,FA,XX,NU,XX,XX,TR,XX,XX,XX,XX,XX},
518 /*string ST*/ {ST,XX,ST,ST,ST,ST,ST,ST,-4,EX,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST,ST},
519 /*escape ES*/ {XX,XX,XX,XX,XX,XX,XX,XX,ST,ST,ST,XX,XX,XX,XX,XX,XX,ST,XX,XX,XX,ST,XX,ST,ST,XX,ST,U1,XX,XX,XX,XX},
520 /*u1 U1*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,U2,U2,U2,U2,U2,U2,U2,U2,XX,XX,XX,XX,XX,XX,U2,U2,XX,XX},
521 /*u2 U2*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,U3,U3,U3,U3,U3,U3,U3,U3,XX,XX,XX,XX,XX,XX,U3,U3,XX,XX},
522 /*u3 U3*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,U4,U4,U4,U4,U4,U4,U4,U4,XX,XX,XX,XX,XX,XX,U4,U4,XX,XX},
523 /*u4 U4*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,UC,UC,UC,UC,UC,UC,UC,UC,XX,XX,XX,XX,XX,XX,UC,UC,XX,XX},
524 /*minus MI*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,ZE,IT,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
525 /*zero ZE*/ {OK,OK,XX,-8,XX,-7,XX,-3,XX,XX,CB,XX,XX,DF,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
526 /*int IT*/ {OK,OK,XX,-8,XX,-7,XX,-3,XX,XX,CB,XX,XX,DF,IT,IT,XX,XX,XX,XX,DE,XX,XX,XX,XX,XX,XX,XX,XX,DE,XX,XX},
527 /*frac FR*/ {OK,OK,XX,-8,XX,-7,XX,-3,XX,XX,CB,XX,XX,XX,FR,FR,XX,XX,XX,XX,E1,XX,XX,XX,XX,XX,XX,XX,XX,E1,XX,XX},
528 /*e E1*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,E2,E2,XX,E3,E3,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
529 /*ex E2*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,E3,E3,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
530 /*exp E3*/ {OK,OK,XX,-8,XX,-7,XX,-3,XX,XX,XX,XX,XX,XX,E3,E3,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
531 /*tr T1*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,T2,XX,XX,XX,XX,XX,XX,XX},
532 /*tru T2*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,T3,XX,XX,XX,XX},
533 /*true T3*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,CB,XX,XX,XX,XX,XX,XX,XX,XX,XX,OK,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
534 /*fa F1*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,F2,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
535 /*fal F2*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,F3,XX,XX,XX,XX,XX,XX,XX,XX,XX},
536 /*fals F3*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,F4,XX,XX,XX,XX,XX,XX},
537 /*false F4*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,CB,XX,XX,XX,XX,XX,XX,XX,XX,XX,OK,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
538 /*nu N1*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,N2,XX,XX,XX,XX},
539 /*nul N2*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,N3,XX,XX,XX,XX,XX,XX,XX,XX,XX},
540 /*null N3*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,CB,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,OK,XX,XX,XX,XX,XX,XX,XX,XX,XX},
541 /*/ C1*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,C2},
542 /*/star C2*/ {C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C3},
543 /** C3*/ {C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,CE,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C2,C3},
544 /*_. FX*/ {OK,OK,XX,-8,XX,-7,XX,-3,XX,XX,XX,XX,XX,XX,FR,FR,XX,XX,XX,XX,E1,XX,XX,XX,XX,XX,XX,XX,XX,E1,XX,XX},
545 /*\ D1*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,D2,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX},
546 /*\ D2*/ {XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,XX,U1,XX,XX,XX,XX},
547 };
548
549
550 /*
551 These modes can be pushed on the stack.
@@ -1050,11 +1050,11 @@
1050 }
1051 if (next_char >= 128) {
1052 next_class = C_ETC;
1053 } else {
1054 next_class = ascii_class[next_char];
1055 if (next_class <= XX) {
1056 set_error(jc);
1057 return false;
1058 }
1059 }
1060
@@ -1090,11 +1090,11 @@
1090 }
1091 break;
1092 /* escaped char */
1093 case EX:
1094 jc->escaped = 1;
1095 jc->state = ESC;
1096 break;
1097 /* integer detected by minus */
1098 case MX:
1099 jc->type = JSON_T_INTEGER;
1100 jc->state = MI;
@@ -1401,10 +1401,14 @@
1401 config->malloc = malloc;
1402 config->free = free;
1403 }
1404 }
1405
1406 #undef XX
1407 #undef COUNTOF
1408 #undef parse_buffer_clear
1409 #undef parse_buffer_pop_back_char
1410 /* end file parser/JSON_parser.c */
1411 /* begin file ./cson.c */
1412 #include <assert.h>
1413 #include <stdlib.h> /* malloc()/free() */
1414 #include <string.h>
@@ -1431,11 +1435,11 @@
1435 #if defined(__cplusplus)
1436 extern "C" {
1437 #endif
1438
1439
1440
1441 /**
1442 This type holds the "vtbl" for type-specific operations when
1443 working with cson_value objects.
1444
1445 All cson_values of a given logical type share a pointer to a single
@@ -1583,11 +1587,11 @@
1587 */
1588 #define CSON_CAST(T,V) ((T*)((V)->value))
1589 /**
1590 Assumes V is a pointer to memory which is allocated as part of a
1591 cson_value instance (the bytes immediately after that part).
1592 Returns a pointer a a cson_value by subtracting sizeof(cson_value)
1593 from that address and casting it to a (cson_value*)
1594 */
1595 #define CSON_VCAST(V) ((cson_value *)(((unsigned char *)(V))-sizeof(cson_value)))
1596
1597 /**
@@ -1607,11 +1611,11 @@
1611 #define CSON_OBJ(V) CSON_CAST(cson_object,(V))
1612 #define CSON_ARRAY(V) CSON_CAST(cson_array,(V))
1613
1614 /**
1615 Holds special shared "constant" (though they are non-const)
1616 values.
1617 */
1618 static struct CSON_EMPTY_HOLDER_
1619 {
1620 char trueValue;
1621 cson_string stringValue;
@@ -1620,17 +1624,17 @@
1624 cson_string_empty_m
1625 };
1626
1627 /**
1628 Indexes into the CSON_SPECIAL_VALUES array.
1629
1630 If this enum changes in any way,
1631 makes damned sure that CSON_SPECIAL_VALUES is updated
1632 to match!!!
1633 */
1634 enum CSON_INTERNAL_VALUES {
1635
1636 CSON_VAL_UNDEF = 0,
1637 CSON_VAL_NULL = 1,
1638 CSON_VAL_TRUE = 2,
1639 CSON_VAL_FALSE = 3,
1640 CSON_VAL_INT_0 = 4,
@@ -1642,11 +1646,11 @@
1646 /**
1647 Some "special" shared cson_value instances.
1648
1649 These values MUST be initialized in the order specified
1650 by the CSON_INTERNAL_VALUES enum.
1651
1652 Note that they are not const because they are used as
1653 shared-allocation objects in non-const contexts. However, the
1654 public API provides no way to modifying them, and clients who
1655 modify values directly are subject to The Wrath of Undefined
1656 Behaviour.
@@ -1665,11 +1669,11 @@
1669
1670 /**
1671 Returns non-0 (true) if m is one of our special
1672 "built-in" values, e.g. from CSON_SPECIAL_VALUES and some
1673 "empty" values.
1674
1675 If this returns true, m MUST NOT be free()d!
1676 */
1677 static char cson_value_is_builtin( void const * m )
1678 {
1679 if((m >= (void const *)&CSON_EMPTY_HOLDER)
@@ -2183,11 +2187,11 @@
2187 static int cson_value_list_visit( cson_value_list * self,
2188 int (*visitor)(cson_value * obj, void * visitorState ),
2189 void * visitorState );
2190 #endif
2191 #endif
2192
2193 #if 0
2194 # define LIST_T cson_value_list
2195 # define VALUE_T cson_value *
2196 # define VALUE_T_IS_PTR 1
2197 # define LIST_T cson_kvp_list
@@ -2362,11 +2366,11 @@
2366 return cson_value_object_alloc();
2367 }
2368
2369 cson_object * cson_new_object()
2370 {
2371
2372 return cson_value_get_object( cson_value_new_object() );
2373 }
2374
2375 cson_value * cson_value_new_array()
2376 {
@@ -2608,11 +2612,11 @@
2612 {
2613 cson_int_t i = 0;
2614 int rc = 0;
2615 switch(val->api->typeID)
2616 {
2617 case CSON_TYPE_UNDEF:
2618 case CSON_TYPE_NULL:
2619 i = 0;
2620 break;
2621 case CSON_TYPE_BOOL: {
2622 char b = 0;
@@ -2661,11 +2665,11 @@
2665 {
2666 cson_double_t d = 0.0;
2667 int rc = 0;
2668 switch(val->api->typeID)
2669 {
2670 case CSON_TYPE_UNDEF:
2671 case CSON_TYPE_NULL:
2672 d = 0;
2673 break;
2674 case CSON_TYPE_BOOL: {
2675 char b = 0;
@@ -2791,11 +2795,11 @@
2795 #if 0
2796 /**
2797 Removes and returns the last value from the given array,
2798 shrinking its size by 1. Returns NULL if ar is NULL,
2799 ar->list.count is 0, or the element at that index is NULL.
2800
2801
2802 If removeRef is true then cson_value_free() is called to remove
2803 ar's reference count for the value. In that case NULL is returned,
2804 even if the object still has live references. If removeRef is false
2805 then the caller takes over ownership of that reference count point.
@@ -2858,11 +2862,11 @@
2862 #if !defined(NDEBUG) && CSON_VOID_PTR_IS_BIG
2863 assert( sizeof(cson_int_t) <= sizeof(void *) );
2864 #endif
2865 if( c )
2866 {
2867 memcpy( CSON_INT(c), &v, sizeof(v) );
2868 }
2869 return c;
2870 }
2871 }
2872
@@ -2877,11 +2881,11 @@
2881 else
2882 {
2883 cson_value * c = cson_value_new(CSON_TYPE_DOUBLE,0);
2884 if( c )
2885 {
2886 memcpy( CSON_DBL(c), &v, sizeof(v) );
2887 }
2888 return c;
2889 }
2890 }
2891
@@ -3068,11 +3072,11 @@
3072 qsort( obj->kvp.list, obj->kvp.count, sizeof(cson_kvp*),
3073 cson_kvp_cmp );
3074 }
3075
3076 }
3077 #endif
3078
3079 int cson_object_unset( cson_object * obj, char const * key )
3080 {
3081 if( ! obj || !key || !*key ) return cson_rc.ArgError;
3082 else
@@ -3238,11 +3242,11 @@
3242 using p->key. In any other case cson_rc.InternalError is returned.
3243
3244 Returns cson_rc.AllocError if an allocation fails.
3245
3246 Returns 0 on success. On error, parsing must be ceased immediately.
3247
3248 Ownership of val is ALWAYS TRANSFERED to this function. If this
3249 function fails, val will be cleaned up and destroyed. (This
3250 simplifies error handling in the core parser.)
3251 */
3252 static int cson_parser_set_key( cson_parser * p, cson_value * val )
@@ -3485,11 +3489,11 @@
3489 ++p->totalKeyCount;
3490 break;
3491 }
3492 case JSON_T_STRING: {
3493 cson_value * v = cson_value_new_string( value->vu.str.value, value->vu.str.length );
3494 rc = ( NULL == v )
3495 ? cson_rc.AllocError
3496 : cson_parser_push_value( p, v );
3497 break;
3498 }
3499 default:
@@ -3532,11 +3536,11 @@
3536 To properly take over ownership of the parser's root node on a
3537 successful parse:
3538
3539 - Copy p->root's pointer and set p->root to NULL.
3540 - Eventually free up p->root with cson_value_free().
3541
3542 If you do not set p->root to NULL, p->root will be freed along with
3543 any other items inserted into it (or under it) during the parsing
3544 process.
3545 */
3546 static int cson_parser_clean( cson_parser * p )
@@ -3571,11 +3575,11 @@
3575 int rc = 0;
3576 unsigned int len = 1;
3577 cson_parse_info info = info_ ? *info_ : cson_parse_info_empty;
3578 cson_parser p = cson_parser_empty;
3579 if( ! tgt || ! src ) return cson_rc.ArgError;
3580
3581 {
3582 JSON_config jopt = {0};
3583 init_JSON_config( &jopt );
3584 jopt.allow_comments = opt.allowComments;
3585 jopt.depth = opt.maxDepth;
@@ -3780,11 +3784,11 @@
3784 unsigned char const * end = (unsigned char const *)(str ? (str + len) : NULL);
3785 unsigned char const * next = NULL;
3786 int ch;
3787 unsigned char clen = 0;
3788 char escChar[3] = {'\\',0,0};
3789 enum { UBLen = 20 };
3790 char ubuf[UBLen];
3791 int rc = 0;
3792 rc = f(state, "\"", 1 );
3793 for( ; (pos < end) && (0 == rc); pos += clen )
3794 {
@@ -4640,11 +4644,11 @@
4644 #endif
4645 #undef TRY_SHARING
4646 cson_value_add_reference(rc);
4647 return rc;
4648 }
4649
4650 static cson_value * cson_value_clone_array( cson_value const * orig )
4651 {
4652 unsigned int i = 0;
4653 cson_array const * asrc = cson_value_get_array( orig );
4654 unsigned int alen = cson_array_length_get( asrc );
@@ -4680,11 +4684,11 @@
4684 cson_value_free(cl)/*remove our artificial reference */;
4685 }
4686 }
4687 return destV;
4688 }
4689
4690 static cson_value * cson_value_clone_object( cson_value const * orig )
4691 {
4692 cson_object const * src = cson_value_get_object( orig );
4693 cson_value * destV = NULL;
4694 cson_object * dest = NULL;
@@ -4834,11 +4838,11 @@
4838 }
4839 case CSON_TYPE_STRING: {
4840 cson_string const * jstr = cson_value_get_string(orig);
4841 unsigned const int slen = cson_string_length_bytes( jstr );
4842 assert( NULL != jstr );
4843 v = cson_strdup( cson_string_cstr( jstr ), slen );
4844 break;
4845 }
4846 case CSON_TYPE_INTEGER: {
4847 char buf[BufSize] = {0};
4848 if( 0 < sprintf( v, "%"CSON_INT_T_PFMT, cson_value_get_integer(orig)) )
@@ -4887,11 +4891,11 @@
4891 }
4892 case CSON_TYPE_STRING: {
4893 cson_string const * jstr = cson_value_get_string(orig);
4894 unsigned const int slen = cson_string_length_bytes( jstr );
4895 assert( NULL != jstr );
4896 v = cson_strdup( cson_string_cstr( jstr ), slen );
4897 break;
4898 }
4899 case CSON_TYPE_INTEGER: {
4900 char buf[BufSize] = {0};
4901 if( 0 < sprintf( v, "%"CSON_INT_T_PFMT, cson_value_get_integer(orig)) )
@@ -5351,11 +5355,11 @@
5355 int rc = 0;
5356 int colCount = 0;
5357 assert(st);
5358 colCount = sqlite3_column_count(st);
5359 if( colCount <= 0 ) return NULL;
5360
5361 aryV = cson_value_new_array();
5362 if( ! aryV ) return NULL;
5363 ary = cson_value_get_array(aryV);
5364 assert(ary);
5365 for( i = 0; (0 ==rc) && (i < colCount); ++i )
@@ -5491,11 +5495,11 @@
5495 aryV = NULL;
5496 end:
5497 return aryV;
5498 }
5499
5500
5501 /**
5502 Internal impl of cson_sqlite3_stmt_to_json() when the 'fat'
5503 parameter is non-0.
5504 */
5505 static int cson_sqlite3_stmt_to_json_fat( sqlite3_stmt * st, cson_value ** tgt )
@@ -5636,11 +5640,11 @@
5640 int rc = sqlite3_prepare_v2( db, sql, -1, &st, NULL );
5641 if( 0 != rc ) return cson_rc.IOError /* FIXME: Better error code? */;
5642 rc = cson_sqlite3_stmt_to_json( st, tgt, fat );
5643 sqlite3_finalize( st );
5644 return rc;
5645 }
5646 }
5647
5648 int cson_sqlite3_bind_value( sqlite3_stmt * st, int ndx, cson_value const * v )
5649 {
5650 int rc = 0;
5651
--- src/cson_amalgamation.h
+++ src/cson_amalgamation.h
@@ -59,11 +59,11 @@
5959
#define CSON_INT_T_PFMT "I64d"
6060
#elif (__STDC_VERSION__ >= 199901L) || (HAVE_LONG_LONG == 1)
6161
typedef long long cson_int_t;
6262
#define CSON_INT_T_SFMT "lld"
6363
#define CSON_INT_T_PFMT "lld"
64
-#else
64
+#else
6565
typedef long cson_int_t;
6666
#define CSON_INT_T_SFMT "ld"
6767
#define CSON_INT_T_PFMT "ld"
6868
#endif
6969
@@ -215,11 +215,11 @@
215215
Convenience typedef.
216216
*/
217217
typedef struct cson_value cson_value;
218218
219219
/** @struct cson_value
220
-
220
+
221221
The core value type of this API. It is opaque to clients, and
222222
only the cson public API should be used for setting or
223223
inspecting their values.
224224
225225
This class is opaque because stack-based usage can easily cause
@@ -231,11 +231,11 @@
231231
counting) as long as those insertions do not cause cycles. However,
232232
be very aware that such value re-use uses a reference to the
233233
original copy, meaning that if its value is changed once, it is
234234
changed everywhere. Also beware that multi-threaded write
235235
operations on such references leads to undefined behaviour.
236
-
236
+
237237
PLEASE read the ACHTUNGEN below...
238238
239239
ACHTUNG #1:
240240
241241
cson_values MUST NOT form cycles (e.g. via object or array
@@ -244,33 +244,33 @@
244244
Not abiding th Holy Law Of No Cycles will lead to double-frees and
245245
the like (i.e. undefined behaviour, likely crashes due to infinite
246246
recursion or stepping on invalid (freed) pointers).
247247
248248
ACHTUNG #2:
249
-
249
+
250250
ALL cson_values returned as non-const cson_value pointers from any
251251
public functions in the cson API are to be treated as if they are
252252
heap-allocated, and MUST be freed by client by doing ONE of:
253
-
253
+
254254
- Passing it to cson_value_free().
255
-
255
+
256256
- Adding it to an Object or Array, in which case the object/array
257257
takes over ownership. As of 20110323, a value may be inserted into
258258
a single container multiple times, or into multiple containers,
259259
in which case they all share ownership (via reference counting)
260260
of the original value (meaning any changes to it are visible in
261261
all references to it).
262
-
262
+
263263
Each call to cson_value_new_xxx() MUST eventually be followed up
264264
by one of those options.
265
-
265
+
266266
Some cson_value_new_XXX() implementations do not actually allocate
267267
memory, but this is an internal implementation detail. Client code
268268
MUST NOT rely on this behaviour and MUST treat each object
269269
returned by such a function as if it was a freshly-allocated copy
270270
(even if their pointer addresses are the same).
271
-
271
+
272272
ACHTUNG #3:
273273
274274
Note that ACHTUNG #2 tells us that we must always free (or transfer
275275
ownership of) all pointers returned bycson_value_new_xxx(), but
276276
that two calls to (e.g.) cson_value_new_bool(1) will (or might)
@@ -315,11 +315,11 @@
315315
if( 0 == rc ) {...success...}
316316
else if( cson_rc.ArgError == rc ) { ... some argument was wrong ... }
317317
else if( cson_rc.AllocError == rc ) { ... allocation error ... }
318318
...
319319
@endcode
320
-
320
+
321321
The entries named Parse_XXX are generally only returned by
322322
cson_parse() and friends.
323323
*/
324324
325325
/** @struct cson_rc_
@@ -472,11 +472,11 @@
472472
473473
/**
474474
Length, in bytes.
475475
*/
476476
unsigned int length;
477
-
477
+
478478
/**
479479
Error code of the parse run (0 for no error).
480480
*/
481481
int errorCode;
482482
@@ -523,11 +523,11 @@
523523
/**
524524
Specifies how to indent (or not) output. The values
525525
are:
526526
527527
(0) == no extra indentation.
528
-
528
+
529529
(1) == 1 TAB character for each level.
530530
531531
(>1) == that number of SPACES for each level.
532532
*/
533533
unsigned char indentation;
@@ -536,11 +536,11 @@
536536
Maximum object/array depth to traverse. Traversing deeply can
537537
be indicative of cycles in the object/array tree, and this
538538
value is used to figure out when to abort the traversal.
539539
*/
540540
unsigned short maxDepth;
541
-
541
+
542542
/**
543543
If true, a newline will be added to generated output,
544544
else not.
545545
*/
546546
char addNewline;
@@ -636,11 +636,11 @@
636636
637637
Must return 0 on success, non-0 on error (preferably a value from
638638
cson_rc).
639639
640640
These functions are called relatively often during the JSON-output
641
- process, and should try to be fast.
641
+ process, and should try to be fast.
642642
*/
643643
typedef int (*cson_data_dest_f)( void * state, void const * src, unsigned int n );
644644
645645
/**
646646
Reads JSON-formatted string data (in ASCII, UTF8, or UTF16), using the
@@ -664,15 +664,15 @@
664664
used.
665665
666666
The info argument may be NULL. If it is not NULL then the parser
667667
populates it with information which is useful in error
668668
reporting. Namely, it contains the line/column of parse errors.
669
-
669
+
670670
The srcState argument is ignored by this function but is passed on to src,
671671
so any output-destination-specific state can be stored there and accessed
672672
via the src callback.
673
-
673
+
674674
Non-parse error conditions include:
675675
676676
- (!tgt) or !src: cson_rc.ArgError
677677
- cson_rc.AllocError can happen at any time during the input phase
678678
@@ -725,11 +725,11 @@
725725
TODOs:
726726
727727
- Buffer the input in larger chunks. We currently read
728728
byte-by-byte, but i'm too tired to write/test the looping code for
729729
the buffering.
730
-
730
+
731731
@see cson_parse_FILE()
732732
@see cson_parse_string()
733733
*/
734734
int cson_parse( cson_value ** tgt, cson_data_source_f src, void * srcState,
735735
cson_parse_opt const * opt, cson_parse_info * info );
@@ -786,11 +786,11 @@
786786
The destState parameter is ignored by this function and is passed
787787
on to the dest function.
788788
789789
Returns 0 on success. On error, any amount of output might have been
790790
generated before the error was triggered.
791
-
791
+
792792
Example:
793793
794794
@code
795795
int rc = cson_output( myValue, cson_data_dest_FILE, stdout, NULL );
796796
// basically equivalent to: cson_output_FILE( myValue, stdout, NULL );
@@ -925,15 +925,15 @@
925925
/**
926926
Converts the given value to a boolean, using JavaScript semantics depending
927927
on the concrete type of val:
928928
929929
undef or null: false
930
-
930
+
931931
boolean: same
932
-
932
+
933933
integer, double: 0 or 0.0 == false, else true
934
-
934
+
935935
object, array: true
936936
937937
string: length-0 string is false, else true.
938938
939939
Returns 0 on success and assigns *v (if v is not NULL) to either 0 or 1.
@@ -945,18 +945,18 @@
945945
Similar to cson_value_fetch_bool(), but fetches an integer value.
946946
947947
The conversion, if any, depends on the concrete type of val:
948948
949949
NULL, null, undefined: *v is set to 0 and 0 is returned.
950
-
950
+
951951
string, object, array: *v is set to 0 and
952952
cson_rc.TypeError is returned. The error may normally be safely
953953
ignored, but it is provided for those wanted to know whether a direct
954954
conversion was possible.
955955
956956
integer: *v is set to the int value and 0 is returned.
957
-
957
+
958958
double: *v is set to the value truncated to int and 0 is returned.
959959
*/
960960
int cson_value_fetch_integer( cson_value const * val, cson_int_t * v );
961961
962962
/**
@@ -1084,11 +1084,11 @@
10841084
10851085
- The maximum number of bytes compared is the lesser of rhsLen and
10861086
the length of lhs. If the strings do not match, but compare equal
10871087
up to the just-described comparison length, the shorter string is
10881088
considered to be less-than the longer one.
1089
-
1089
+
10901090
- If lhs and rhs are both NULL, or both have a length of 0 then they will
10911091
compare equal.
10921092
10931093
- If lhs is null/length-0 but rhs is not then lhs is considered to be less-than
10941094
rhs.
@@ -1110,11 +1110,11 @@
11101110
Returns the length, in bytes, of str, or 0 if str is NULL. This is
11111111
an O(1) operation.
11121112
11131113
TODO: add cson_string_length_chars() (is O(N) unless we add another
11141114
member to store the char length).
1115
-
1115
+
11161116
@see cson_string_cstr()
11171117
*/
11181118
unsigned int cson_string_length_bytes( cson_string const * str );
11191119
11201120
/**
@@ -1207,11 +1207,11 @@
12071207
ar is expanded, if needed, to be able to hold at least (ndx+1)
12081208
items, and any new entries created by that expansion are empty
12091209
(NULL values).
12101210
12111211
On success, 0 is returned and ownership of v is transfered to ar.
1212
-
1212
+
12131213
On error ownership of v is NOT modified, and the caller may still
12141214
need to clean it up. For example, the following code will introduce
12151215
a leak if this function fails:
12161216
12171217
@code
@@ -1240,11 +1240,11 @@
12401240
12411241
This is functionally equivalent to
12421242
cson_array_set(ar,cson_array_length_get(ar),v), but this
12431243
implementation has slightly different array-preallocation policy
12441244
(it grows more eagerly).
1245
-
1245
+
12461246
Returns 0 on success, non-zero on error. Error cases include:
12471247
12481248
- ar or v are NULL: cson_rc.ArgError
12491249
12501250
- Array cannot be expanded to hold enough elements: cson_rc.AllocError.
@@ -1283,25 +1283,25 @@
12831283
cson_value * cson_new_bool(char v);
12841284
12851285
/**
12861286
Returns the special JSON "null" value. When outputing JSON,
12871287
its string representation is "null" (without the quotes).
1288
-
1288
+
12891289
See cson_value_new_bool() for notes regarding the returned
12901290
value's memory.
12911291
*/
1292
-cson_value * cson_value_null();
1292
+cson_value * cson_value_null( void );
12931293
12941294
/**
12951295
Equivalent to cson_value_new_bool(1).
12961296
*/
1297
-cson_value * cson_value_true();
1297
+cson_value * cson_value_true( void );
12981298
12991299
/**
13001300
Equivalent to cson_value_new_bool(0).
13011301
*/
1302
-cson_value * cson_value_false();
1302
+cson_value * cson_value_false( void );
13031303
13041304
/**
13051305
Semantically the same as cson_value_new_bool(), but for integers.
13061306
*/
13071307
cson_value * cson_value_new_integer( cson_int_t v );
@@ -1323,16 +1323,16 @@
13231323
13241324
/**
13251325
Semantically the same as cson_value_new_bool(), but for strings.
13261326
This creates a JSON value which copies the first n bytes of str.
13271327
The string will automatically be NUL-terminated.
1328
-
1328
+
13291329
Note that if str is NULL or n is 0, this function still
13301330
returns non-NULL value representing that empty string.
1331
-
1331
+
13321332
Returns NULL on allocation error.
1333
-
1333
+
13341334
See cson_value_new_bool() for important information about the
13351335
returned memory.
13361336
*/
13371337
cson_value * cson_value_new_string( char const * str, unsigned int n );
13381338
@@ -1346,32 +1346,32 @@
13461346
Post-conditions: cson_value_is_object(value) will return true.
13471347
13481348
@see cson_value_new_array()
13491349
@see cson_value_free()
13501350
*/
1351
-cson_value * cson_value_new_object();
1351
+cson_value * cson_value_new_object( void );
13521352
13531353
/**
13541354
This works like cson_value_new_object() but returns an Object
13551355
handle directly.
13561356
13571357
The value handle for the returned object can be fetched with
13581358
cson_object_value(theObject).
1359
-
1359
+
13601360
Ownership is transfered to the caller, who must eventually free it
13611361
by passing the Value handle (NOT the Object handle) to
13621362
cson_value_free() or passing ownership to a parent container.
13631363
13641364
Returns NULL on error (out of memory).
13651365
*/
1366
-cson_object * cson_new_object();
1366
+cson_object * cson_new_object( void );
13671367
13681368
/**
13691369
Identical to cson_new_object() except that it creates
13701370
an Array.
13711371
*/
1372
-cson_array * cson_new_array();
1372
+cson_array * cson_new_array( void );
13731373
13741374
/**
13751375
Identical to cson_new_object() except that it creates
13761376
a String.
13771377
*/
@@ -1403,11 +1403,11 @@
14031403
Post-conditions: cson_value_is_array(value) will return true.
14041404
14051405
@see cson_value_new_object()
14061406
@see cson_value_free()
14071407
*/
1408
-cson_value * cson_value_new_array();
1408
+cson_value * cson_value_new_array( void );
14091409
14101410
/**
14111411
Frees any resources owned by v, then frees v. If v is a container
14121412
type (object or array) its children are also freed (recursively).
14131413
@@ -1419,11 +1419,11 @@
14191419
cson_value_add_reference(). Even if this function does not
14201420
immediately destroy the value, the value must be considered, from
14211421
the perspective of that client code, to have been
14221422
destroyed/invalidated by this call.
14231423
1424
-
1424
+
14251425
@see cson_value_new_object()
14261426
@see cson_value_new_array()
14271427
@see cson_value_add_reference()
14281428
*/
14291429
void cson_value_free(cson_value * v);
@@ -1445,11 +1445,11 @@
14451445
case, the key is set to the special null value.
14461446
14471447
The key may be encoded as ASCII or UTF8. Results are undefined
14481448
with other encodings, and the errors won't show up here, but may
14491449
show up later, e.g. during output.
1450
-
1450
+
14511451
Returns 0 on success, non-0 on error. It has the following error
14521452
cases:
14531453
14541454
- cson_rc.ArgError: obj or key are NULL or strlen(key) is 0.
14551455
@@ -1498,11 +1498,11 @@
14981498
*/
14991499
int cson_object_set_s( cson_object * obj, cson_string * key, cson_value * v );
15001500
15011501
/**
15021502
Removes a property from an object.
1503
-
1503
+
15041504
If obj contains the given key, it is removed and 0 is returned. If
15051505
it is not found, cson_rc.NotFoundError is returned (which can
15061506
normally be ignored by client code).
15071507
15081508
cson_rc.ArgError is returned if obj or key are NULL or key has
@@ -1569,33 +1569,33 @@
15691569
15701570
If it finds the given path, it returns the value by assiging *tgt
15711571
to it. If tgt is NULL then this function has no side-effects but
15721572
will return 0 if the given path is found within the object, so it can be used
15731573
to test for existence without fetching it.
1574
-
1574
+
15751575
Returns 0 if it finds an entry, cson_rc.NotFoundError if it finds
15761576
no item, and any other non-zero error code on a "real" error. Errors include:
15771577
15781578
- obj or path are NULL: cson_rc.ArgError
1579
-
1579
+
15801580
- separator is 0, or path is an empty string or contains only
15811581
separator characters: cson_rc.RangeError
15821582
15831583
- There is an upper limit on how long a single path component may
15841584
be (some "reasonable" internal size), and cson_rc.RangeError is
15851585
returned if that length is violated.
15861586
1587
-
1587
+
15881588
Limitations:
15891589
15901590
- It has no way to fetch data from arrays this way. i could
15911591
imagine, e.g., a path of "subobj.subArray.0" for
15921592
subobj.subArray[0], or "0.3.1" for [0][3][1]. But i'm too
15931593
lazy/tired to add this.
15941594
15951595
Example usage:
1596
-
1596
+
15971597
15981598
Assume we have a JSON structure which abstractly looks like:
15991599
16001600
@code
16011601
{"subobj":{"subsubobj":{"myValue":[1,2,3]}}}
@@ -1613,11 +1613,11 @@
16131613
"subobj/subsubobj/myValue" with separator='/' is equivalent the
16141614
path "subobj.subsubobj.myValue" with separator='.'. The value of 0
16151615
is not legal as a separator character because we cannot
16161616
distinguish that use from the real end-of-string without requiring
16171617
the caller to also pass in the length of the string.
1618
-
1618
+
16191619
Multiple successive separators in the list are collapsed into a
16201620
single separator for parsing purposes. e.g. the path "a...b...c"
16211621
(separator='.') is equivalent to "a.b.c".
16221622
16231623
@see cson_object_get_sub()
@@ -1700,11 +1700,11 @@
17001700
@see cson_object_iter_init()
17011701
@see cson_object_iter_next()
17021702
*/
17031703
struct cson_object_iterator
17041704
{
1705
-
1705
+
17061706
/** @internal
17071707
The underlying object.
17081708
*/
17091709
cson_object const * obj;
17101710
/** @internal
@@ -1771,11 +1771,11 @@
17711771
...
17721772
}
17731773
@endcode
17741774
17751775
There is no need to clean up an iterator, as it holds no dynamic resources.
1776
-
1776
+
17771777
@see cson_kvp_key()
17781778
@see cson_kvp_value()
17791779
*/
17801780
cson_kvp * cson_object_iter_next( cson_object_iterator * iter );
17811781
@@ -1863,11 +1863,11 @@
18631863
buf = cson_buffer_empty;
18641864
@endcode
18651865
18661866
(You might also need to store buf.used and buf.capacity,
18671867
depending on what you want to do with the memory.)
1868
-
1868
+
18691869
When doing so, the memory must eventually be passed to free()
18701870
to deallocate it.
18711871
*/
18721872
unsigned char * mem;
18731873
};
@@ -1894,11 +1894,11 @@
18941894
On error non-zero is returned. Errors include:
18951895
18961896
- Invalid arguments: cson_rc.ArgError
18971897
18981898
- Buffer cannot be expanded (runs out of memory): cson_rc.AllocError
1899
-
1899
+
19001900
Example usage:
19011901
19021902
@code
19031903
cson_buffer buf = cson_buffer_empty;
19041904
// optional: cson_buffer_reserve(&buf, 1024 * 10);
@@ -1918,13 +1918,13 @@
19181918
buf = cson_buffer_empty;
19191919
...
19201920
free(mem);
19211921
}
19221922
@endcode
1923
-
1923
+
19241924
@see cson_output()
1925
-
1925
+
19261926
*/
19271927
int cson_output_buffer( cson_value const * v, cson_buffer * buf,
19281928
cson_output_opt const * opt );
19291929
19301930
/**
@@ -1993,11 +1993,11 @@
19931993
calling cson_buffer_reserve(dest,0).
19941994
19951995
dest->mem might (and possibly will) be (re)allocated by this
19961996
function, so any pointers to it held from before this call might be
19971997
invalidated by this call.
1998
-
1998
+
19991999
On error non-0 is returned and dest has almost certainly been
20002000
modified but its state must be considered incomplete.
20012001
20022002
Errors include:
20032003
@@ -2042,11 +2042,11 @@
20422042
void * mem = buf.mem;
20432043
buf = cson_buffer_empty;
20442044
@endcode
20452045
20462046
In which case the memory must eventually be passed to free() to
2047
- free it.
2047
+ free it.
20482048
*/
20492049
int cson_buffer_fill_from( cson_buffer * dest, cson_data_source_f src, void * state );
20502050
20512051
20522052
/**
@@ -2072,11 +2072,11 @@
20722072
for other sharing points which added a reference.
20732073
20742074
Normally any such value handles would be invalidated when the
20752075
parent container(s) is/are cleaned up, but this function can be
20762076
used to effectively delay the cleanup.
2077
-
2077
+
20782078
This function, at its lowest level, increments the value's
20792079
reference count by 1.
20802080
20812081
To decrement the reference count, pass the value to
20822082
cson_value_free(), after which the value must be considered, from
@@ -2091,11 +2091,11 @@
20912091
errors long before the reference count could overflow (assuming
20922092
those reference counts come from container insertions, as opposed
20932093
to via this function).
20942094
20952095
Insider notes which clients really need to know:
2096
-
2096
+
20972097
For shared/constant value instances, such as those returned by
20982098
cson_value_true() and cson_value_null(), this function has no side
20992099
effects - it does not actually modify the reference count because
21002100
(A) those instances are shared across all client code and (B) those
21012101
objects are static and never get cleaned up. However, that is an
@@ -2173,11 +2173,11 @@
21732173
returned object will be of the same logical type as orig.
21742174
21752175
ACHTUNG: if orig contains any cyclic references at any depth level
21762176
this function will endlessly recurse. (Having _any_ cyclic
21772177
references violates this library's requirements.)
2178
-
2178
+
21792179
Returns NULL if orig is NULL or if cloning fails. Assuming that
21802180
orig is in a valid state, the only "likely" error case is that an
21812181
allocation fails while constructing the clone. In other words, if
21822182
cloning fails due to something other than an allocation error then
21832183
either orig is in an invalid state or there is a bug.
@@ -2278,11 +2278,11 @@
22782278
--key=VAL : Treats VAL as either a double, integer, or string.
22792279
22802280
--key= : Treats key as a JSON null (not literal NULL) value.
22812281
22822282
Arguments not starting with a dash are skipped.
2283
-
2283
+
22842284
Each key/value pair is inserted into an object. If a given key
22852285
appears more than once then only the final entry is actually
22862286
stored.
22872287
22882288
argc and argv are expected to be values from main() (or similar,
@@ -2434,22 +2434,22 @@
24342434
Integer, double, null, or string (TEXT and BLOB data, though not
24352435
all blob data is legal for a JSON string).
24362436
24372437
st must be a sqlite3_step()'d row and col must be a 0-based column
24382438
index within that result row.
2439
- */
2439
+ */
24402440
cson_value * cson_sqlite3_column_to_value( sqlite3_stmt * st, int col );
24412441
24422442
/**
24432443
Creates a JSON Array object containing the names of all columns
2444
- of the given prepared statement handle.
2445
-
2444
+ of the given prepared statement handle.
2445
+
24462446
Returns a new array value on success, which the caller owns. Its elements
24472447
are in the same order as in the underlying query.
24482448
24492449
On error NULL is returned.
2450
-
2450
+
24512451
st is not traversed or freed by this function - only the column
24522452
count and names are read.
24532453
*/
24542454
cson_value * cson_sqlite3_column_names( sqlite3_stmt * st );
24552455
@@ -2494,22 +2494,22 @@
24942494
*/
24952495
cson_value * cson_sqlite3_row_to_array( sqlite3_stmt * st );
24962496
/**
24972497
Converts the results of an sqlite3 SELECT statement to JSON,
24982498
in the form of a cson_value object tree.
2499
-
2499
+
25002500
st must be a prepared, but not yet traversed, SELECT query.
25012501
tgt must be a pointer to NULL (see the example below). If
25022502
either of those arguments are NULL, cson_rc.ArgError is returned.
2503
-
2503
+
25042504
This walks the query results and returns a JSON object which
25052505
has a different structure depending on the value of the 'fat'
25062506
argument.
2507
-
2508
-
2507
+
2508
+
25092509
If 'fat' is 0 then the structure is:
2510
-
2510
+
25112511
@code
25122512
{
25132513
"columns":["colName1",..."colNameN"],
25142514
"rows":[
25152515
[colVal0, ... colValN],
@@ -2516,16 +2516,16 @@
25162516
[colVal0, ... colValN],
25172517
...
25182518
]
25192519
}
25202520
@endcode
2521
-
2521
+
25222522
In the "non-fat" format the order of the columns and row values is
25232523
guaranteed to be the same as that of the underlying query.
2524
-
2524
+
25252525
If 'fat' is not 0 then the structure is:
2526
-
2526
+
25272527
@code
25282528
{
25292529
"columns":["colName1",..."colNameN"],
25302530
"rows":[
25312531
{"colName1":value1,..."colNameN":valueN},
@@ -2543,33 +2543,33 @@
25432543
25442544
On success it returns 0 and assigns *tgt to a newly-allocated
25452545
JSON object tree (using the above structure), which the caller owns.
25462546
If the query returns no rows, the "rows" value will be an empty
25472547
array, as opposed to null.
2548
-
2548
+
25492549
On error non-0 is returned and *tgt is not modified.
2550
-
2550
+
25512551
The error code cson_rc.IOError is used to indicate a db-level
25522552
error, and cson_rc.TypeError is returned if sqlite3_column_count(st)
25532553
returns 0 or less (indicating an invalid or non-SELECT statement).
2554
-
2554
+
25552555
The JSON data types are determined by the column type as reported
25562556
by sqlite3_column_type():
2557
-
2557
+
25582558
SQLITE_INTEGER: integer
2559
-
2559
+
25602560
SQLITE_FLOAT: double
2561
-
2561
+
25622562
SQLITE_TEXT or SQLITE_BLOB: string, and this will only work if
25632563
the data is UTF8 compatible.
2564
-
2564
+
25652565
If the db returns a literal or SQL NULL for a value it is converted
25662566
to a JSON null. If it somehow finds a column type it cannot handle,
25672567
the value is also converted to a NULL in the output.
25682568
25692569
Example
2570
-
2570
+
25712571
@code
25722572
cson_value * json = NULL;
25732573
int rc = cson_sqlite3_stmt_to_json( myStatement, &json, 1 );
25742574
if( 0 != rc ) { ... error ... }
25752575
else {
@@ -2603,14 +2603,14 @@
26032603
TODO: add Object support for named parameters.
26042604
26052605
Returns 0 on success, non-0 on error.
26062606
*/
26072607
int cson_sqlite3_bind_value( sqlite3_stmt * st, int ndx, cson_value const * v );
2608
-
2608
+
26092609
#if defined(__cplusplus)
26102610
} /*extern "C"*/
26112611
#endif
2612
-
2612
+
26132613
#endif /* CSON_ENABLE_SQLITE3 */
26142614
#endif /* WANDERINGHORSE_NET_CSON_SQLITE3_H_INCLUDED */
26152615
/* end file include/wh/cson/cson_sqlite3.h */
26162616
#endif /* FOSSIL_ENABLE_JSON */
26172617
--- src/cson_amalgamation.h
+++ src/cson_amalgamation.h
@@ -59,11 +59,11 @@
59 #define CSON_INT_T_PFMT "I64d"
60 #elif (__STDC_VERSION__ >= 199901L) || (HAVE_LONG_LONG == 1)
61 typedef long long cson_int_t;
62 #define CSON_INT_T_SFMT "lld"
63 #define CSON_INT_T_PFMT "lld"
64 #else
65 typedef long cson_int_t;
66 #define CSON_INT_T_SFMT "ld"
67 #define CSON_INT_T_PFMT "ld"
68 #endif
69
@@ -215,11 +215,11 @@
215 Convenience typedef.
216 */
217 typedef struct cson_value cson_value;
218
219 /** @struct cson_value
220
221 The core value type of this API. It is opaque to clients, and
222 only the cson public API should be used for setting or
223 inspecting their values.
224
225 This class is opaque because stack-based usage can easily cause
@@ -231,11 +231,11 @@
231 counting) as long as those insertions do not cause cycles. However,
232 be very aware that such value re-use uses a reference to the
233 original copy, meaning that if its value is changed once, it is
234 changed everywhere. Also beware that multi-threaded write
235 operations on such references leads to undefined behaviour.
236
237 PLEASE read the ACHTUNGEN below...
238
239 ACHTUNG #1:
240
241 cson_values MUST NOT form cycles (e.g. via object or array
@@ -244,33 +244,33 @@
244 Not abiding th Holy Law Of No Cycles will lead to double-frees and
245 the like (i.e. undefined behaviour, likely crashes due to infinite
246 recursion or stepping on invalid (freed) pointers).
247
248 ACHTUNG #2:
249
250 ALL cson_values returned as non-const cson_value pointers from any
251 public functions in the cson API are to be treated as if they are
252 heap-allocated, and MUST be freed by client by doing ONE of:
253
254 - Passing it to cson_value_free().
255
256 - Adding it to an Object or Array, in which case the object/array
257 takes over ownership. As of 20110323, a value may be inserted into
258 a single container multiple times, or into multiple containers,
259 in which case they all share ownership (via reference counting)
260 of the original value (meaning any changes to it are visible in
261 all references to it).
262
263 Each call to cson_value_new_xxx() MUST eventually be followed up
264 by one of those options.
265
266 Some cson_value_new_XXX() implementations do not actually allocate
267 memory, but this is an internal implementation detail. Client code
268 MUST NOT rely on this behaviour and MUST treat each object
269 returned by such a function as if it was a freshly-allocated copy
270 (even if their pointer addresses are the same).
271
272 ACHTUNG #3:
273
274 Note that ACHTUNG #2 tells us that we must always free (or transfer
275 ownership of) all pointers returned bycson_value_new_xxx(), but
276 that two calls to (e.g.) cson_value_new_bool(1) will (or might)
@@ -315,11 +315,11 @@
315 if( 0 == rc ) {...success...}
316 else if( cson_rc.ArgError == rc ) { ... some argument was wrong ... }
317 else if( cson_rc.AllocError == rc ) { ... allocation error ... }
318 ...
319 @endcode
320
321 The entries named Parse_XXX are generally only returned by
322 cson_parse() and friends.
323 */
324
325 /** @struct cson_rc_
@@ -472,11 +472,11 @@
472
473 /**
474 Length, in bytes.
475 */
476 unsigned int length;
477
478 /**
479 Error code of the parse run (0 for no error).
480 */
481 int errorCode;
482
@@ -523,11 +523,11 @@
523 /**
524 Specifies how to indent (or not) output. The values
525 are:
526
527 (0) == no extra indentation.
528
529 (1) == 1 TAB character for each level.
530
531 (>1) == that number of SPACES for each level.
532 */
533 unsigned char indentation;
@@ -536,11 +536,11 @@
536 Maximum object/array depth to traverse. Traversing deeply can
537 be indicative of cycles in the object/array tree, and this
538 value is used to figure out when to abort the traversal.
539 */
540 unsigned short maxDepth;
541
542 /**
543 If true, a newline will be added to generated output,
544 else not.
545 */
546 char addNewline;
@@ -636,11 +636,11 @@
636
637 Must return 0 on success, non-0 on error (preferably a value from
638 cson_rc).
639
640 These functions are called relatively often during the JSON-output
641 process, and should try to be fast.
642 */
643 typedef int (*cson_data_dest_f)( void * state, void const * src, unsigned int n );
644
645 /**
646 Reads JSON-formatted string data (in ASCII, UTF8, or UTF16), using the
@@ -664,15 +664,15 @@
664 used.
665
666 The info argument may be NULL. If it is not NULL then the parser
667 populates it with information which is useful in error
668 reporting. Namely, it contains the line/column of parse errors.
669
670 The srcState argument is ignored by this function but is passed on to src,
671 so any output-destination-specific state can be stored there and accessed
672 via the src callback.
673
674 Non-parse error conditions include:
675
676 - (!tgt) or !src: cson_rc.ArgError
677 - cson_rc.AllocError can happen at any time during the input phase
678
@@ -725,11 +725,11 @@
725 TODOs:
726
727 - Buffer the input in larger chunks. We currently read
728 byte-by-byte, but i'm too tired to write/test the looping code for
729 the buffering.
730
731 @see cson_parse_FILE()
732 @see cson_parse_string()
733 */
734 int cson_parse( cson_value ** tgt, cson_data_source_f src, void * srcState,
735 cson_parse_opt const * opt, cson_parse_info * info );
@@ -786,11 +786,11 @@
786 The destState parameter is ignored by this function and is passed
787 on to the dest function.
788
789 Returns 0 on success. On error, any amount of output might have been
790 generated before the error was triggered.
791
792 Example:
793
794 @code
795 int rc = cson_output( myValue, cson_data_dest_FILE, stdout, NULL );
796 // basically equivalent to: cson_output_FILE( myValue, stdout, NULL );
@@ -925,15 +925,15 @@
925 /**
926 Converts the given value to a boolean, using JavaScript semantics depending
927 on the concrete type of val:
928
929 undef or null: false
930
931 boolean: same
932
933 integer, double: 0 or 0.0 == false, else true
934
935 object, array: true
936
937 string: length-0 string is false, else true.
938
939 Returns 0 on success and assigns *v (if v is not NULL) to either 0 or 1.
@@ -945,18 +945,18 @@
945 Similar to cson_value_fetch_bool(), but fetches an integer value.
946
947 The conversion, if any, depends on the concrete type of val:
948
949 NULL, null, undefined: *v is set to 0 and 0 is returned.
950
951 string, object, array: *v is set to 0 and
952 cson_rc.TypeError is returned. The error may normally be safely
953 ignored, but it is provided for those wanted to know whether a direct
954 conversion was possible.
955
956 integer: *v is set to the int value and 0 is returned.
957
958 double: *v is set to the value truncated to int and 0 is returned.
959 */
960 int cson_value_fetch_integer( cson_value const * val, cson_int_t * v );
961
962 /**
@@ -1084,11 +1084,11 @@
1084
1085 - The maximum number of bytes compared is the lesser of rhsLen and
1086 the length of lhs. If the strings do not match, but compare equal
1087 up to the just-described comparison length, the shorter string is
1088 considered to be less-than the longer one.
1089
1090 - If lhs and rhs are both NULL, or both have a length of 0 then they will
1091 compare equal.
1092
1093 - If lhs is null/length-0 but rhs is not then lhs is considered to be less-than
1094 rhs.
@@ -1110,11 +1110,11 @@
1110 Returns the length, in bytes, of str, or 0 if str is NULL. This is
1111 an O(1) operation.
1112
1113 TODO: add cson_string_length_chars() (is O(N) unless we add another
1114 member to store the char length).
1115
1116 @see cson_string_cstr()
1117 */
1118 unsigned int cson_string_length_bytes( cson_string const * str );
1119
1120 /**
@@ -1207,11 +1207,11 @@
1207 ar is expanded, if needed, to be able to hold at least (ndx+1)
1208 items, and any new entries created by that expansion are empty
1209 (NULL values).
1210
1211 On success, 0 is returned and ownership of v is transfered to ar.
1212
1213 On error ownership of v is NOT modified, and the caller may still
1214 need to clean it up. For example, the following code will introduce
1215 a leak if this function fails:
1216
1217 @code
@@ -1240,11 +1240,11 @@
1240
1241 This is functionally equivalent to
1242 cson_array_set(ar,cson_array_length_get(ar),v), but this
1243 implementation has slightly different array-preallocation policy
1244 (it grows more eagerly).
1245
1246 Returns 0 on success, non-zero on error. Error cases include:
1247
1248 - ar or v are NULL: cson_rc.ArgError
1249
1250 - Array cannot be expanded to hold enough elements: cson_rc.AllocError.
@@ -1283,25 +1283,25 @@
1283 cson_value * cson_new_bool(char v);
1284
1285 /**
1286 Returns the special JSON "null" value. When outputing JSON,
1287 its string representation is "null" (without the quotes).
1288
1289 See cson_value_new_bool() for notes regarding the returned
1290 value's memory.
1291 */
1292 cson_value * cson_value_null();
1293
1294 /**
1295 Equivalent to cson_value_new_bool(1).
1296 */
1297 cson_value * cson_value_true();
1298
1299 /**
1300 Equivalent to cson_value_new_bool(0).
1301 */
1302 cson_value * cson_value_false();
1303
1304 /**
1305 Semantically the same as cson_value_new_bool(), but for integers.
1306 */
1307 cson_value * cson_value_new_integer( cson_int_t v );
@@ -1323,16 +1323,16 @@
1323
1324 /**
1325 Semantically the same as cson_value_new_bool(), but for strings.
1326 This creates a JSON value which copies the first n bytes of str.
1327 The string will automatically be NUL-terminated.
1328
1329 Note that if str is NULL or n is 0, this function still
1330 returns non-NULL value representing that empty string.
1331
1332 Returns NULL on allocation error.
1333
1334 See cson_value_new_bool() for important information about the
1335 returned memory.
1336 */
1337 cson_value * cson_value_new_string( char const * str, unsigned int n );
1338
@@ -1346,32 +1346,32 @@
1346 Post-conditions: cson_value_is_object(value) will return true.
1347
1348 @see cson_value_new_array()
1349 @see cson_value_free()
1350 */
1351 cson_value * cson_value_new_object();
1352
1353 /**
1354 This works like cson_value_new_object() but returns an Object
1355 handle directly.
1356
1357 The value handle for the returned object can be fetched with
1358 cson_object_value(theObject).
1359
1360 Ownership is transfered to the caller, who must eventually free it
1361 by passing the Value handle (NOT the Object handle) to
1362 cson_value_free() or passing ownership to a parent container.
1363
1364 Returns NULL on error (out of memory).
1365 */
1366 cson_object * cson_new_object();
1367
1368 /**
1369 Identical to cson_new_object() except that it creates
1370 an Array.
1371 */
1372 cson_array * cson_new_array();
1373
1374 /**
1375 Identical to cson_new_object() except that it creates
1376 a String.
1377 */
@@ -1403,11 +1403,11 @@
1403 Post-conditions: cson_value_is_array(value) will return true.
1404
1405 @see cson_value_new_object()
1406 @see cson_value_free()
1407 */
1408 cson_value * cson_value_new_array();
1409
1410 /**
1411 Frees any resources owned by v, then frees v. If v is a container
1412 type (object or array) its children are also freed (recursively).
1413
@@ -1419,11 +1419,11 @@
1419 cson_value_add_reference(). Even if this function does not
1420 immediately destroy the value, the value must be considered, from
1421 the perspective of that client code, to have been
1422 destroyed/invalidated by this call.
1423
1424
1425 @see cson_value_new_object()
1426 @see cson_value_new_array()
1427 @see cson_value_add_reference()
1428 */
1429 void cson_value_free(cson_value * v);
@@ -1445,11 +1445,11 @@
1445 case, the key is set to the special null value.
1446
1447 The key may be encoded as ASCII or UTF8. Results are undefined
1448 with other encodings, and the errors won't show up here, but may
1449 show up later, e.g. during output.
1450
1451 Returns 0 on success, non-0 on error. It has the following error
1452 cases:
1453
1454 - cson_rc.ArgError: obj or key are NULL or strlen(key) is 0.
1455
@@ -1498,11 +1498,11 @@
1498 */
1499 int cson_object_set_s( cson_object * obj, cson_string * key, cson_value * v );
1500
1501 /**
1502 Removes a property from an object.
1503
1504 If obj contains the given key, it is removed and 0 is returned. If
1505 it is not found, cson_rc.NotFoundError is returned (which can
1506 normally be ignored by client code).
1507
1508 cson_rc.ArgError is returned if obj or key are NULL or key has
@@ -1569,33 +1569,33 @@
1569
1570 If it finds the given path, it returns the value by assiging *tgt
1571 to it. If tgt is NULL then this function has no side-effects but
1572 will return 0 if the given path is found within the object, so it can be used
1573 to test for existence without fetching it.
1574
1575 Returns 0 if it finds an entry, cson_rc.NotFoundError if it finds
1576 no item, and any other non-zero error code on a "real" error. Errors include:
1577
1578 - obj or path are NULL: cson_rc.ArgError
1579
1580 - separator is 0, or path is an empty string or contains only
1581 separator characters: cson_rc.RangeError
1582
1583 - There is an upper limit on how long a single path component may
1584 be (some "reasonable" internal size), and cson_rc.RangeError is
1585 returned if that length is violated.
1586
1587
1588 Limitations:
1589
1590 - It has no way to fetch data from arrays this way. i could
1591 imagine, e.g., a path of "subobj.subArray.0" for
1592 subobj.subArray[0], or "0.3.1" for [0][3][1]. But i'm too
1593 lazy/tired to add this.
1594
1595 Example usage:
1596
1597
1598 Assume we have a JSON structure which abstractly looks like:
1599
1600 @code
1601 {"subobj":{"subsubobj":{"myValue":[1,2,3]}}}
@@ -1613,11 +1613,11 @@
1613 "subobj/subsubobj/myValue" with separator='/' is equivalent the
1614 path "subobj.subsubobj.myValue" with separator='.'. The value of 0
1615 is not legal as a separator character because we cannot
1616 distinguish that use from the real end-of-string without requiring
1617 the caller to also pass in the length of the string.
1618
1619 Multiple successive separators in the list are collapsed into a
1620 single separator for parsing purposes. e.g. the path "a...b...c"
1621 (separator='.') is equivalent to "a.b.c".
1622
1623 @see cson_object_get_sub()
@@ -1700,11 +1700,11 @@
1700 @see cson_object_iter_init()
1701 @see cson_object_iter_next()
1702 */
1703 struct cson_object_iterator
1704 {
1705
1706 /** @internal
1707 The underlying object.
1708 */
1709 cson_object const * obj;
1710 /** @internal
@@ -1771,11 +1771,11 @@
1771 ...
1772 }
1773 @endcode
1774
1775 There is no need to clean up an iterator, as it holds no dynamic resources.
1776
1777 @see cson_kvp_key()
1778 @see cson_kvp_value()
1779 */
1780 cson_kvp * cson_object_iter_next( cson_object_iterator * iter );
1781
@@ -1863,11 +1863,11 @@
1863 buf = cson_buffer_empty;
1864 @endcode
1865
1866 (You might also need to store buf.used and buf.capacity,
1867 depending on what you want to do with the memory.)
1868
1869 When doing so, the memory must eventually be passed to free()
1870 to deallocate it.
1871 */
1872 unsigned char * mem;
1873 };
@@ -1894,11 +1894,11 @@
1894 On error non-zero is returned. Errors include:
1895
1896 - Invalid arguments: cson_rc.ArgError
1897
1898 - Buffer cannot be expanded (runs out of memory): cson_rc.AllocError
1899
1900 Example usage:
1901
1902 @code
1903 cson_buffer buf = cson_buffer_empty;
1904 // optional: cson_buffer_reserve(&buf, 1024 * 10);
@@ -1918,13 +1918,13 @@
1918 buf = cson_buffer_empty;
1919 ...
1920 free(mem);
1921 }
1922 @endcode
1923
1924 @see cson_output()
1925
1926 */
1927 int cson_output_buffer( cson_value const * v, cson_buffer * buf,
1928 cson_output_opt const * opt );
1929
1930 /**
@@ -1993,11 +1993,11 @@
1993 calling cson_buffer_reserve(dest,0).
1994
1995 dest->mem might (and possibly will) be (re)allocated by this
1996 function, so any pointers to it held from before this call might be
1997 invalidated by this call.
1998
1999 On error non-0 is returned and dest has almost certainly been
2000 modified but its state must be considered incomplete.
2001
2002 Errors include:
2003
@@ -2042,11 +2042,11 @@
2042 void * mem = buf.mem;
2043 buf = cson_buffer_empty;
2044 @endcode
2045
2046 In which case the memory must eventually be passed to free() to
2047 free it.
2048 */
2049 int cson_buffer_fill_from( cson_buffer * dest, cson_data_source_f src, void * state );
2050
2051
2052 /**
@@ -2072,11 +2072,11 @@
2072 for other sharing points which added a reference.
2073
2074 Normally any such value handles would be invalidated when the
2075 parent container(s) is/are cleaned up, but this function can be
2076 used to effectively delay the cleanup.
2077
2078 This function, at its lowest level, increments the value's
2079 reference count by 1.
2080
2081 To decrement the reference count, pass the value to
2082 cson_value_free(), after which the value must be considered, from
@@ -2091,11 +2091,11 @@
2091 errors long before the reference count could overflow (assuming
2092 those reference counts come from container insertions, as opposed
2093 to via this function).
2094
2095 Insider notes which clients really need to know:
2096
2097 For shared/constant value instances, such as those returned by
2098 cson_value_true() and cson_value_null(), this function has no side
2099 effects - it does not actually modify the reference count because
2100 (A) those instances are shared across all client code and (B) those
2101 objects are static and never get cleaned up. However, that is an
@@ -2173,11 +2173,11 @@
2173 returned object will be of the same logical type as orig.
2174
2175 ACHTUNG: if orig contains any cyclic references at any depth level
2176 this function will endlessly recurse. (Having _any_ cyclic
2177 references violates this library's requirements.)
2178
2179 Returns NULL if orig is NULL or if cloning fails. Assuming that
2180 orig is in a valid state, the only "likely" error case is that an
2181 allocation fails while constructing the clone. In other words, if
2182 cloning fails due to something other than an allocation error then
2183 either orig is in an invalid state or there is a bug.
@@ -2278,11 +2278,11 @@
2278 --key=VAL : Treats VAL as either a double, integer, or string.
2279
2280 --key= : Treats key as a JSON null (not literal NULL) value.
2281
2282 Arguments not starting with a dash are skipped.
2283
2284 Each key/value pair is inserted into an object. If a given key
2285 appears more than once then only the final entry is actually
2286 stored.
2287
2288 argc and argv are expected to be values from main() (or similar,
@@ -2434,22 +2434,22 @@
2434 Integer, double, null, or string (TEXT and BLOB data, though not
2435 all blob data is legal for a JSON string).
2436
2437 st must be a sqlite3_step()'d row and col must be a 0-based column
2438 index within that result row.
2439 */
2440 cson_value * cson_sqlite3_column_to_value( sqlite3_stmt * st, int col );
2441
2442 /**
2443 Creates a JSON Array object containing the names of all columns
2444 of the given prepared statement handle.
2445
2446 Returns a new array value on success, which the caller owns. Its elements
2447 are in the same order as in the underlying query.
2448
2449 On error NULL is returned.
2450
2451 st is not traversed or freed by this function - only the column
2452 count and names are read.
2453 */
2454 cson_value * cson_sqlite3_column_names( sqlite3_stmt * st );
2455
@@ -2494,22 +2494,22 @@
2494 */
2495 cson_value * cson_sqlite3_row_to_array( sqlite3_stmt * st );
2496 /**
2497 Converts the results of an sqlite3 SELECT statement to JSON,
2498 in the form of a cson_value object tree.
2499
2500 st must be a prepared, but not yet traversed, SELECT query.
2501 tgt must be a pointer to NULL (see the example below). If
2502 either of those arguments are NULL, cson_rc.ArgError is returned.
2503
2504 This walks the query results and returns a JSON object which
2505 has a different structure depending on the value of the 'fat'
2506 argument.
2507
2508
2509 If 'fat' is 0 then the structure is:
2510
2511 @code
2512 {
2513 "columns":["colName1",..."colNameN"],
2514 "rows":[
2515 [colVal0, ... colValN],
@@ -2516,16 +2516,16 @@
2516 [colVal0, ... colValN],
2517 ...
2518 ]
2519 }
2520 @endcode
2521
2522 In the "non-fat" format the order of the columns and row values is
2523 guaranteed to be the same as that of the underlying query.
2524
2525 If 'fat' is not 0 then the structure is:
2526
2527 @code
2528 {
2529 "columns":["colName1",..."colNameN"],
2530 "rows":[
2531 {"colName1":value1,..."colNameN":valueN},
@@ -2543,33 +2543,33 @@
2543
2544 On success it returns 0 and assigns *tgt to a newly-allocated
2545 JSON object tree (using the above structure), which the caller owns.
2546 If the query returns no rows, the "rows" value will be an empty
2547 array, as opposed to null.
2548
2549 On error non-0 is returned and *tgt is not modified.
2550
2551 The error code cson_rc.IOError is used to indicate a db-level
2552 error, and cson_rc.TypeError is returned if sqlite3_column_count(st)
2553 returns 0 or less (indicating an invalid or non-SELECT statement).
2554
2555 The JSON data types are determined by the column type as reported
2556 by sqlite3_column_type():
2557
2558 SQLITE_INTEGER: integer
2559
2560 SQLITE_FLOAT: double
2561
2562 SQLITE_TEXT or SQLITE_BLOB: string, and this will only work if
2563 the data is UTF8 compatible.
2564
2565 If the db returns a literal or SQL NULL for a value it is converted
2566 to a JSON null. If it somehow finds a column type it cannot handle,
2567 the value is also converted to a NULL in the output.
2568
2569 Example
2570
2571 @code
2572 cson_value * json = NULL;
2573 int rc = cson_sqlite3_stmt_to_json( myStatement, &json, 1 );
2574 if( 0 != rc ) { ... error ... }
2575 else {
@@ -2603,14 +2603,14 @@
2603 TODO: add Object support for named parameters.
2604
2605 Returns 0 on success, non-0 on error.
2606 */
2607 int cson_sqlite3_bind_value( sqlite3_stmt * st, int ndx, cson_value const * v );
2608
2609 #if defined(__cplusplus)
2610 } /*extern "C"*/
2611 #endif
2612
2613 #endif /* CSON_ENABLE_SQLITE3 */
2614 #endif /* WANDERINGHORSE_NET_CSON_SQLITE3_H_INCLUDED */
2615 /* end file include/wh/cson/cson_sqlite3.h */
2616 #endif /* FOSSIL_ENABLE_JSON */
2617
--- src/cson_amalgamation.h
+++ src/cson_amalgamation.h
@@ -59,11 +59,11 @@
59 #define CSON_INT_T_PFMT "I64d"
60 #elif (__STDC_VERSION__ >= 199901L) || (HAVE_LONG_LONG == 1)
61 typedef long long cson_int_t;
62 #define CSON_INT_T_SFMT "lld"
63 #define CSON_INT_T_PFMT "lld"
64 #else
65 typedef long cson_int_t;
66 #define CSON_INT_T_SFMT "ld"
67 #define CSON_INT_T_PFMT "ld"
68 #endif
69
@@ -215,11 +215,11 @@
215 Convenience typedef.
216 */
217 typedef struct cson_value cson_value;
218
219 /** @struct cson_value
220
221 The core value type of this API. It is opaque to clients, and
222 only the cson public API should be used for setting or
223 inspecting their values.
224
225 This class is opaque because stack-based usage can easily cause
@@ -231,11 +231,11 @@
231 counting) as long as those insertions do not cause cycles. However,
232 be very aware that such value re-use uses a reference to the
233 original copy, meaning that if its value is changed once, it is
234 changed everywhere. Also beware that multi-threaded write
235 operations on such references leads to undefined behaviour.
236
237 PLEASE read the ACHTUNGEN below...
238
239 ACHTUNG #1:
240
241 cson_values MUST NOT form cycles (e.g. via object or array
@@ -244,33 +244,33 @@
244 Not abiding th Holy Law Of No Cycles will lead to double-frees and
245 the like (i.e. undefined behaviour, likely crashes due to infinite
246 recursion or stepping on invalid (freed) pointers).
247
248 ACHTUNG #2:
249
250 ALL cson_values returned as non-const cson_value pointers from any
251 public functions in the cson API are to be treated as if they are
252 heap-allocated, and MUST be freed by client by doing ONE of:
253
254 - Passing it to cson_value_free().
255
256 - Adding it to an Object or Array, in which case the object/array
257 takes over ownership. As of 20110323, a value may be inserted into
258 a single container multiple times, or into multiple containers,
259 in which case they all share ownership (via reference counting)
260 of the original value (meaning any changes to it are visible in
261 all references to it).
262
263 Each call to cson_value_new_xxx() MUST eventually be followed up
264 by one of those options.
265
266 Some cson_value_new_XXX() implementations do not actually allocate
267 memory, but this is an internal implementation detail. Client code
268 MUST NOT rely on this behaviour and MUST treat each object
269 returned by such a function as if it was a freshly-allocated copy
270 (even if their pointer addresses are the same).
271
272 ACHTUNG #3:
273
274 Note that ACHTUNG #2 tells us that we must always free (or transfer
275 ownership of) all pointers returned bycson_value_new_xxx(), but
276 that two calls to (e.g.) cson_value_new_bool(1) will (or might)
@@ -315,11 +315,11 @@
315 if( 0 == rc ) {...success...}
316 else if( cson_rc.ArgError == rc ) { ... some argument was wrong ... }
317 else if( cson_rc.AllocError == rc ) { ... allocation error ... }
318 ...
319 @endcode
320
321 The entries named Parse_XXX are generally only returned by
322 cson_parse() and friends.
323 */
324
325 /** @struct cson_rc_
@@ -472,11 +472,11 @@
472
473 /**
474 Length, in bytes.
475 */
476 unsigned int length;
477
478 /**
479 Error code of the parse run (0 for no error).
480 */
481 int errorCode;
482
@@ -523,11 +523,11 @@
523 /**
524 Specifies how to indent (or not) output. The values
525 are:
526
527 (0) == no extra indentation.
528
529 (1) == 1 TAB character for each level.
530
531 (>1) == that number of SPACES for each level.
532 */
533 unsigned char indentation;
@@ -536,11 +536,11 @@
536 Maximum object/array depth to traverse. Traversing deeply can
537 be indicative of cycles in the object/array tree, and this
538 value is used to figure out when to abort the traversal.
539 */
540 unsigned short maxDepth;
541
542 /**
543 If true, a newline will be added to generated output,
544 else not.
545 */
546 char addNewline;
@@ -636,11 +636,11 @@
636
637 Must return 0 on success, non-0 on error (preferably a value from
638 cson_rc).
639
640 These functions are called relatively often during the JSON-output
641 process, and should try to be fast.
642 */
643 typedef int (*cson_data_dest_f)( void * state, void const * src, unsigned int n );
644
645 /**
646 Reads JSON-formatted string data (in ASCII, UTF8, or UTF16), using the
@@ -664,15 +664,15 @@
664 used.
665
666 The info argument may be NULL. If it is not NULL then the parser
667 populates it with information which is useful in error
668 reporting. Namely, it contains the line/column of parse errors.
669
670 The srcState argument is ignored by this function but is passed on to src,
671 so any output-destination-specific state can be stored there and accessed
672 via the src callback.
673
674 Non-parse error conditions include:
675
676 - (!tgt) or !src: cson_rc.ArgError
677 - cson_rc.AllocError can happen at any time during the input phase
678
@@ -725,11 +725,11 @@
725 TODOs:
726
727 - Buffer the input in larger chunks. We currently read
728 byte-by-byte, but i'm too tired to write/test the looping code for
729 the buffering.
730
731 @see cson_parse_FILE()
732 @see cson_parse_string()
733 */
734 int cson_parse( cson_value ** tgt, cson_data_source_f src, void * srcState,
735 cson_parse_opt const * opt, cson_parse_info * info );
@@ -786,11 +786,11 @@
786 The destState parameter is ignored by this function and is passed
787 on to the dest function.
788
789 Returns 0 on success. On error, any amount of output might have been
790 generated before the error was triggered.
791
792 Example:
793
794 @code
795 int rc = cson_output( myValue, cson_data_dest_FILE, stdout, NULL );
796 // basically equivalent to: cson_output_FILE( myValue, stdout, NULL );
@@ -925,15 +925,15 @@
925 /**
926 Converts the given value to a boolean, using JavaScript semantics depending
927 on the concrete type of val:
928
929 undef or null: false
930
931 boolean: same
932
933 integer, double: 0 or 0.0 == false, else true
934
935 object, array: true
936
937 string: length-0 string is false, else true.
938
939 Returns 0 on success and assigns *v (if v is not NULL) to either 0 or 1.
@@ -945,18 +945,18 @@
945 Similar to cson_value_fetch_bool(), but fetches an integer value.
946
947 The conversion, if any, depends on the concrete type of val:
948
949 NULL, null, undefined: *v is set to 0 and 0 is returned.
950
951 string, object, array: *v is set to 0 and
952 cson_rc.TypeError is returned. The error may normally be safely
953 ignored, but it is provided for those wanted to know whether a direct
954 conversion was possible.
955
956 integer: *v is set to the int value and 0 is returned.
957
958 double: *v is set to the value truncated to int and 0 is returned.
959 */
960 int cson_value_fetch_integer( cson_value const * val, cson_int_t * v );
961
962 /**
@@ -1084,11 +1084,11 @@
1084
1085 - The maximum number of bytes compared is the lesser of rhsLen and
1086 the length of lhs. If the strings do not match, but compare equal
1087 up to the just-described comparison length, the shorter string is
1088 considered to be less-than the longer one.
1089
1090 - If lhs and rhs are both NULL, or both have a length of 0 then they will
1091 compare equal.
1092
1093 - If lhs is null/length-0 but rhs is not then lhs is considered to be less-than
1094 rhs.
@@ -1110,11 +1110,11 @@
1110 Returns the length, in bytes, of str, or 0 if str is NULL. This is
1111 an O(1) operation.
1112
1113 TODO: add cson_string_length_chars() (is O(N) unless we add another
1114 member to store the char length).
1115
1116 @see cson_string_cstr()
1117 */
1118 unsigned int cson_string_length_bytes( cson_string const * str );
1119
1120 /**
@@ -1207,11 +1207,11 @@
1207 ar is expanded, if needed, to be able to hold at least (ndx+1)
1208 items, and any new entries created by that expansion are empty
1209 (NULL values).
1210
1211 On success, 0 is returned and ownership of v is transfered to ar.
1212
1213 On error ownership of v is NOT modified, and the caller may still
1214 need to clean it up. For example, the following code will introduce
1215 a leak if this function fails:
1216
1217 @code
@@ -1240,11 +1240,11 @@
1240
1241 This is functionally equivalent to
1242 cson_array_set(ar,cson_array_length_get(ar),v), but this
1243 implementation has slightly different array-preallocation policy
1244 (it grows more eagerly).
1245
1246 Returns 0 on success, non-zero on error. Error cases include:
1247
1248 - ar or v are NULL: cson_rc.ArgError
1249
1250 - Array cannot be expanded to hold enough elements: cson_rc.AllocError.
@@ -1283,25 +1283,25 @@
1283 cson_value * cson_new_bool(char v);
1284
1285 /**
1286 Returns the special JSON "null" value. When outputing JSON,
1287 its string representation is "null" (without the quotes).
1288
1289 See cson_value_new_bool() for notes regarding the returned
1290 value's memory.
1291 */
1292 cson_value * cson_value_null( void );
1293
1294 /**
1295 Equivalent to cson_value_new_bool(1).
1296 */
1297 cson_value * cson_value_true( void );
1298
1299 /**
1300 Equivalent to cson_value_new_bool(0).
1301 */
1302 cson_value * cson_value_false( void );
1303
1304 /**
1305 Semantically the same as cson_value_new_bool(), but for integers.
1306 */
1307 cson_value * cson_value_new_integer( cson_int_t v );
@@ -1323,16 +1323,16 @@
1323
1324 /**
1325 Semantically the same as cson_value_new_bool(), but for strings.
1326 This creates a JSON value which copies the first n bytes of str.
1327 The string will automatically be NUL-terminated.
1328
1329 Note that if str is NULL or n is 0, this function still
1330 returns non-NULL value representing that empty string.
1331
1332 Returns NULL on allocation error.
1333
1334 See cson_value_new_bool() for important information about the
1335 returned memory.
1336 */
1337 cson_value * cson_value_new_string( char const * str, unsigned int n );
1338
@@ -1346,32 +1346,32 @@
1346 Post-conditions: cson_value_is_object(value) will return true.
1347
1348 @see cson_value_new_array()
1349 @see cson_value_free()
1350 */
1351 cson_value * cson_value_new_object( void );
1352
1353 /**
1354 This works like cson_value_new_object() but returns an Object
1355 handle directly.
1356
1357 The value handle for the returned object can be fetched with
1358 cson_object_value(theObject).
1359
1360 Ownership is transfered to the caller, who must eventually free it
1361 by passing the Value handle (NOT the Object handle) to
1362 cson_value_free() or passing ownership to a parent container.
1363
1364 Returns NULL on error (out of memory).
1365 */
1366 cson_object * cson_new_object( void );
1367
1368 /**
1369 Identical to cson_new_object() except that it creates
1370 an Array.
1371 */
1372 cson_array * cson_new_array( void );
1373
1374 /**
1375 Identical to cson_new_object() except that it creates
1376 a String.
1377 */
@@ -1403,11 +1403,11 @@
1403 Post-conditions: cson_value_is_array(value) will return true.
1404
1405 @see cson_value_new_object()
1406 @see cson_value_free()
1407 */
1408 cson_value * cson_value_new_array( void );
1409
1410 /**
1411 Frees any resources owned by v, then frees v. If v is a container
1412 type (object or array) its children are also freed (recursively).
1413
@@ -1419,11 +1419,11 @@
1419 cson_value_add_reference(). Even if this function does not
1420 immediately destroy the value, the value must be considered, from
1421 the perspective of that client code, to have been
1422 destroyed/invalidated by this call.
1423
1424
1425 @see cson_value_new_object()
1426 @see cson_value_new_array()
1427 @see cson_value_add_reference()
1428 */
1429 void cson_value_free(cson_value * v);
@@ -1445,11 +1445,11 @@
1445 case, the key is set to the special null value.
1446
1447 The key may be encoded as ASCII or UTF8. Results are undefined
1448 with other encodings, and the errors won't show up here, but may
1449 show up later, e.g. during output.
1450
1451 Returns 0 on success, non-0 on error. It has the following error
1452 cases:
1453
1454 - cson_rc.ArgError: obj or key are NULL or strlen(key) is 0.
1455
@@ -1498,11 +1498,11 @@
1498 */
1499 int cson_object_set_s( cson_object * obj, cson_string * key, cson_value * v );
1500
1501 /**
1502 Removes a property from an object.
1503
1504 If obj contains the given key, it is removed and 0 is returned. If
1505 it is not found, cson_rc.NotFoundError is returned (which can
1506 normally be ignored by client code).
1507
1508 cson_rc.ArgError is returned if obj or key are NULL or key has
@@ -1569,33 +1569,33 @@
1569
1570 If it finds the given path, it returns the value by assiging *tgt
1571 to it. If tgt is NULL then this function has no side-effects but
1572 will return 0 if the given path is found within the object, so it can be used
1573 to test for existence without fetching it.
1574
1575 Returns 0 if it finds an entry, cson_rc.NotFoundError if it finds
1576 no item, and any other non-zero error code on a "real" error. Errors include:
1577
1578 - obj or path are NULL: cson_rc.ArgError
1579
1580 - separator is 0, or path is an empty string or contains only
1581 separator characters: cson_rc.RangeError
1582
1583 - There is an upper limit on how long a single path component may
1584 be (some "reasonable" internal size), and cson_rc.RangeError is
1585 returned if that length is violated.
1586
1587
1588 Limitations:
1589
1590 - It has no way to fetch data from arrays this way. i could
1591 imagine, e.g., a path of "subobj.subArray.0" for
1592 subobj.subArray[0], or "0.3.1" for [0][3][1]. But i'm too
1593 lazy/tired to add this.
1594
1595 Example usage:
1596
1597
1598 Assume we have a JSON structure which abstractly looks like:
1599
1600 @code
1601 {"subobj":{"subsubobj":{"myValue":[1,2,3]}}}
@@ -1613,11 +1613,11 @@
1613 "subobj/subsubobj/myValue" with separator='/' is equivalent the
1614 path "subobj.subsubobj.myValue" with separator='.'. The value of 0
1615 is not legal as a separator character because we cannot
1616 distinguish that use from the real end-of-string without requiring
1617 the caller to also pass in the length of the string.
1618
1619 Multiple successive separators in the list are collapsed into a
1620 single separator for parsing purposes. e.g. the path "a...b...c"
1621 (separator='.') is equivalent to "a.b.c".
1622
1623 @see cson_object_get_sub()
@@ -1700,11 +1700,11 @@
1700 @see cson_object_iter_init()
1701 @see cson_object_iter_next()
1702 */
1703 struct cson_object_iterator
1704 {
1705
1706 /** @internal
1707 The underlying object.
1708 */
1709 cson_object const * obj;
1710 /** @internal
@@ -1771,11 +1771,11 @@
1771 ...
1772 }
1773 @endcode
1774
1775 There is no need to clean up an iterator, as it holds no dynamic resources.
1776
1777 @see cson_kvp_key()
1778 @see cson_kvp_value()
1779 */
1780 cson_kvp * cson_object_iter_next( cson_object_iterator * iter );
1781
@@ -1863,11 +1863,11 @@
1863 buf = cson_buffer_empty;
1864 @endcode
1865
1866 (You might also need to store buf.used and buf.capacity,
1867 depending on what you want to do with the memory.)
1868
1869 When doing so, the memory must eventually be passed to free()
1870 to deallocate it.
1871 */
1872 unsigned char * mem;
1873 };
@@ -1894,11 +1894,11 @@
1894 On error non-zero is returned. Errors include:
1895
1896 - Invalid arguments: cson_rc.ArgError
1897
1898 - Buffer cannot be expanded (runs out of memory): cson_rc.AllocError
1899
1900 Example usage:
1901
1902 @code
1903 cson_buffer buf = cson_buffer_empty;
1904 // optional: cson_buffer_reserve(&buf, 1024 * 10);
@@ -1918,13 +1918,13 @@
1918 buf = cson_buffer_empty;
1919 ...
1920 free(mem);
1921 }
1922 @endcode
1923
1924 @see cson_output()
1925
1926 */
1927 int cson_output_buffer( cson_value const * v, cson_buffer * buf,
1928 cson_output_opt const * opt );
1929
1930 /**
@@ -1993,11 +1993,11 @@
1993 calling cson_buffer_reserve(dest,0).
1994
1995 dest->mem might (and possibly will) be (re)allocated by this
1996 function, so any pointers to it held from before this call might be
1997 invalidated by this call.
1998
1999 On error non-0 is returned and dest has almost certainly been
2000 modified but its state must be considered incomplete.
2001
2002 Errors include:
2003
@@ -2042,11 +2042,11 @@
2042 void * mem = buf.mem;
2043 buf = cson_buffer_empty;
2044 @endcode
2045
2046 In which case the memory must eventually be passed to free() to
2047 free it.
2048 */
2049 int cson_buffer_fill_from( cson_buffer * dest, cson_data_source_f src, void * state );
2050
2051
2052 /**
@@ -2072,11 +2072,11 @@
2072 for other sharing points which added a reference.
2073
2074 Normally any such value handles would be invalidated when the
2075 parent container(s) is/are cleaned up, but this function can be
2076 used to effectively delay the cleanup.
2077
2078 This function, at its lowest level, increments the value's
2079 reference count by 1.
2080
2081 To decrement the reference count, pass the value to
2082 cson_value_free(), after which the value must be considered, from
@@ -2091,11 +2091,11 @@
2091 errors long before the reference count could overflow (assuming
2092 those reference counts come from container insertions, as opposed
2093 to via this function).
2094
2095 Insider notes which clients really need to know:
2096
2097 For shared/constant value instances, such as those returned by
2098 cson_value_true() and cson_value_null(), this function has no side
2099 effects - it does not actually modify the reference count because
2100 (A) those instances are shared across all client code and (B) those
2101 objects are static and never get cleaned up. However, that is an
@@ -2173,11 +2173,11 @@
2173 returned object will be of the same logical type as orig.
2174
2175 ACHTUNG: if orig contains any cyclic references at any depth level
2176 this function will endlessly recurse. (Having _any_ cyclic
2177 references violates this library's requirements.)
2178
2179 Returns NULL if orig is NULL or if cloning fails. Assuming that
2180 orig is in a valid state, the only "likely" error case is that an
2181 allocation fails while constructing the clone. In other words, if
2182 cloning fails due to something other than an allocation error then
2183 either orig is in an invalid state or there is a bug.
@@ -2278,11 +2278,11 @@
2278 --key=VAL : Treats VAL as either a double, integer, or string.
2279
2280 --key= : Treats key as a JSON null (not literal NULL) value.
2281
2282 Arguments not starting with a dash are skipped.
2283
2284 Each key/value pair is inserted into an object. If a given key
2285 appears more than once then only the final entry is actually
2286 stored.
2287
2288 argc and argv are expected to be values from main() (or similar,
@@ -2434,22 +2434,22 @@
2434 Integer, double, null, or string (TEXT and BLOB data, though not
2435 all blob data is legal for a JSON string).
2436
2437 st must be a sqlite3_step()'d row and col must be a 0-based column
2438 index within that result row.
2439 */
2440 cson_value * cson_sqlite3_column_to_value( sqlite3_stmt * st, int col );
2441
2442 /**
2443 Creates a JSON Array object containing the names of all columns
2444 of the given prepared statement handle.
2445
2446 Returns a new array value on success, which the caller owns. Its elements
2447 are in the same order as in the underlying query.
2448
2449 On error NULL is returned.
2450
2451 st is not traversed or freed by this function - only the column
2452 count and names are read.
2453 */
2454 cson_value * cson_sqlite3_column_names( sqlite3_stmt * st );
2455
@@ -2494,22 +2494,22 @@
2494 */
2495 cson_value * cson_sqlite3_row_to_array( sqlite3_stmt * st );
2496 /**
2497 Converts the results of an sqlite3 SELECT statement to JSON,
2498 in the form of a cson_value object tree.
2499
2500 st must be a prepared, but not yet traversed, SELECT query.
2501 tgt must be a pointer to NULL (see the example below). If
2502 either of those arguments are NULL, cson_rc.ArgError is returned.
2503
2504 This walks the query results and returns a JSON object which
2505 has a different structure depending on the value of the 'fat'
2506 argument.
2507
2508
2509 If 'fat' is 0 then the structure is:
2510
2511 @code
2512 {
2513 "columns":["colName1",..."colNameN"],
2514 "rows":[
2515 [colVal0, ... colValN],
@@ -2516,16 +2516,16 @@
2516 [colVal0, ... colValN],
2517 ...
2518 ]
2519 }
2520 @endcode
2521
2522 In the "non-fat" format the order of the columns and row values is
2523 guaranteed to be the same as that of the underlying query.
2524
2525 If 'fat' is not 0 then the structure is:
2526
2527 @code
2528 {
2529 "columns":["colName1",..."colNameN"],
2530 "rows":[
2531 {"colName1":value1,..."colNameN":valueN},
@@ -2543,33 +2543,33 @@
2543
2544 On success it returns 0 and assigns *tgt to a newly-allocated
2545 JSON object tree (using the above structure), which the caller owns.
2546 If the query returns no rows, the "rows" value will be an empty
2547 array, as opposed to null.
2548
2549 On error non-0 is returned and *tgt is not modified.
2550
2551 The error code cson_rc.IOError is used to indicate a db-level
2552 error, and cson_rc.TypeError is returned if sqlite3_column_count(st)
2553 returns 0 or less (indicating an invalid or non-SELECT statement).
2554
2555 The JSON data types are determined by the column type as reported
2556 by sqlite3_column_type():
2557
2558 SQLITE_INTEGER: integer
2559
2560 SQLITE_FLOAT: double
2561
2562 SQLITE_TEXT or SQLITE_BLOB: string, and this will only work if
2563 the data is UTF8 compatible.
2564
2565 If the db returns a literal or SQL NULL for a value it is converted
2566 to a JSON null. If it somehow finds a column type it cannot handle,
2567 the value is also converted to a NULL in the output.
2568
2569 Example
2570
2571 @code
2572 cson_value * json = NULL;
2573 int rc = cson_sqlite3_stmt_to_json( myStatement, &json, 1 );
2574 if( 0 != rc ) { ... error ... }
2575 else {
@@ -2603,14 +2603,14 @@
2603 TODO: add Object support for named parameters.
2604
2605 Returns 0 on success, non-0 on error.
2606 */
2607 int cson_sqlite3_bind_value( sqlite3_stmt * st, int ndx, cson_value const * v );
2608
2609 #if defined(__cplusplus)
2610 } /*extern "C"*/
2611 #endif
2612
2613 #endif /* CSON_ENABLE_SQLITE3 */
2614 #endif /* WANDERINGHORSE_NET_CSON_SQLITE3_H_INCLUDED */
2615 /* end file include/wh/cson/cson_sqlite3.h */
2616 #endif /* FOSSIL_ENABLE_JSON */
2617
+186 -38
--- src/db.c
+++ src/db.c
@@ -124,19 +124,37 @@
124124
} aHook[5];
125125
char *azDeleteOnFail[3]; /* Files to delete on a failure */
126126
char *azBeforeCommit[5]; /* Commands to run prior to COMMIT */
127127
int nBeforeCommit; /* Number of entries in azBeforeCommit */
128128
int nPriorChanges; /* sqlite3_total_changes() at transaction start */
129
+ const char *zStartFile; /* File in which transaction was started */
130
+ int iStartLine; /* Line of zStartFile where transaction started */
129131
} db = {0, 0, 0, 0, 0, 0, };
130132
131133
/*
132134
** Arrange for the given file to be deleted on a failure.
133135
*/
134136
void db_delete_on_failure(const char *zFilename){
135137
assert( db.nDeleteOnFail<count(db.azDeleteOnFail) );
136138
db.azDeleteOnFail[db.nDeleteOnFail++] = fossil_strdup(zFilename);
137139
}
140
+
141
+/*
142
+** Return the transaction nesting depth. 0 means we are currently
143
+** not in a transaction.
144
+*/
145
+int db_transaction_nesting_depth(void){
146
+ return db.nBegin;
147
+}
148
+
149
+/*
150
+** Return a pointer to a string that is the code point where the
151
+** current transaction was started.
152
+*/
153
+char *db_transaction_start_point(void){
154
+ return mprintf("%s:%d", db.zStartFile, db.iStartLine);
155
+}
138156
139157
/*
140158
** This routine is called by the SQLite commit-hook mechanism
141159
** just prior to each commit. All this routine does is verify
142160
** that nBegin really is zero. That insures that transactions
@@ -152,24 +170,66 @@
152170
}
153171
return 0;
154172
}
155173
156174
/*
157
-** Begin and end a nested transaction
175
+** Silently add the filename and line number as parameter to each
176
+** db_begin_transaction call.
158177
*/
159
-void db_begin_transaction(void){
178
+#if INTERFACE
179
+#define db_begin_transaction() db_begin_transaction_real(__FILE__,__LINE__)
180
+#define db_begin_write() db_begin_write_real(__FILE__,__LINE__)
181
+#define db_commit_transaction() db_end_transaction(0)
182
+#define db_rollback_transaction() db_end_transaction(1)
183
+#endif
184
+
185
+/*
186
+** Begin a nested transaction
187
+*/
188
+void db_begin_transaction_real(const char *zStartFile, int iStartLine){
160189
if( db.nBegin==0 ){
161190
db_multi_exec("BEGIN");
162191
sqlite3_commit_hook(g.db, db_verify_at_commit, 0);
163192
db.nPriorChanges = sqlite3_total_changes(g.db);
193
+ db.doRollback = 0;
194
+ db.zStartFile = zStartFile;
195
+ db.iStartLine = iStartLine;
196
+ }
197
+ db.nBegin++;
198
+}
199
+/*
200
+** Begin a new transaction for writing.
201
+*/
202
+void db_begin_write_real(const char *zStartFile, int iStartLine){
203
+ if( db.nBegin==0 ){
204
+ db_multi_exec("BEGIN IMMEDIATE");
205
+ sqlite3_commit_hook(g.db, db_verify_at_commit, 0);
206
+ db.nPriorChanges = sqlite3_total_changes(g.db);
207
+ db.doRollback = 0;
208
+ db.zStartFile = zStartFile;
209
+ db.iStartLine = iStartLine;
210
+ }else{
211
+ fossil_warning("read txn at %s:%d might cause SQLITE_BUSY "
212
+ "for the write txn at %s:%d",
213
+ db.zStartFile, db.iStartLine, zStartFile, iStartLine);
164214
}
165215
db.nBegin++;
166216
}
217
+
218
+/* End a transaction previously started using db_begin_transaction()
219
+** or db_begin_write().
220
+*/
167221
void db_end_transaction(int rollbackFlag){
168222
if( g.db==0 ) return;
169
- if( db.nBegin<=0 ) return;
170
- if( rollbackFlag ) db.doRollback = 1;
223
+ if( db.nBegin<=0 ){
224
+ fossil_warning("Extra call to db_end_transaction");
225
+ return;
226
+ }
227
+ if( rollbackFlag ){
228
+ db.doRollback = 1;
229
+ if( g.fSqlTrace ) fossil_trace("-- ROLLBACK by request\n");
230
+ }
171231
db.nBegin--;
172232
if( db.nBegin==0 ){
173233
int i;
174234
if( db.doRollback==0 && db.nPriorChanges<sqlite3_total_changes(g.db) ){
175235
i = 0;
@@ -180,11 +240,15 @@
180240
i++;
181241
}
182242
leaf_do_pending_checks();
183243
}
184244
for(i=0; db.doRollback==0 && i<db.nCommitHook; i++){
185
- db.doRollback |= db.aHook[i].xHook();
245
+ int rc = db.aHook[i].xHook();
246
+ if( rc ){
247
+ db.doRollback = 1;
248
+ if( g.fSqlTrace ) fossil_trace("-- ROLLBACK due to aHook[%d]\n", i);
249
+ }
186250
}
187251
while( db.pAllStmt ){
188252
db_finalize(db.pAllStmt);
189253
}
190254
db_multi_exec("%s", db.doRollback ? "ROLLBACK" : "COMMIT");
@@ -272,11 +336,11 @@
272336
db.nPrepare++;
273337
if( flags & DB_PREPARE_PERSISTENT ){
274338
prepFlags = SQLITE_PREPARE_PERSISTENT;
275339
}
276340
rc = sqlite3_prepare_v3(g.db, zSql, -1, prepFlags, &pStmt->pStmt, 0);
277
- if( rc!=0 && (flags & DB_PREPARE_IGNORE_ERROR)!=0 ){
341
+ if( rc!=0 && (flags & DB_PREPARE_IGNORE_ERROR)==0 ){
278342
db_err("%s\n%s", sqlite3_errmsg(g.db), zSql);
279343
}
280344
pStmt->pNext = pStmt->pPrev = 0;
281345
pStmt->nStep = 0;
282346
pStmt->rc = rc;
@@ -310,10 +374,31 @@
310374
db.pAllStmt = pStmt;
311375
va_end(ap);
312376
}
313377
return rc;
314378
}
379
+
380
+/* Prepare a statement using text placed inside a Blob
381
+** using blob_append_sql().
382
+*/
383
+int db_prepare_blob(Stmt *pStmt, Blob *pSql){
384
+ int rc;
385
+ char *zSql;
386
+ pStmt->sql = *pSql;
387
+ blob_init(pSql, 0, 0);
388
+ zSql = blob_sql_text(&pStmt->sql);
389
+ db.nPrepare++;
390
+ rc = sqlite3_prepare_v3(g.db, zSql, -1, 0, &pStmt->pStmt, 0);
391
+ if( rc!=0 ){
392
+ db_err("%s\n%s", sqlite3_errmsg(g.db), zSql);
393
+ }
394
+ pStmt->pNext = pStmt->pPrev = 0;
395
+ pStmt->nStep = 0;
396
+ pStmt->rc = rc;
397
+ return rc;
398
+}
399
+
315400
316401
/*
317402
** Return the index of a bind parameter
318403
*/
319404
static int paramIdx(Stmt *pStmt, const char *zParamName){
@@ -428,11 +513,11 @@
428513
** Return the rowid of the most recent insert
429514
*/
430515
int db_last_insert_rowid(void){
431516
i64 x = sqlite3_last_insert_rowid(g.db);
432517
if( x<0 || x>(i64)2147483647 ){
433
- fossil_fatal("rowid out of range (0..2147483647)");
518
+ fossil_panic("rowid out of range (0..2147483647)");
434519
}
435520
return (int)x;
436521
}
437522
438523
/*
@@ -479,10 +564,16 @@
479564
return mprintf("%s", db_column_text(pStmt, N));
480565
}
481566
void db_column_blob(Stmt *pStmt, int N, Blob *pBlob){
482567
blob_append(pBlob, sqlite3_column_blob(pStmt->pStmt, N),
483568
sqlite3_column_bytes(pStmt->pStmt, N));
569
+}
570
+Blob db_column_text_as_blob(Stmt *pStmt, int N){
571
+ Blob x;
572
+ blob_init(&x, (char*)sqlite3_column_text(pStmt->pStmt,N),
573
+ sqlite3_column_bytes(pStmt->pStmt,N));
574
+ return x;
484575
}
485576
486577
/*
487578
** Initialize a blob to an ephemeral copy of the content of a
488579
** column in the current row. The data in the blob will become
@@ -875,10 +966,33 @@
875966
}else{
876967
sqlite3_result_text(context, "utc", -1, SQLITE_STATIC);
877968
}
878969
}
879970
971
+/*
972
+** If the input is a hexadecimal string, convert that string into a BLOB.
973
+** If the input is not a hexadecimal string, return NULL.
974
+*/
975
+void db_hextoblob(
976
+ sqlite3_context *context,
977
+ int argc,
978
+ sqlite3_value **argv
979
+){
980
+ const unsigned char *zIn = sqlite3_value_text(argv[0]);
981
+ int nIn = sqlite3_value_bytes(argv[0]);
982
+ unsigned char *zOut;
983
+ if( zIn==0 ) return;
984
+ if( nIn&1 ) return;
985
+ if( !validate16((const char*)zIn, nIn) ) return;
986
+ zOut = sqlite3_malloc64( nIn/2 );
987
+ if( zOut==0 ){
988
+ sqlite3_result_error_nomem(context);
989
+ return;
990
+ }
991
+ decode16(zIn, zOut, nIn);
992
+ sqlite3_result_blob(context, zOut, nIn/2, sqlite3_free);
993
+}
880994
881995
/*
882996
** Register the SQL functions that are useful both to the internal
883997
** representation and to the "fossil sql" command.
884998
*/
@@ -893,10 +1007,12 @@
8931007
db_now_function, 0, 0);
8941008
sqlite3_create_function(db, "toLocal", 0, SQLITE_UTF8, 0,
8951009
db_tolocal_function, 0, 0);
8961010
sqlite3_create_function(db, "fromLocal", 0, SQLITE_UTF8, 0,
8971011
db_fromlocal_function, 0, 0);
1012
+ sqlite3_create_function(db, "hextoblob", 1, SQLITE_UTF8, 0,
1013
+ db_hextoblob, 0, 0);
8981014
}
8991015
9001016
#if USE_SEE
9011017
/*
9021018
** This is a pointer to the saved database encryption key string.
@@ -939,11 +1055,11 @@
9391055
blobSize = blob_size(pKey);
9401056
if( blobSize==0 ) return;
9411057
fossil_get_page_size(&pageSize);
9421058
assert( pageSize>0 );
9431059
if( blobSize>pageSize ){
944
- fossil_fatal("key blob too large: %u versus %u", blobSize, pageSize);
1060
+ fossil_panic("key blob too large: %u versus %u", blobSize, pageSize);
9451061
}
9461062
p = fossil_secure_alloc_page(&n);
9471063
assert( p!=NULL );
9481064
assert( n==pageSize );
9491065
assert( n>=blobSize );
@@ -973,11 +1089,11 @@
9731089
size_t blobSize = blob_size(pKey);
9741090
if( blobSize==0 ){
9751091
db_unsave_encryption_key();
9761092
}else{
9771093
if( blobSize>savedKeySize ){
978
- fossil_fatal("key blob too large: %u versus %u",
1094
+ fossil_panic("key blob too large: %u versus %u",
9791095
blobSize, savedKeySize);
9801096
}
9811097
fossil_secure_zero(zSavedKey, savedKeySize);
9821098
memcpy(zSavedKey, blob_str(pKey), blobSize);
9831099
}
@@ -1003,11 +1119,11 @@
10031119
HANDLE hProcess = NULL;
10041120
10051121
fossil_get_page_size(&pageSize);
10061122
assert( pageSize>0 );
10071123
if( nSize>pageSize ){
1008
- fossil_fatal("key too large: %u versus %u", nSize, pageSize);
1124
+ fossil_panic("key too large: %u versus %u", nSize, pageSize);
10091125
}
10101126
p = fossil_secure_alloc_page(&n);
10111127
assert( p!=NULL );
10121128
assert( n==pageSize );
10131129
assert( n>=nSize );
@@ -1019,20 +1135,20 @@
10191135
if( nRead==nSize ){
10201136
db_unsave_encryption_key();
10211137
zSavedKey = p;
10221138
savedKeySize = n;
10231139
}else{
1024
- fossil_fatal("bad size read, %u out of %u bytes at %p from pid %lu",
1140
+ fossil_panic("bad size read, %u out of %u bytes at %p from pid %lu",
10251141
nRead, nSize, pAddress, processId);
10261142
}
10271143
}else{
10281144
CloseHandle(hProcess);
1029
- fossil_fatal("failed read, %u bytes at %p from pid %lu: %lu", nSize,
1145
+ fossil_panic("failed read, %u bytes at %p from pid %lu: %lu", nSize,
10301146
pAddress, processId, GetLastError());
10311147
}
10321148
}else{
1033
- fossil_fatal("failed to open pid %lu: %lu", processId, GetLastError());
1149
+ fossil_panic("failed to open pid %lu: %lu", processId, GetLastError());
10341150
}
10351151
}
10361152
#endif /* defined(_WIN32) */
10371153
#endif /* USE_SEE */
10381154
@@ -1092,10 +1208,17 @@
10921208
LOCAL sqlite3 *db_open(const char *zDbName){
10931209
int rc;
10941210
sqlite3 *db;
10951211
10961212
if( g.fSqlTrace ) fossil_trace("-- sqlite3_open: [%s]\n", zDbName);
1213
+ if( strcmp(zDbName, g.nameOfExe)==0 ){
1214
+ extern int sqlite3_appendvfs_init(
1215
+ sqlite3 *, char **, const sqlite3_api_routines *
1216
+ );
1217
+ sqlite3_appendvfs_init(0,0,0);
1218
+ g.zVfsName = "apndvfs";
1219
+ }
10971220
rc = sqlite3_open_v2(
10981221
zDbName, &db,
10991222
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
11001223
g.zVfsName
11011224
);
@@ -1170,11 +1293,11 @@
11701293
** After calling this routine, db_database_slot(zLabel) should
11711294
** return 0.
11721295
*/
11731296
void db_set_main_schemaname(sqlite3 *db, const char *zLabel){
11741297
if( sqlite3_db_config(db, SQLITE_DBCONFIG_MAINDBNAME, zLabel) ){
1175
- fossil_fatal("Fossil requires a version of SQLite that supports the "
1298
+ fossil_panic("Fossil requires a version of SQLite that supports the "
11761299
"SQLITE_DBCONFIG_MAINDBNAME interface.");
11771300
}
11781301
}
11791302
11801303
/*
@@ -1227,12 +1350,14 @@
12271350
sqlite3_wal_checkpoint(g.dbConfig, 0);
12281351
sqlite3_close(g.dbConfig);
12291352
g.dbConfig = 0;
12301353
g.zConfigDbName = 0;
12311354
}else if( g.db && 0==iSlot ){
1355
+ int rc;
12321356
sqlite3_wal_checkpoint(g.db, 0);
1233
- sqlite3_close(g.db);
1357
+ rc = sqlite3_close(g.db);
1358
+ if( g.fSqlTrace ) fossil_trace("-- db_close_config(%d)\n", rc);
12341359
g.db = 0;
12351360
g.zConfigDbName = 0;
12361361
}
12371362
}
12381363
@@ -1269,27 +1394,27 @@
12691394
}
12701395
}
12711396
}
12721397
if( zHome==0 ){
12731398
if( isOptional ) return 0;
1274
- fossil_fatal("cannot locate home directory - please set the "
1399
+ fossil_panic("cannot locate home directory - please set the "
12751400
"FOSSIL_HOME, LOCALAPPDATA, APPDATA, or HOMEPATH "
12761401
"environment variables");
12771402
}
12781403
#else
12791404
if( zHome==0 ){
12801405
zHome = fossil_getenv("HOME");
12811406
}
12821407
if( zHome==0 ){
12831408
if( isOptional ) return 0;
1284
- fossil_fatal("cannot locate home directory - please set the "
1409
+ fossil_panic("cannot locate home directory - please set the "
12851410
"FOSSIL_HOME or HOME environment variables");
12861411
}
12871412
#endif
12881413
if( file_isdir(zHome, ExtFILE)!=1 ){
12891414
if( isOptional ) return 0;
1290
- fossil_fatal("invalid home directory: %s", zHome);
1415
+ fossil_panic("invalid home directory: %s", zHome);
12911416
}
12921417
#if defined(_WIN32) || defined(__CYGWIN__)
12931418
/* . filenames give some window systems problems and many apps problems */
12941419
zDbName = mprintf("%//_fossil", zHome);
12951420
#else
@@ -1296,17 +1421,17 @@
12961421
zDbName = mprintf("%s/.fossil", zHome);
12971422
#endif
12981423
if( file_size(zDbName, ExtFILE)<1024*3 ){
12991424
if( file_access(zHome, W_OK) ){
13001425
if( isOptional ) return 0;
1301
- fossil_fatal("home directory %s must be writeable", zHome);
1426
+ fossil_panic("home directory %s must be writeable", zHome);
13021427
}
13031428
db_init_database(zDbName, zConfigSchema, (char*)0);
13041429
}
13051430
if( file_access(zDbName, W_OK) ){
13061431
if( isOptional ) return 0;
1307
- fossil_fatal("configuration file %s must be writeable", zDbName);
1432
+ fossil_panic("configuration file %s must be writeable", zDbName);
13081433
}
13091434
if( useAttach ){
13101435
db_open_or_attach(zDbName, "configdb");
13111436
g.dbConfig = 0;
13121437
}else{
@@ -1524,16 +1649,19 @@
15241649
}
15251650
}
15261651
g.zRepositoryName = mprintf("%s", zDbName);
15271652
db_open_or_attach(g.zRepositoryName, "repository");
15281653
g.repositoryOpen = 1;
1529
-#if USE_SYSTEM_SQLITE+0==1
1530
- g.maxWorkerThreads = db_get_int("max-wthreads", 0);
1531
-#endif
1654
+ sqlite3_file_control(g.db, "repository", SQLITE_FCNTL_DATA_VERSION,
1655
+ &g.iRepoDataVers);
15321656
/* Cache "allow-symlinks" option, because we'll need it on every stat call */
15331657
g.allowSymlinks = db_get_boolean("allow-symlinks",
15341658
db_allow_symlinks_by_default());
1659
+ /* Cache "max-wthreads" option */
1660
+#if USE_SYSTEM_SQLITE+0==1
1661
+ g.maxWorkerThreads = db_get_int("max-wthreads", 0);
1662
+#endif
15351663
g.zAuxSchema = db_get("aux-schema","");
15361664
g.eHashPolicy = db_get_int("hash-policy",-1);
15371665
if( g.eHashPolicy<0 ){
15381666
g.eHashPolicy = hname_default_policy();
15391667
db_set_int("hash-policy", g.eHashPolicy, 0);
@@ -1542,10 +1670,25 @@
15421670
/* Make a change to the CHECK constraint on the BLOB table for
15431671
** version 2.0 and later.
15441672
*/
15451673
rebuild_schema_update_2_0(); /* Do the Fossil-2.0 schema updates */
15461674
}
1675
+
1676
+/*
1677
+** Return true if there have been any changes to the repository
1678
+** database since it was opened.
1679
+**
1680
+** Changes to "config" and "localdb" and "temp" do not count.
1681
+** This routine only returns true if there have been changes
1682
+** to "repository".
1683
+*/
1684
+int db_repository_has_changed(void){
1685
+ unsigned int v;
1686
+ if( !g.repositoryOpen ) return 0;
1687
+ sqlite3_file_control(g.db, "repository", SQLITE_FCNTL_DATA_VERSION, &v);
1688
+ return g.iRepoDataVers != v;
1689
+}
15471690
15481691
/*
15491692
** Flags for the db_find_and_open_repository() function.
15501693
*/
15511694
#if INTERFACE
@@ -1