Fossil SCM
Merge trunk
Commit
02bc919bc0f63be563105359e7c0625c1d0007d1da2ba1fb357c14ecbc242477
Parent
b8e4dcc991a8b1c…
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
+3
| --- Makefile.classic | ||
| +++ Makefile.classic | ||
| @@ -42,10 +42,13 @@ | ||
| 42 | 42 | # TCC += -DFOSSIL_ENABLE_MINIZ |
| 43 | 43 | |
| 44 | 44 | # To add support for HTTPS |
| 45 | 45 | TCC += -DFOSSIL_ENABLE_SSL |
| 46 | 46 | |
| 47 | +# To enable legacy mv/rm support | |
| 48 | +TCC += -DFOSSIL_ENABLE_LEGACY_MV_RM=1 | |
| 49 | + | |
| 47 | 50 | #### We sometimes add the -static option here so that we can build a |
| 48 | 51 | # static executable that will run in a chroot jail. |
| 49 | 52 | #LIB = -static |
| 50 | 53 | TCC += -DFOSSIL_DYNAMIC_BUILD=1 |
| 51 | 54 | |
| 52 | 55 |
| --- 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 @@ | ||
| 36 | 36 | #### Tcl shell for use in running the fossil testsuite. If you do not |
| 37 | 37 | # care about testing the end result, this can be blank. |
| 38 | 38 | # |
| 39 | 39 | TCLSH = tclsh |
| 40 | 40 | |
| 41 | +CFLAGS = @CFLAGS@ | |
| 41 | 42 | LIB = @LDFLAGS@ @EXTRA_LDFLAGS@ @LIBS@ |
| 42 | 43 | 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 | |
| 44 | 45 | INSTALLDIR = $(DESTDIR)@prefix@/bin |
| 45 | 46 | USE_SYSTEM_SQLITE = @USE_SYSTEM_SQLITE@ |
| 46 | 47 | USE_LINENOISE = @USE_LINENOISE@ |
| 48 | +USE_MMAN_H = @USE_MMAN_H@ | |
| 47 | 49 | USE_SEE = @USE_SEE@ |
| 48 | 50 | FOSSIL_ENABLE_MINIZ = @FOSSIL_ENABLE_MINIZ@ |
| 49 | 51 | |
| 50 | 52 | include $(SRCDIR)/main.mk |
| 51 | 53 | |
| 52 | 54 | |
| 53 | 55 | 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 |
+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 |
| --- 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 |
M
VERSION
+1
-1
| --- VERSION | ||
| +++ VERSION | ||
| @@ -1,1 +1,1 @@ | ||
| 1 | -2.5 | |
| 1 | +2.6 | |
| 2 | 2 |
| --- VERSION | |
| +++ VERSION | |
| @@ -1,1 +1,1 @@ | |
| 1 | 2.5 |
| 2 |
| --- VERSION | |
| +++ VERSION | |
| @@ -1,1 +1,1 @@ | |
| 1 | 2.6 |
| 2 |
M
auto.def
+23
-6
| --- auto.def | ||
| +++ auto.def | ||
| @@ -8,17 +8,18 @@ | ||
| 8 | 8 | with-miniz=0 => {Use miniz from the source tree} |
| 9 | 9 | with-zlib:path|auto|tree |
| 10 | 10 | => {Look for zlib in the given path, automatically, or in the source tree} |
| 11 | 11 | with-exec-rel-paths=0 |
| 12 | 12 | => {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)} | |
| 14 | 14 | with-th1-docs=0 => {Enable TH1 for embedded documentation pages} |
| 15 | 15 | with-th1-hooks=0 => {Enable TH1 hooks for commands and web pages} |
| 16 | 16 | with-tcl:path => {Enable Tcl integration, with Tcl in the specified path} |
| 17 | 17 | with-tcl-stubs=0 => {Enable Tcl integration via stubs library mechanism} |
| 18 | 18 | with-tcl-private-stubs=0 |
| 19 | 19 | => {Enable Tcl integration via private stubs mechanism} |
| 20 | + with-mman=0 => {Enable use of POSIX memory APIs from "sys/mman.h"} | |
| 20 | 21 | with-see=0 => {Enable the SQLite Encryption Extension (SEE)} |
| 21 | 22 | internal-sqlite=1 => {Don't use the internal SQLite, use the system one} |
| 22 | 23 | static=0 => {Link a static executable} |
| 23 | 24 | fusefs=1 => {Disable the Fuse Filesystem} |
| 24 | 25 | fossil-debug=0 => {Build with fossil debugging enabled} |
| @@ -40,10 +41,11 @@ | ||
| 40 | 41 | define EXTRA_CFLAGS "-Wall" |
| 41 | 42 | define EXTRA_LDFLAGS "" |
| 42 | 43 | define USE_SYSTEM_SQLITE 0 |
| 43 | 44 | define USE_LINENOISE 0 |
| 44 | 45 | define FOSSIL_ENABLE_MINIZ 0 |
| 46 | +define USE_MMAN_H 0 | |
| 45 | 47 | define USE_SEE 0 |
| 46 | 48 | |
| 47 | 49 | # This procedure is a customized version of "cc-check-function-in-lib", |
| 48 | 50 | # that does not modify the LIBS variable. Its use prevents prematurely |
| 49 | 51 | # pulling in libraries that will be added later anyhow (e.g. "-ldl"). |
| @@ -89,17 +91,17 @@ | ||
| 89 | 91 | # the code below will append -ldl to LIBS. |
| 90 | 92 | # |
| 91 | 93 | foreach extralibs {{} {-ldl}} { |
| 92 | 94 | |
| 93 | 95 | # 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 | |
| 96 | 98 | # fossil. |
| 97 | 99 | # |
| 98 | 100 | 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)" | |
| 101 | 103 | } |
| 102 | 104 | |
| 103 | 105 | # Success. Update symbols and return. |
| 104 | 106 | # |
| 105 | 107 | define USE_SYSTEM_SQLITE 1 |
| @@ -141,10 +143,16 @@ | ||
| 141 | 143 | |
| 142 | 144 | if {[opt-bool no-opt]} { |
| 143 | 145 | define CFLAGS {-g -O0 -Wall} |
| 144 | 146 | msg-result "Builting without compiler optimization" |
| 145 | 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 | +} | |
| 146 | 154 | |
| 147 | 155 | if {[opt-bool with-see]} { |
| 148 | 156 | define-append EXTRA_CFLAGS -DUSE_SEE |
| 149 | 157 | define USE_SEE 1 |
| 150 | 158 | msg-result "Enabling encryption support" |
| @@ -470,13 +478,15 @@ | ||
| 470 | 478 | # Last resort, may be Windows |
| 471 | 479 | if {[is_mingw]} { |
| 472 | 480 | define-append LIBS -lwsock32 |
| 473 | 481 | } |
| 474 | 482 | } |
| 483 | +cc-check-function-in-lib ns_name_uncompress resolv | |
| 475 | 484 | cc-check-functions utime |
| 476 | 485 | cc-check-functions usleep |
| 477 | 486 | cc-check-functions strchrnul |
| 487 | +cc-check-functions pledge | |
| 478 | 488 | |
| 479 | 489 | # Check for getloadavg(), and if it doesn't exist, define FOSSIL_OMIT_LOAD_AVERAGE |
| 480 | 490 | if {![cc-check-functions getloadavg]} { |
| 481 | 491 | define FOSSIL_OMIT_LOAD_AVERAGE 1 |
| 482 | 492 | msg-result "Load average support unavailable" |
| @@ -485,11 +495,10 @@ | ||
| 485 | 495 | # Check for getpassphrase() for Solaris 10 where getpass() truncates to 10 chars |
| 486 | 496 | if {![cc-check-functions getpassphrase]} { |
| 487 | 497 | # Haiku needs this |
| 488 | 498 | cc-check-function-in-lib getpass bsd |
| 489 | 499 | } |
| 490 | -cc-check-function-in-lib dlopen dl | |
| 491 | 500 | cc-check-function-in-lib sin m |
| 492 | 501 | |
| 493 | 502 | # Check for the FuseFS library |
| 494 | 503 | if {[opt-bool fusefs]} { |
| 495 | 504 | if {[cc-check-function-in-lib fuse_mount fuse]} { |
| @@ -497,8 +506,16 @@ | ||
| 497 | 506 | define FOSSIL_HAVE_FUSEFS 1 |
| 498 | 507 | define-append LIBS -lfuse |
| 499 | 508 | msg-result "FuseFS support enabled" |
| 500 | 509 | } |
| 501 | 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 | +} | |
| 502 | 519 | |
| 503 | 520 | make-template Makefile.in |
| 504 | 521 | make-config-header autoconfig.h -auto {USE_* FOSSIL_*} |
| 505 | 522 | |
| 506 | 523 | ADDED skins/ardoise/README.md |
| 507 | 524 | ADDED skins/ardoise/css.txt |
| 508 | 525 | ADDED skins/ardoise/details.txt |
| 509 | 526 | ADDED skins/ardoise/footer.txt |
| 510 | 527 | ADDED skins/ardoise/header.txt |
| 511 | 528 | ADDED skins/ardoise/images/active.svg |
| 512 | 529 | ADDED skins/ardoise/images/addindent.svg |
| 513 | 530 | ADDED skins/ardoise/images/addindent_h.svg |
| 514 | 531 | ADDED skins/ardoise/images/blist.svg |
| 515 | 532 | ADDED skins/ardoise/images/blist_h.svg |
| 516 | 533 | ADDED skins/ardoise/images/bold.svg |
| 517 | 534 | ADDED skins/ardoise/images/bold_h.svg |
| 518 | 535 | ADDED skins/ardoise/images/calign.svg |
| 519 | 536 | ADDED skins/ardoise/images/calign_h.svg |
| 520 | 537 | ADDED skins/ardoise/images/clrfmt.svg |
| 521 | 538 | ADDED skins/ardoise/images/clrfmt_h.svg |
| 522 | 539 | ADDED skins/ardoise/images/delindent.svg |
| 523 | 540 | ADDED skins/ardoise/images/delindent_h.svg |
| 524 | 541 | ADDED skins/ardoise/images/dir.svg |
| 525 | 542 | ADDED skins/ardoise/images/file.svg |
| 526 | 543 | ADDED skins/ardoise/images/italic.svg |
| 527 | 544 | ADDED skins/ardoise/images/italic_h.svg |
| 528 | 545 | ADDED skins/ardoise/images/lalign.svg |
| 529 | 546 | ADDED skins/ardoise/images/lalign_h.svg |
| 530 | 547 | ADDED skins/ardoise/images/link.svg |
| 531 | 548 | ADDED skins/ardoise/images/link_h.svg |
| 532 | 549 | ADDED skins/ardoise/images/nlist.svg |
| 533 | 550 | ADDED skins/ardoise/images/nlist_h.svg |
| 534 | 551 | ADDED skins/ardoise/images/quote.svg |
| 535 | 552 | ADDED skins/ardoise/images/quote_h.svg |
| 536 | 553 | ADDED skins/ardoise/images/ralign.svg |
| 537 | 554 | ADDED skins/ardoise/images/ralign_h.svg |
| 538 | 555 | ADDED skins/ardoise/images/redo.svg |
| 539 | 556 | ADDED skins/ardoise/images/redo_h.svg |
| 540 | 557 | ADDED skins/ardoise/images/underline.svg |
| 541 | 558 | ADDED skins/ardoise/images/underline_h.svg |
| 542 | 559 | ADDED skins/ardoise/images/undo.svg |
| 543 | 560 | 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 |
+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/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). |
+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/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 |
+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/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> |
+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> $<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> $<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> |
+1
-1
| --- skins/default/css.txt | ||
| +++ skins/default/css.txt | ||
| @@ -42,11 +42,11 @@ | ||
| 42 | 42 | font-size: 1.25em; |
| 43 | 43 | } |
| 44 | 44 | .content h2 { |
| 45 | 45 | font-size: 1.15em; |
| 46 | 46 | } |
| 47 | -.content h2 { | |
| 47 | +.content h3 { | |
| 48 | 48 | font-size: 1.05em; |
| 49 | 49 | font-weight: bold; |
| 50 | 50 | } |
| 51 | 51 | |
| 52 | 52 | .section { |
| 53 | 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 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 @@ | ||
| 707 | 707 | |
| 708 | 708 | /************************************** |
| 709 | 709 | * Timeline |
| 710 | 710 | */ |
| 711 | 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 | 712 | /* The suppressed duplicates lines in timeline, .. */ |
| 721 | -span.timelineDisabled { | |
| 722 | - font-size: 0.5rem; | |
| 713 | +.timelineDisabled { | |
| 714 | + font-size: 0.5rem; | |
| 723 | 715 | font-style: italic; |
| 724 | 716 | } |
| 725 | 717 | |
| 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; | |
| 760 | 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; | |
| 761 | 818 | } |
| 762 | 819 | |
| 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; | |
| 765 | 827 | } |
| 766 | 828 | |
| 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 { | |
| 771 | 835 | } |
| 772 | 836 | |
| 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 { | |
| 775 | 841 | font-weight: bold; |
| 776 | 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 | +} | |
| 777 | 867 | |
| 778 | 868 | /* the format for the timeline version links */ |
| 779 | 869 | a.timelineHistLink { |
| 780 | 870 | } |
| 781 | 871 | |
| 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 | 872 | |
| 802 | 873 | /************************************** |
| 803 | 874 | * User Edit |
| 804 | 875 | */ |
| 805 | 876 | |
| 806 | 877 |
| --- 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 |
+1
-1
| --- skins/xekri/details.txt | ||
| +++ skins/xekri/details.txt | ||
| @@ -1,4 +1,4 @@ | ||
| 1 | 1 | timeline-arrowheads: 1 |
| 2 | 2 | timeline-circle-nodes: 0 |
| 3 | -timeline-color-graph-lines: 0 | |
| 3 | +timeline-color-graph-lines: 1 | |
| 4 | 4 | white-foreground: 0 |
| 5 | 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: 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 @@ | ||
| 253 | 253 | ** |
| 254 | 254 | ** When adding files or directories recursively, filenames that begin |
| 255 | 255 | ** with "." are excluded by default. To include such files, add |
| 256 | 256 | ** the "--dotfiles" option to the command-line. |
| 257 | 257 | ** |
| 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 | |
| 259 | 259 | ** for files to be excluded. Example: '*.o,*.obj,*.exe' If the --ignore |
| 260 | 260 | ** option does not appear on the command line then the "ignore-glob" setting |
| 261 | 261 | ** is used. If the --clean option does not appear on the command line then |
| 262 | 262 | ** the "clean-glob" setting is used. |
| 263 | 263 | ** |
| @@ -859,10 +859,12 @@ | ||
| 859 | 859 | int vid; |
| 860 | 860 | int moveFiles; |
| 861 | 861 | int dryRunFlag; |
| 862 | 862 | int softFlag; |
| 863 | 863 | int hardFlag; |
| 864 | + int origType; | |
| 865 | + int destType; | |
| 864 | 866 | char *zDest; |
| 865 | 867 | Blob dest; |
| 866 | 868 | Stmt q; |
| 867 | 869 | |
| 868 | 870 | db_must_be_within_tree(); |
| @@ -873,11 +875,11 @@ | ||
| 873 | 875 | /* We should be done with options.. */ |
| 874 | 876 | verify_all_options(); |
| 875 | 877 | |
| 876 | 878 | vid = db_lget_int("checkout", 0); |
| 877 | 879 | if( vid==0 ){ |
| 878 | - fossil_fatal("no checkout rename files in"); | |
| 880 | + fossil_fatal("no checkout in which to rename files"); | |
| 879 | 881 | } |
| 880 | 882 | if( g.argc<4 ){ |
| 881 | 883 | usage("OLDNAME NEWNAME"); |
| 882 | 884 | } |
| 883 | 885 | zDest = g.argv[g.argc-1]; |
| @@ -900,15 +902,23 @@ | ||
| 900 | 902 | "UPDATE vfile SET origname=pathname WHERE origname IS NULL;" |
| 901 | 903 | ); |
| 902 | 904 | db_multi_exec( |
| 903 | 905 | "CREATE TEMP TABLE mv(f TEXT UNIQUE ON CONFLICT IGNORE, t TEXT);" |
| 904 | 906 | ); |
| 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 ){ | |
| 906 | 919 | Blob orig; |
| 907 | - if( g.argc!=4 ){ | |
| 908 | - usage("OLDNAME NEWNAME"); | |
| 909 | - } | |
| 910 | 920 | file_tree_name(g.argv[2], &orig, 0, 1); |
| 911 | 921 | db_multi_exec( |
| 912 | 922 | "INSERT INTO mv VALUES(%B,%B)", &orig, &dest |
| 913 | 923 | ); |
| 914 | 924 | }else{ |
| @@ -936,10 +946,12 @@ | ||
| 936 | 946 | const char *zPath = db_column_text(&q, 0); |
| 937 | 947 | int nPath = db_column_bytes(&q, 0); |
| 938 | 948 | const char *zTail; |
| 939 | 949 | if( nPath==nOrig ){ |
| 940 | 950 | zTail = file_tail(zPath); |
| 951 | + }else if( destType==1 ){ | |
| 952 | + zTail = &zPath[nOrig-strlen(file_tail(zOrig))]; | |
| 941 | 953 | }else{ |
| 942 | 954 | zTail = &zPath[nOrig+1]; |
| 943 | 955 | } |
| 944 | 956 | db_multi_exec( |
| 945 | 957 | "INSERT INTO mv VALUES('%q','%q%q')", |
| 946 | 958 |
| --- 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 @@ | ||
| 377 | 377 | } |
| 378 | 378 | if( zFrom==0 ) zFrom = mprintf("%s/home", g.zTop); |
| 379 | 379 | if( P("cancel") ){ |
| 380 | 380 | cgi_redirect(zFrom); |
| 381 | 381 | } |
| 382 | - if( P("ok") && szContent>0 && (goodCaptcha = captcha_is_correct()) ){ | |
| 382 | + if( P("ok") && szContent>0 && (goodCaptcha = captcha_is_correct(0)) ){ | |
| 383 | 383 | int needModerator = (zTkt!=0 && ticket_need_moderation(0)) || |
| 384 | 384 | (zPage!=0 && wiki_need_moderation(0)); |
| 385 | 385 | const char *zComment = PD("comment", ""); |
| 386 | 386 | attach_commit(zName, zTarget, aContent, szContent, needModerator, zComment); |
| 387 | 387 | cgi_redirect(zFrom); |
| 388 | 388 | |
| 389 | 389 | 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 |
+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> </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> </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> </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> </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> </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> </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> </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> </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> </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> </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> </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> </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> </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> </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> </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> </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 @@ | ||
| 115 | 115 | } |
| 116 | 116 | int fossil_isalnum(char c){ |
| 117 | 117 | return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9'); |
| 118 | 118 | } |
| 119 | 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 | + | |
| 120 | 128 | |
| 121 | 129 | /* |
| 122 | 130 | ** COMMAND: test-isspace |
| 123 | 131 | ** |
| 124 | 132 | ** Verify that the fossil_isspace() routine is working correctly by |
| @@ -285,10 +293,23 @@ | ||
| 285 | 293 | } |
| 286 | 294 | memcpy(&pBlob->aData[pBlob->nUsed], aData, nData); |
| 287 | 295 | pBlob->nUsed += nData; |
| 288 | 296 | pBlob->aData[pBlob->nUsed] = 0; /* Blobs are always nul-terminated */ |
| 289 | 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 | +} | |
| 290 | 311 | |
| 291 | 312 | /* |
| 292 | 313 | ** Copy a blob |
| 293 | 314 | */ |
| 294 | 315 | void blob_copy(Blob *pTo, Blob *pFrom){ |
| @@ -301,11 +322,11 @@ | ||
| 301 | 322 | ** Return a pointer to a null-terminated string for a blob. |
| 302 | 323 | */ |
| 303 | 324 | char *blob_str(Blob *p){ |
| 304 | 325 | blob_is_init(p); |
| 305 | 326 | if( p->nUsed==0 ){ |
| 306 | - blob_append(p, "", 1); /* NOTE: Changes nUsed. */ | |
| 327 | + blob_append_char(p, 0); /* NOTE: Changes nUsed. */ | |
| 307 | 328 | p->nUsed = 0; |
| 308 | 329 | } |
| 309 | 330 | if( p->aData[p->nUsed]!=0 ){ |
| 310 | 331 | blob_materialize(p); |
| 311 | 332 | } |
| @@ -318,11 +339,11 @@ | ||
| 318 | 339 | ** text was ever added using blob_appendf() then throw an error. |
| 319 | 340 | */ |
| 320 | 341 | char *blob_sql_text(Blob *p){ |
| 321 | 342 | blob_is_init(p); |
| 322 | 343 | 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"); | |
| 324 | 345 | } |
| 325 | 346 | return blob_str(p); |
| 326 | 347 | } |
| 327 | 348 | |
| 328 | 349 | |
| @@ -472,10 +493,17 @@ | ||
| 472 | 493 | ** Rewind the cursor on a blob back to the beginning. |
| 473 | 494 | */ |
| 474 | 495 | void blob_rewind(Blob *p){ |
| 475 | 496 | p->iCursor = 0; |
| 476 | 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 | +} | |
| 477 | 505 | |
| 478 | 506 | /* |
| 479 | 507 | ** Seek the cursor in a blob to the indicated offset. |
| 480 | 508 | */ |
| 481 | 509 | int blob_seek(Blob *p, int offset, int whence){ |
| @@ -645,10 +673,20 @@ | ||
| 645 | 673 | if( pTo ){ |
| 646 | 674 | blob_append(pTo, &pFrom->aData[pFrom->iCursor], i - pFrom->iCursor); |
| 647 | 675 | } |
| 648 | 676 | pFrom->iCursor = i; |
| 649 | 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 | +} | |
| 650 | 688 | |
| 651 | 689 | /* |
| 652 | 690 | ** Return true if the blob contains a valid base16 identifier artifact hash. |
| 653 | 691 | ** |
| 654 | 692 | ** The value returned is actually one of HNAME_SHA1 OR HNAME_K256 if the |
| @@ -1229,16 +1267,16 @@ | ||
| 1229 | 1267 | if( !needEscape && !fossil_isalnum(c) && c!='/' && c!='.' && c!='_' ){ |
| 1230 | 1268 | needEscape = 1; |
| 1231 | 1269 | } |
| 1232 | 1270 | } |
| 1233 | 1271 | if( n>0 && !fossil_isspace(z[n-1]) ){ |
| 1234 | - blob_append(pBlob, " ", 1); | |
| 1272 | + blob_append_char(pBlob, ' '); | |
| 1235 | 1273 | } |
| 1236 | - if( needEscape ) blob_append(pBlob, &cQuote, 1); | |
| 1274 | + if( needEscape ) blob_append_char(pBlob, cQuote); | |
| 1237 | 1275 | if( zIn[0]=='-' ) blob_append(pBlob, "./", 2); |
| 1238 | 1276 | blob_append(pBlob, zIn, -1); |
| 1239 | - if( needEscape ) blob_append(pBlob, &cQuote, 1); | |
| 1277 | + if( needEscape ) blob_append_char(pBlob, cQuote); | |
| 1240 | 1278 | } |
| 1241 | 1279 | |
| 1242 | 1280 | /* |
| 1243 | 1281 | ** A read(2)-like impl for the Blob class. Reads (copies) up to nLen |
| 1244 | 1282 | ** bytes from pIn, starting at position pIn->iCursor, and copies them |
| @@ -1305,11 +1343,11 @@ | ||
| 1305 | 1343 | zUtf8[i] = zUtf8[i-1]; |
| 1306 | 1344 | zUtf8[--i] = zTemp; |
| 1307 | 1345 | } |
| 1308 | 1346 | } |
| 1309 | 1347 | /* Make sure the blob contains two terminating 0-bytes */ |
| 1310 | - blob_append(pBlob, "", 1); | |
| 1348 | + blob_append_char(pBlob, 0); | |
| 1311 | 1349 | zUtf8 = blob_str(pBlob) + bomSize; |
| 1312 | 1350 | zUtf8 = fossil_unicode_to_utf8(zUtf8); |
| 1313 | 1351 | blob_set_dynamic(pBlob, zUtf8); |
| 1314 | 1352 | }else if( useMbcs && invalid_utf8(pBlob) ){ |
| 1315 | 1353 | #if defined(_WIN32) || defined(__CYGWIN__) |
| 1316 | 1354 |
| --- 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 @@ | ||
| 197 | 197 | if( nD==0 ){ |
| 198 | 198 | style_submenu_element("File Ages", "%R/fileage?name=%!S", zUuid); |
| 199 | 199 | } |
| 200 | 200 | }else{ |
| 201 | 201 | @ <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 | + @ %z(href("%R/timeline?chng=%T/*", zD))[history]</a> | |
| 205 | + } | |
| 206 | + @ </h2> | |
| 203 | 207 | zSubdirLink = mprintf("%R/dir?name=%T", zPrefix); |
| 204 | 208 | } |
| 205 | 209 | style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0)); |
| 206 | 210 | style_submenu_element("Tree-View", "%s", |
| 207 | 211 | url_render(&sURI, "type", "tree", 0, 0)); |
| 208 | 212 |
| --- 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 | @ %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 @@ | ||
| 60 | 60 | if( !doInit && file_size(zFile, ExtFILE)<0 ){ |
| 61 | 61 | fossil_fatal("no such file: %s", zFile); |
| 62 | 62 | } |
| 63 | 63 | assert( g.db ); |
| 64 | 64 | 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"); | |
| 66 | 66 | rc = sqlite3_exec(g.db, zSql, 0, 0, &zErrMsg); |
| 67 | 67 | sqlite3_free(zSql); |
| 68 | 68 | if( rc!=SQLITE_OK || zErrMsg ){ |
| 69 | 69 | if( zErrMsg==0 ) zErrMsg = (char*)sqlite3_errmsg(g.db); |
| 70 | 70 | fossil_fatal("not a valid bundle: %s", zFile); |
| @@ -73,18 +73,18 @@ | ||
| 73 | 73 | db_multi_exec(zBundleInit /*works-like:"%w%w"*/, zBName, zBName); |
| 74 | 74 | }else{ |
| 75 | 75 | sqlite3_stmt *pStmt; |
| 76 | 76 | zSql = sqlite3_mprintf("SELECT bcname, bcvalue" |
| 77 | 77 | " FROM \"%w\".bconfig", zBName); |
| 78 | - if( zSql==0 ) fossil_fatal("out of memory"); | |
| 78 | + if( zSql==0 ) fossil_panic("out of memory"); | |
| 79 | 79 | rc = sqlite3_prepare(g.db, zSql, -1, &pStmt, 0); |
| 80 | 80 | if( rc ) fossil_fatal("not a valid bundle: %s", zFile); |
| 81 | 81 | sqlite3_free(zSql); |
| 82 | 82 | sqlite3_finalize(pStmt); |
| 83 | 83 | zSql = sqlite3_mprintf("SELECT blobid, uuid, sz, delta, notes, data" |
| 84 | 84 | " FROM \"%w\".bblob", zBName); |
| 85 | - if( zSql==0 ) fossil_fatal("out of memory"); | |
| 85 | + if( zSql==0 ) fossil_panic("out of memory"); | |
| 86 | 86 | rc = sqlite3_prepare(g.db, zSql, -1, &pStmt, 0); |
| 87 | 87 | if( rc ) fossil_fatal("not a valid bundle: %s", zFile); |
| 88 | 88 | sqlite3_free(zSql); |
| 89 | 89 | sqlite3_finalize(pStmt); |
| 90 | 90 | } |
| 91 | 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_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 @@ | ||
| 61 | 61 | fossil_free(zDbName); |
| 62 | 62 | if( rc ){ |
| 63 | 63 | sqlite3_close(db); |
| 64 | 64 | return 0; |
| 65 | 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; | |
| 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 | + } | |
| 83 | 87 | } |
| 84 | 88 | return db; |
| 85 | 89 | } |
| 86 | 90 | |
| 87 | 91 | /* |
| @@ -165,17 +169,25 @@ | ||
| 165 | 169 | sqlite3_bind_int(pStmt, 3, sqlite3_last_insert_rowid(db)); |
| 166 | 170 | if( sqlite3_step(pStmt)!=SQLITE_DONE) goto cache_write_end; |
| 167 | 171 | rc = sqlite3_changes(db); |
| 168 | 172 | |
| 169 | 173 | /* 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 | + */ | |
| 171 | 182 | if( rc ){ |
| 172 | 183 | nKeep = db_get_int("max-cache-entry",10); |
| 173 | 184 | sqlite3_finalize(pStmt); |
| 174 | 185 | pStmt = cacheStmt(db, |
| 175 | 186 | "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" | |
| 177 | 189 | " LIMIT -1 OFFSET ?1)"); |
| 178 | 190 | if( pStmt ){ |
| 179 | 191 | sqlite3_bind_int(pStmt, 1, nKeep); |
| 180 | 192 | sqlite3_step(pStmt); |
| 181 | 193 | } |
| @@ -291,11 +303,12 @@ | ||
| 291 | 303 | sqlite3_close(db); |
| 292 | 304 | fossil_print("cache cleared\n"); |
| 293 | 305 | }else{ |
| 294 | 306 | fossil_print("nothing to clear; cache does not exist\n"); |
| 295 | 307 | } |
| 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 )){ | |
| 297 | 310 | db = cacheOpen(0); |
| 298 | 311 | if( db==0 ){ |
| 299 | 312 | fossil_print("cache does not exist\n"); |
| 300 | 313 | }else{ |
| 301 | 314 | int nEntry = 0; |
| @@ -350,11 +363,11 @@ | ||
| 350 | 363 | char *zDbName = cacheName(); |
| 351 | 364 | cache_register_sizename(db); |
| 352 | 365 | pStmt = cacheStmt(db, |
| 353 | 366 | "SELECT key, sizename(sz), nRef, datetime(tm,'unixepoch')" |
| 354 | 367 | " FROM cache" |
| 355 | - " ORDER BY tm DESC" | |
| 368 | + " ORDER BY (tm + 3600*min(nRef,48)) DESC" | |
| 356 | 369 | ); |
| 357 | 370 | if( pStmt ){ |
| 358 | 371 | @ <ol> |
| 359 | 372 | while( sqlite3_step(pStmt)==SQLITE_ROW ){ |
| 360 | 373 | const unsigned char *zName = sqlite3_column_text(pStmt,0); |
| 361 | 374 |
| --- 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 @@ | ||
| 497 | 497 | ** true (non-zero). |
| 498 | 498 | ** |
| 499 | 499 | ** The query parameters examined are "captchaseed" for the seed value and |
| 500 | 500 | ** "captcha" for text that the user types in response to the captcha prompt. |
| 501 | 501 | */ |
| 502 | -int captcha_is_correct(void){ | |
| 502 | +int captcha_is_correct(int bAlwaysNeeded){ | |
| 503 | 503 | const char *zSeed; |
| 504 | 504 | const char *zEntered; |
| 505 | 505 | const char *zDecode; |
| 506 | 506 | char z[30]; |
| 507 | 507 | int i; |
| 508 | - if( !captcha_needed() ){ | |
| 508 | + if( !bAlwaysNeeded && !captcha_needed() ){ | |
| 509 | 509 | return 1; /* No captcha needed */ |
| 510 | 510 | } |
| 511 | 511 | zSeed = P("captchaseed"); |
| 512 | 512 | if( zSeed==0 ) return 0; |
| 513 | 513 | zEntered = P("captcha"); |
| @@ -593,11 +593,11 @@ | ||
| 593 | 593 | } |
| 594 | 594 | #endif |
| 595 | 595 | zCookieName = mprintf("fossil-cc-%.10s", db_get("project-code","x")); |
| 596 | 596 | zCookieValue = P(zCookieName); |
| 597 | 597 | if( zCookieValue && atoi(zCookieValue)==1 ) return 0; |
| 598 | - if( captcha_is_correct() ){ | |
| 598 | + if( captcha_is_correct(0) ){ | |
| 599 | 599 | cgi_set_cookie(zCookieName, "1", login_cookie_path(), 8*3600); |
| 600 | 600 | return 0; |
| 601 | 601 | } |
| 602 | 602 | |
| 603 | 603 | /* This appears to be a spider. Offer the captcha */ |
| 604 | 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(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 @@ | ||
| 56 | 56 | #define P(x) cgi_parameter((x),0) |
| 57 | 57 | #define PD(x,y) cgi_parameter((x),(y)) |
| 58 | 58 | #define PT(x) cgi_parameter_trimmed((x),0) |
| 59 | 59 | #define PDT(x,y) cgi_parameter_trimmed((x),(y)) |
| 60 | 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) | |
| 61 | 63 | |
| 62 | 64 | |
| 63 | 65 | /* |
| 64 | 66 | ** Destinations for output text. |
| 65 | 67 | */ |
| @@ -198,97 +200,47 @@ | ||
| 198 | 200 | void cgi_append_header(const char *zLine){ |
| 199 | 201 | blob_append(&extraHeader, zLine, -1); |
| 200 | 202 | } |
| 201 | 203 | |
| 202 | 204 | /* |
| 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. | |
| 204 | 207 | ** |
| 205 | 208 | ** Zero lifetime implies a session cookie. |
| 206 | 209 | */ |
| 207 | 210 | void cgi_set_cookie( |
| 208 | 211 | const char *zName, /* Name of the cookie */ |
| 209 | 212 | const char *zValue, /* Value of the cookie. Automatically escaped */ |
| 210 | 213 | const char *zPath, /* Path cookie applies to. NULL means "/" */ |
| 211 | 214 | int lifetime /* Expiration of the cookie in seconds from now */ |
| 212 | 215 | ){ |
| 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 ){ | |
| 215 | 219 | zPath = g.zTop; |
| 216 | 220 | if( zPath[0]==0 ) zPath = "/"; |
| 217 | 221 | } |
| 218 | 222 | if( g.zBaseURL!=0 && strncmp(g.zBaseURL, "https:", 6)==0 ){ |
| 219 | 223 | zSecure = " secure;"; |
| 220 | 224 | } |
| 221 | 225 | if( lifetime>0 ){ |
| 222 | - lifetime += (int)time(0); | |
| 223 | 226 | 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); | |
| 226 | 229 | }else{ |
| 227 | 230 | blob_appendf(&extraHeader, |
| 228 | 231 | "Set-Cookie: %s=%t; Path=%s; HttpOnly;%s Version=1\r\n", |
| 229 | 232 | zName, zValue, zPath, zSecure); |
| 230 | 233 | } |
| 231 | 234 | } |
| 232 | 235 | |
| 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 | 236 | |
| 286 | 237 | /* |
| 287 | 238 | ** Return true if the response should be sent with Content-Encoding: gzip. |
| 288 | 239 | */ |
| 289 | 240 | static int is_gzippable(void){ |
| 241 | + if( g.fNoHttpCompress ) return 0; | |
| 290 | 242 | if( strstr(PD("HTTP_ACCEPT_ENCODING", ""), "gzip")==0 ) return 0; |
| 291 | 243 | return strncmp(zContentType, "text/", 5)==0 |
| 292 | 244 | || sqlite3_strglob("application/*xml", zContentType)==0 |
| 293 | 245 | || sqlite3_strglob("application/*javascript", zContentType)==0; |
| 294 | 246 | } |
| @@ -301,29 +253,32 @@ | ||
| 301 | 253 | if( iReplyStatus<=0 ){ |
| 302 | 254 | iReplyStatus = 200; |
| 303 | 255 | zReplyStatus = "OK"; |
| 304 | 256 | } |
| 305 | 257 | |
| 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 | 258 | if( g.fullHttpReply ){ |
| 318 | 259 | fprintf(g.httpOut, "HTTP/1.0 %d %s\r\n", iReplyStatus, zReplyStatus); |
| 319 | 260 | fprintf(g.httpOut, "Date: %s\r\n", cgi_rfc822_datestamp(time(0))); |
| 320 | 261 | fprintf(g.httpOut, "Connection: close\r\n"); |
| 321 | 262 | fprintf(g.httpOut, "X-UA-Compatible: IE=edge\r\n"); |
| 322 | 263 | }else{ |
| 323 | 264 | fprintf(g.httpOut, "Status: %d %s\r\n", iReplyStatus, zReplyStatus); |
| 324 | 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 | + } | |
| 325 | 280 | |
| 326 | 281 | if( blob_size(&extraHeader)>0 ){ |
| 327 | 282 | fprintf(g.httpOut, "%s", blob_buffer(&extraHeader)); |
| 328 | 283 | } |
| 329 | 284 | |
| @@ -343,24 +298,10 @@ | ||
| 343 | 298 | ** |
| 344 | 299 | ** These headers are probably best added by the web server hosting fossil as |
| 345 | 300 | ** a CGI script. |
| 346 | 301 | */ |
| 347 | 302 | |
| 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 | 303 | /* Content intended for logged in users should only be cached in |
| 363 | 304 | ** the browser, not some shared location. |
| 364 | 305 | */ |
| 365 | 306 | fprintf(g.httpOut, "Content-Type: %s; charset=utf-8\r\n", zContentType); |
| 366 | 307 | if( fossil_strcmp(zContentType,"application/x-fossil")==0 ){ |
| @@ -398,10 +339,18 @@ | ||
| 398 | 339 | } |
| 399 | 340 | } |
| 400 | 341 | } |
| 401 | 342 | fflush(g.httpOut); |
| 402 | 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 | + } | |
| 403 | 352 | } |
| 404 | 353 | |
| 405 | 354 | /* |
| 406 | 355 | ** Do a redirect request to the URL given in the argument. |
| 407 | 356 | ** |
| @@ -456,10 +405,33 @@ | ||
| 456 | 405 | zRef = P("HTTP_REFERER"); |
| 457 | 406 | if( zRef==0 ) zRef = zDefault; |
| 458 | 407 | } |
| 459 | 408 | return zRef; |
| 460 | 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 | +} | |
| 461 | 433 | |
| 462 | 434 | /* |
| 463 | 435 | ** Information about all query parameters and cookies are stored |
| 464 | 436 | ** in these variables. |
| 465 | 437 | */ |
| @@ -596,10 +568,15 @@ | ||
| 596 | 568 | ** are ignored. |
| 597 | 569 | ** |
| 598 | 570 | ** * it is impossible for a cookie or query parameter to |
| 599 | 571 | ** override the value of an environment variable since |
| 600 | 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. | |
| 601 | 578 | ** |
| 602 | 579 | ** Parameters are separated by the "terminator" character. Whitespace |
| 603 | 580 | ** before the NAME is ignored. |
| 604 | 581 | ** |
| 605 | 582 | ** The input string "z" is modified but no copies is made. "z" |
| @@ -626,11 +603,11 @@ | ||
| 626 | 603 | dehttpize(zValue); |
| 627 | 604 | }else{ |
| 628 | 605 | if( *z ){ *z++ = 0; } |
| 629 | 606 | zValue = ""; |
| 630 | 607 | } |
| 631 | - if( fossil_islower(zName[0]) ){ | |
| 608 | + if( fossil_islower(zName[0]) && fossil_no_strange_characters(zName+1) ){ | |
| 632 | 609 | cgi_set_parameter_nocopy(zName, zValue, isQP); |
| 633 | 610 | } |
| 634 | 611 | #ifdef FOSSIL_ENABLE_JSON |
| 635 | 612 | json_setenv( zName, cson_value_new_string(zValue,strlen(zValue)) ); |
| 636 | 613 | #endif /* FOSSIL_ENABLE_JSON */ |
| @@ -912,13 +889,17 @@ | ||
| 912 | 889 | pLog = 0; |
| 913 | 890 | return; |
| 914 | 891 | } |
| 915 | 892 | if( pLog==0 ){ |
| 916 | 893 | char zFile[50]; |
| 894 | +#if defined(_WIN32) | |
| 917 | 895 | unsigned r; |
| 918 | 896 | sqlite3_randomness(sizeof(r), &r); |
| 919 | 897 | sqlite3_snprintf(sizeof(zFile), zFile, "httplog-%08x.txt", r); |
| 898 | +#else | |
| 899 | + sqlite3_snprintf(sizeof(zFile), zFile, "httplog-%05d.txt", getpid()); | |
| 900 | +#endif | |
| 920 | 901 | pLog = fossil_fopen(zFile, "wb"); |
| 921 | 902 | if( pLog ){ |
| 922 | 903 | fprintf(stderr, "# open log on %s\n", zFile); |
| 923 | 904 | }else{ |
| 924 | 905 | fprintf(stderr, "# failed to open %s\n", zFile); |
| @@ -1137,10 +1118,11 @@ | ||
| 1137 | 1118 | const char *zIn; |
| 1138 | 1119 | char *zOut; |
| 1139 | 1120 | int i; |
| 1140 | 1121 | zIn = cgi_parameter(zName, 0); |
| 1141 | 1122 | if( zIn==0 ) zIn = zDefault; |
| 1123 | + if( zIn==0 ) return 0; | |
| 1142 | 1124 | while( fossil_isspace(zIn[0]) ) zIn++; |
| 1143 | 1125 | zOut = fossil_strdup(zIn); |
| 1144 | 1126 | for(i=0; zOut[i]; i++){} |
| 1145 | 1127 | while( i>0 && fossil_isspace(zOut[i-1]) ) zOut[--i] = 0; |
| 1146 | 1128 | return zOut; |
| @@ -1153,10 +1135,33 @@ | ||
| 1153 | 1135 | int cgi_parameter_boolean(const char *zName){ |
| 1154 | 1136 | const char *zIn = cgi_parameter(zName, 0); |
| 1155 | 1137 | if( zIn==0 ) return 0; |
| 1156 | 1138 | return zIn[0]==0 || is_truth(zIn); |
| 1157 | 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 | +} | |
| 1158 | 1163 | |
| 1159 | 1164 | /* |
| 1160 | 1165 | ** Return the name of the i-th CGI parameter. Return NULL if there |
| 1161 | 1166 | ** are fewer than i registered CGI parameters. |
| 1162 | 1167 | */ |
| @@ -1217,20 +1222,24 @@ | ||
| 1217 | 1222 | ** Print all query parameters on standard output. Format the |
| 1218 | 1223 | ** parameters as HTML. This is used for testing and debugging. |
| 1219 | 1224 | ** |
| 1220 | 1225 | ** Omit the values of the cookies unless showAll is true. |
| 1221 | 1226 | */ |
| 1222 | -void cgi_print_all(int showAll){ | |
| 1227 | +void cgi_print_all(int showAll, int onConsole){ | |
| 1223 | 1228 | int i; |
| 1224 | 1229 | cgi_parameter("",""); /* Force the parameters into sorted order */ |
| 1225 | 1230 | for(i=0; i<nUsedQP; i++){ |
| 1226 | 1231 | const char *zName = aParamQP[i].zName; |
| 1227 | 1232 | if( !showAll ){ |
| 1228 | 1233 | if( fossil_stricmp("HTTP_COOKIE",zName)==0 ) continue; |
| 1229 | 1234 | if( fossil_strnicmp("fossil-",zName,7)==0 ) continue; |
| 1230 | 1235 | } |
| 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 | + } | |
| 1232 | 1241 | } |
| 1233 | 1242 | } |
| 1234 | 1243 | |
| 1235 | 1244 | /* |
| 1236 | 1245 | ** Export all untagged query parameters (but not cookies or environment |
| @@ -1343,12 +1352,14 @@ | ||
| 1343 | 1352 | ** NULL pointer to stick with the IP address previously computed and |
| 1344 | 1353 | ** loaded into g.zIpAddr. |
| 1345 | 1354 | */ |
| 1346 | 1355 | static const char *cgi_accept_forwarded_for(const char *z){ |
| 1347 | 1356 | 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 | + } | |
| 1350 | 1361 | i = strlen(z)-1; |
| 1351 | 1362 | while( i>=0 && z[i]!=',' && !fossil_isspace(z[i]) ) i--; |
| 1352 | 1363 | return &z[++i]; |
| 1353 | 1364 | } |
| 1354 | 1365 | |
| @@ -1372,10 +1383,36 @@ | ||
| 1372 | 1383 | while( fossil_isspace(*zInput) ){ zInput++; } |
| 1373 | 1384 | } |
| 1374 | 1385 | if( zLeftOver ){ *zLeftOver = zInput; } |
| 1375 | 1386 | return zResult; |
| 1376 | 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 | +} | |
| 1377 | 1414 | |
| 1378 | 1415 | /* |
| 1379 | 1416 | ** This routine handles a single HTTP request which is coming in on |
| 1380 | 1417 | ** g.httpIn and which replies on g.httpOut |
| 1381 | 1418 | ** |
| @@ -1386,12 +1423,10 @@ | ||
| 1386 | 1423 | ** and subsequent code handles the actual generation of the webpage. |
| 1387 | 1424 | */ |
| 1388 | 1425 | void cgi_handle_http_request(const char *zIpAddr){ |
| 1389 | 1426 | char *z, *zToken; |
| 1390 | 1427 | int i; |
| 1391 | - struct sockaddr_in remoteName; | |
| 1392 | - socklen_t size = sizeof(struct sockaddr_in); | |
| 1393 | 1428 | char zLine[2000]; /* A single line of input. */ |
| 1394 | 1429 | g.fullHttpReply = 1; |
| 1395 | 1430 | if( fgets(zLine, sizeof(zLine),g.httpIn)==0 ){ |
| 1396 | 1431 | malformed_request("missing HTTP header"); |
| 1397 | 1432 | } |
| @@ -1415,15 +1450,12 @@ | ||
| 1415 | 1450 | cgi_setenv("SCRIPT_NAME", ""); |
| 1416 | 1451 | for(i=0; zToken[i] && zToken[i]!='?'; i++){} |
| 1417 | 1452 | if( zToken[i] ) zToken[i++] = 0; |
| 1418 | 1453 | cgi_setenv("PATH_INFO", zToken); |
| 1419 | 1454 | 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)); | |
| 1425 | 1457 | } |
| 1426 | 1458 | if( zIpAddr ){ |
| 1427 | 1459 | cgi_setenv("REMOTE_ADDR", zIpAddr); |
| 1428 | 1460 | g.zIpAddr = mprintf("%s", zIpAddr); |
| 1429 | 1461 | } |
| @@ -1782,10 +1814,11 @@ | ||
| 1782 | 1814 | /* Use win32_http_server() instead */ |
| 1783 | 1815 | fossil_exit(1); |
| 1784 | 1816 | #else |
| 1785 | 1817 | int listener = -1; /* The server socket */ |
| 1786 | 1818 | int connection; /* A socket for each individual connection */ |
| 1819 | + int nRequest = 0; /* Number of requests handled so far */ | |
| 1787 | 1820 | fd_set readfds; /* Set of file descriptors for select() */ |
| 1788 | 1821 | socklen_t lenaddr; /* Length of the inaddr structure */ |
| 1789 | 1822 | int child; /* PID of the child process */ |
| 1790 | 1823 | int nchildren = 0; /* Number of child processes */ |
| 1791 | 1824 | struct timeval delay; /* How long to wait inside select() */ |
| @@ -1869,27 +1902,31 @@ | ||
| 1869 | 1902 | lenaddr = sizeof(inaddr); |
| 1870 | 1903 | connection = accept(listener, (struct sockaddr*)&inaddr, &lenaddr); |
| 1871 | 1904 | if( connection>=0 ){ |
| 1872 | 1905 | child = fork(); |
| 1873 | 1906 | if( child!=0 ){ |
| 1874 | - if( child>0 ) nchildren++; | |
| 1907 | + if( child>0 ){ | |
| 1908 | + nchildren++; | |
| 1909 | + nRequest++; | |
| 1910 | + } | |
| 1875 | 1911 | close(connection); |
| 1876 | 1912 | }else{ |
| 1877 | 1913 | int nErr = 0, fd; |
| 1878 | 1914 | close(0); |
| 1879 | 1915 | fd = dup(connection); |
| 1880 | 1916 | if( fd!=0 ) nErr++; |
| 1881 | 1917 | close(1); |
| 1882 | 1918 | fd = dup(connection); |
| 1883 | 1919 | if( fd!=1 ) nErr++; |
| 1884 | - if( !g.fAnyTrace ){ | |
| 1920 | + if( 0 && !g.fAnyTrace ){ | |
| 1885 | 1921 | close(2); |
| 1886 | 1922 | fd = dup(connection); |
| 1887 | 1923 | if( fd!=2 ) nErr++; |
| 1888 | 1924 | } |
| 1889 | 1925 | close(connection); |
| 1890 | 1926 | g.nPendingRequest = nchildren+1; |
| 1927 | + g.nRequest = nRequest+1; | |
| 1891 | 1928 | return nErr; |
| 1892 | 1929 | } |
| 1893 | 1930 | } |
| 1894 | 1931 | } |
| 1895 | 1932 | /* Bury dead children */ |
| @@ -1896,10 +1933,14 @@ | ||
| 1896 | 1933 | if( nchildren ){ |
| 1897 | 1934 | while(1){ |
| 1898 | 1935 | int iStatus = 0; |
| 1899 | 1936 | pid_t x = waitpid(-1, &iStatus, WNOHANG); |
| 1900 | 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 | + } | |
| 1901 | 1942 | nchildren--; |
| 1902 | 1943 | } |
| 1903 | 1944 | } |
| 1904 | 1945 | } |
| 1905 | 1946 | /* NOT REACHED */ |
| @@ -1921,12 +1962,12 @@ | ||
| 1921 | 1962 | |
| 1922 | 1963 | |
| 1923 | 1964 | /* |
| 1924 | 1965 | ** Returns an RFC822-formatted time string suitable for HTTP headers. |
| 1925 | 1966 | ** 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. | |
| 1928 | 1969 | ** |
| 1929 | 1970 | ** See http://www.faqs.org/rfcs/rfc822.html, section 5 |
| 1930 | 1971 | ** and http://www.faqs.org/rfcs/rfc2616.html, section 3.3. |
| 1931 | 1972 | */ |
| 1932 | 1973 | char *cgi_rfc822_datestamp(time_t now){ |
| @@ -1933,11 +1974,11 @@ | ||
| 1933 | 1974 | struct tm *pTm; |
| 1934 | 1975 | pTm = gmtime(&now); |
| 1935 | 1976 | if( pTm==0 ){ |
| 1936 | 1977 | return mprintf(""); |
| 1937 | 1978 | }else{ |
| 1938 | - return mprintf("%s, %d %s %02d %02d:%02d:%02d GMT", | |
| 1979 | + return mprintf("%s, %d %s %02d %02d:%02d:%02d +0000", | |
| 1939 | 1980 | azDays[pTm->tm_wday], pTm->tm_mday, azMonths[pTm->tm_mon], |
| 1940 | 1981 | pTm->tm_year+1900, pTm->tm_hour, pTm->tm_min, pTm->tm_sec); |
| 1941 | 1982 | } |
| 1942 | 1983 | } |
| 1943 | 1984 | |
| @@ -1947,55 +1988,42 @@ | ||
| 1947 | 1988 | ** |
| 1948 | 1989 | ** Note that this won't handle all the _allowed_ HTTP formats, just the |
| 1949 | 1990 | ** most popular one (the one generated by cgi_rfc822_datestamp(), actually). |
| 1950 | 1991 | */ |
| 1951 | 1992 | 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; | |
| 1997 | 2025 | } |
| 1998 | 2026 | |
| 1999 | 2027 | /* |
| 2000 | 2028 | ** Check the objectTime against the If-Modified-Since request header. If the |
| 2001 | 2029 | ** object time isn't any newer than the header, we immediately send back |
| @@ -2026,5 +2054,14 @@ | ||
| 2026 | 2054 | return zSshClient; |
| 2027 | 2055 | } |
| 2028 | 2056 | } |
| 2029 | 2057 | return zDefault; |
| 2030 | 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 | +} | |
| 2031 | 2068 |
| --- 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 @@ | ||
| 472 | 472 | const char *zIgnoreFlag = find_option("ignore", 0, 1); |
| 473 | 473 | unsigned scanFlags = 0; |
| 474 | 474 | unsigned flags = 0; |
| 475 | 475 | int vid, i; |
| 476 | 476 | |
| 477 | + fossil_pledge("stdio rpath wpath cpath fattr id flock tty chown"); | |
| 478 | + | |
| 477 | 479 | /* Load affirmative flag options. */ |
| 478 | 480 | for( i=0; i<count(flagDefs); ++i ){ |
| 479 | 481 | if( (command==CHANGES || !(flagDefs[i].mask & C_CLASSIFY)) |
| 480 | 482 | && find_option(flagDefs[i].option, 0, 0) ){ |
| 481 | 483 | flags |= flagDefs[i].mask; |
| @@ -555,12 +557,13 @@ | ||
| 555 | 557 | /* Find and print all requested changes. */ |
| 556 | 558 | blob_zero(&report); |
| 557 | 559 | status_report(&report, flags); |
| 558 | 560 | if( blob_size(&report) ){ |
| 559 | 561 | 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); | |
| 562 | 565 | } |
| 563 | 566 | blob_write_to_file(&report, "-"); |
| 564 | 567 | }else if( verboseFlag ){ |
| 565 | 568 | fossil_print(" (none)\n"); |
| 566 | 569 | } |
| @@ -863,11 +866,11 @@ | ||
| 863 | 866 | |
| 864 | 867 | blob_zero(&report); |
| 865 | 868 | status_report(&report, flags); |
| 866 | 869 | if( blob_size(&report) ){ |
| 867 | 870 | 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>"), | |
| 869 | 872 | g.zLocalRoot); |
| 870 | 873 | } |
| 871 | 874 | blob_write_to_file(&report, "-"); |
| 872 | 875 | } |
| 873 | 876 | blob_reset(&report); |
| @@ -1189,17 +1192,19 @@ | ||
| 1189 | 1192 | blob_add_cr(pPrompt); |
| 1190 | 1193 | #endif |
| 1191 | 1194 | } |
| 1192 | 1195 | #endif |
| 1193 | 1196 | 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 | + } | |
| 1201 | 1206 | zFile = mprintf("-"); |
| 1202 | 1207 | }else{ |
| 1203 | 1208 | Blob fname; |
| 1204 | 1209 | blob_zero(&fname); |
| 1205 | 1210 | if( g.zLocalRoot!=0 ){ |
| @@ -1213,11 +1218,11 @@ | ||
| 1213 | 1218 | blob_reset(&fname); |
| 1214 | 1219 | } |
| 1215 | 1220 | #if defined(_WIN32) |
| 1216 | 1221 | blob_add_cr(pPrompt); |
| 1217 | 1222 | #endif |
| 1218 | - blob_write_to_file(pPrompt, zFile); | |
| 1223 | + if( blob_size(pPrompt)>0 ) blob_write_to_file(pPrompt, zFile); | |
| 1219 | 1224 | if( zEditor ){ |
| 1220 | 1225 | zCmd = mprintf("%s \"%s\"", zEditor, zFile); |
| 1221 | 1226 | fossil_print("%s\n", zCmd); |
| 1222 | 1227 | if( fossil_system(zCmd) ){ |
| 1223 | 1228 | fossil_fatal("editor aborted: \"%s\"", zCmd); |
| 1224 | 1229 |
| --- 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 |
+1
| --- src/checkout.c | ||
| +++ src/checkout.c | ||
| @@ -291,10 +291,11 @@ | ||
| 291 | 291 | }else{ |
| 292 | 292 | zVers = g.argv[2]; |
| 293 | 293 | } |
| 294 | 294 | vid = load_vfile(zVers, forceMissingFlag); |
| 295 | 295 | if( prior==vid ){ |
| 296 | + db_end_transaction(0); | |
| 296 | 297 | return; |
| 297 | 298 | } |
| 298 | 299 | if( !keepFlag ){ |
| 299 | 300 | uncheckout(prior); |
| 300 | 301 | } |
| 301 | 302 |
| --- 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 @@ | ||
| 109 | 109 | ** admin user. This can be overridden using the -A|--admin-user |
| 110 | 110 | ** parameter. |
| 111 | 111 | ** |
| 112 | 112 | ** Options: |
| 113 | 113 | ** --admin-user|-A USERNAME Make USERNAME the administrator |
| 114 | +** --nocompress Omit extra delta compression | |
| 114 | 115 | ** --once Don't remember the URI. |
| 115 | 116 | ** --private Also clone private branches |
| 116 | 117 | ** --ssl-identity FILENAME Use the SSL identity if requested by the server |
| 117 | 118 | ** --ssh-command|-c SSH Use SSH as the "ssh" command |
| 118 | 119 | ** --httpauth|-B USER:PASS Add HTTP Basic Authorization to requests |
| @@ -126,10 +127,11 @@ | ||
| 126 | 127 | const char *zDefaultUser; /* Optional name of the default user */ |
| 127 | 128 | const char *zHttpAuth; /* HTTP Authorization user:pass information */ |
| 128 | 129 | int nErr = 0; |
| 129 | 130 | int urlFlags = URL_PROMPT_PW | URL_REMEMBER; |
| 130 | 131 | int syncFlags = SYNC_CLONE; |
| 132 | + int noCompress = find_option("nocompress",0,0)!=0; | |
| 131 | 133 | |
| 132 | 134 | /* Also clone private branches */ |
| 133 | 135 | if( find_option("private",0,0)!=0 ) syncFlags |= SYNC_PRIVATE; |
| 134 | 136 | if( find_option("once",0,0)!=0) urlFlags &= ~URL_REMEMBER; |
| 135 | 137 | if( find_option("verbose","v",0)!=0) syncFlags |= SYNC_VERBOSE; |
| @@ -211,14 +213,17 @@ | ||
| 211 | 213 | db_open_repository(g.argv[3]); |
| 212 | 214 | } |
| 213 | 215 | db_begin_transaction(); |
| 214 | 216 | fossil_print("Rebuilding repository meta-data...\n"); |
| 215 | 217 | 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 | + } | |
| 218 | 223 | db_end_transaction(0); |
| 219 | - fossil_print("\nVacuuming the database... "); fflush(stdout); | |
| 224 | + fossil_print("Vacuuming the database... "); fflush(stdout); | |
| 220 | 225 | if( db_int(0, "PRAGMA page_count")>1000 |
| 221 | 226 | && db_int(0, "PRAGMA page_size")<8192 ){ |
| 222 | 227 | db_multi_exec("PRAGMA page_size=8192;"); |
| 223 | 228 | } |
| 224 | 229 | db_multi_exec("VACUUM"); |
| @@ -317,14 +322,16 @@ | ||
| 317 | 322 | @ you "Download Zip" privileges. |
| 318 | 323 | } |
| 319 | 324 | }else{ |
| 320 | 325 | const char *zDLTag = db_get("download-tag","trunk"); |
| 321 | 326 | 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); | |
| 323 | 328 | @ <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); | |
| 325 | 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> | |
| 326 | 333 | } |
| 327 | 334 | if( !g.perm.Clone ){ |
| 328 | 335 | @ <p>You are not authorized to clone this repository. |
| 329 | 336 | if( g.zLogin==0 || g.zLogin[0]==0 ){ |
| 330 | 337 | @ Maybe you would be able to clone if you |
| 331 | 338 |
| --- 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 @@ | ||
| 39 | 39 | #include <stdlib.h> |
| 40 | 40 | #include <ctype.h> |
| 41 | 41 | #include <string.h> |
| 42 | 42 | #include <assert.h> |
| 43 | 43 | |
| 44 | +/* | |
| 45 | +** Debugging switch | |
| 46 | +*/ | |
| 47 | +static int eVerbose = 0; | |
| 48 | + | |
| 44 | 49 | /* |
| 45 | 50 | ** Malloc, aborting if it fails. |
| 46 | 51 | */ |
| 47 | 52 | void *safe_malloc(int nByte){ |
| 48 | 53 | void *x = malloc(nByte); |
| @@ -141,10 +146,14 @@ | ||
| 141 | 146 | (*pLN)++; |
| 142 | 147 | i++; |
| 143 | 148 | } |
| 144 | 149 | *pType = TK_SPACE; |
| 145 | 150 | return i; |
| 151 | + } | |
| 152 | + if( z[0]=='\\' && (z[1]=='\n' || (z[1]=='\r' && z[2]=='\n')) ){ | |
| 153 | + *pType = TK_SPACE; | |
| 154 | + return 1; | |
| 146 | 155 | } |
| 147 | 156 | *pType = TK_OTHER; |
| 148 | 157 | return 1; |
| 149 | 158 | } |
| 150 | 159 | |
| @@ -194,17 +203,40 @@ | ||
| 194 | 203 | */ |
| 195 | 204 | static const char *skip_space(const char *z){ |
| 196 | 205 | while( isspace(z[0]) ){ z++; } |
| 197 | 206 | return z; |
| 198 | 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 | +} | |
| 199 | 230 | |
| 200 | 231 | /* |
| 201 | 232 | ** Return true if the input is a string literal. |
| 202 | 233 | */ |
| 203 | 234 | static int is_string_lit(const char *z){ |
| 204 | 235 | int nu1, nu2; |
| 205 | 236 | z = next_non_whitespace(z, &nu1, &nu2); |
| 237 | + if( strcmp(z, "NULL")==0 ) return 1; | |
| 206 | 238 | return z[0]=='"'; |
| 207 | 239 | } |
| 208 | 240 | |
| 209 | 241 | /* |
| 210 | 242 | ** Return true if the input is an expression of string literals: |
| @@ -263,11 +295,11 @@ | ||
| 263 | 295 | |
| 264 | 296 | /* |
| 265 | 297 | ** Return true if the input is an argument that is safe to use with %s |
| 266 | 298 | ** while building an SQL statement. |
| 267 | 299 | */ |
| 268 | -static int is_s_safe(const char *z){ | |
| 300 | +static int is_sql_safe(const char *z){ | |
| 269 | 301 | int len, eType; |
| 270 | 302 | int i; |
| 271 | 303 | |
| 272 | 304 | /* A string literal is safe for use with %s */ |
| 273 | 305 | if( is_string_lit(z) ) return 1; |
| @@ -292,15 +324,33 @@ | ||
| 292 | 324 | ** let it through */ |
| 293 | 325 | if( strstr(z, "/*safe-for-%s*/")!=0 ) return 1; |
| 294 | 326 | |
| 295 | 327 | return 0; |
| 296 | 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 | +} | |
| 297 | 344 | |
| 298 | 345 | /* |
| 299 | 346 | ** Processing flags |
| 300 | 347 | */ |
| 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 */ | |
| 302 | 352 | |
| 303 | 353 | /* |
| 304 | 354 | ** A list of internal Fossil interfaces that take a printf-style format |
| 305 | 355 | ** string. |
| 306 | 356 | */ |
| @@ -308,45 +358,55 @@ | ||
| 308 | 358 | const char *zFName; /* Name of the function */ |
| 309 | 359 | int iFmtArg; /* Index of format argument. Leftmost is 1. */ |
| 310 | 360 | unsigned fmtFlags; /* Processing flags */ |
| 311 | 361 | } aFmtFunc[] = { |
| 312 | 362 | { "admin_log", 1, 0 }, |
| 313 | - { "blob_append_sql", 2, FMT_NO_S }, | |
| 363 | + { "blob_append_sql", 2, FMT_SQL }, | |
| 314 | 364 | { "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 }, | |
| 320 | 373 | { "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 }, | |
| 340 | 396 | { "json_new_string_f", 1, 0 }, |
| 397 | + { "json_set_err", 2, 0 }, | |
| 398 | + { "json_warn", 2, 0 }, | |
| 341 | 399 | { "mprintf", 1, 0 }, |
| 342 | 400 | { "socket_set_errmsg", 1, 0 }, |
| 343 | 401 | { "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 }, | |
| 348 | 408 | }; |
| 349 | 409 | |
| 350 | 410 | /* |
| 351 | 411 | ** Determine if the indentifier zIdent of length nIndent is a Fossil |
| 352 | 412 | ** internal interface that uses a printf-style argument. Return zero if not. |
| @@ -449,16 +509,17 @@ | ||
| 449 | 509 | zCopy[len] = 0; |
| 450 | 510 | azArg = 0; |
| 451 | 511 | nArg = 0; |
| 452 | 512 | z = zCopy; |
| 453 | 513 | while( z[0] ){ |
| 514 | + char cEnd; | |
| 454 | 515 | 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]; | |
| 458 | 517 | 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; | |
| 460 | 521 | z += len + 1; |
| 461 | 522 | } |
| 462 | 523 | acType = (char*)&azArg[nArg]; |
| 463 | 524 | if( fmtArg>nArg ){ |
| 464 | 525 | printf("%s:%d: too few arguments to %.*s()\n", |
| @@ -467,38 +528,47 @@ | ||
| 467 | 528 | }else{ |
| 468 | 529 | const char *zFmt = azArg[fmtArg-1]; |
| 469 | 530 | const char *zOverride = strstr(zFmt, "/*works-like:"); |
| 470 | 531 | if( zOverride ) zFmt = zOverride + sizeof("/*works-like:")-1; |
| 471 | 532 | 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); | |
| 474 | 535 | nErr++; |
| 475 | 536 | }else if( (k = formatArgCount(zFmt, nArg, acType))>=0 |
| 476 | 537 | && nArg!=fmtArg+k ){ |
| 477 | 538 | printf("%s:%d: too %s arguments to %.*s() " |
| 478 | 539 | "- got %d and expected %d\n", |
| 479 | 540 | zFilename, lnFCall, (nArg<fmtArg+k ? "few" : "many"), |
| 480 | 541 | szFName, zFCall, nArg, fmtArg+k); |
| 481 | 542 | nErr++; |
| 482 | - }else if( fmtFlags & FMT_NO_S ){ | |
| 543 | + }else if( (fmtFlags & FMT_SAFE)==0 ){ | |
| 483 | 544 | 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 | + } | |
| 490 | 558 | } |
| 491 | 559 | } |
| 492 | 560 | } |
| 493 | 561 | } |
| 494 | 562 | if( nErr ){ |
| 495 | 563 | for(i=0; i<nArg; i++){ |
| 496 | 564 | printf(" arg[%d]: %s\n", i, azArg[i]); |
| 497 | 565 | } |
| 566 | + }else if( eVerbose>1 ){ | |
| 567 | + printf("%s:%d: %.*s() ok for %d arguments\n", | |
| 568 | + zFilename, lnFCall, szFName, zFCall, nArg); | |
| 498 | 569 | } |
| 499 | - | |
| 500 | 570 | free((char*)azArg); |
| 501 | 571 | free(zCopy); |
| 502 | 572 | return nErr; |
| 503 | 573 | } |
| 504 | 574 | |
| @@ -548,16 +618,24 @@ | ||
| 548 | 618 | } |
| 549 | 619 | |
| 550 | 620 | /* |
| 551 | 621 | ** Check for format-string design rule violations on all files listed |
| 552 | 622 | ** on the command-line. |
| 623 | +** | |
| 624 | +** The eVerbose global variable is incremented with each "-v" argument. | |
| 553 | 625 | */ |
| 554 | 626 | int main(int argc, char **argv){ |
| 555 | 627 | int i; |
| 556 | 628 | int nErr = 0; |
| 557 | 629 | 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]); | |
| 559 | 637 | nErr += scan_file(argv[i], zFile); |
| 560 | 638 | free(zFile); |
| 561 | 639 | } |
| 562 | 640 | return nErr; |
| 563 | 641 | } |
| 564 | 642 |
| --- 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 @@ | ||
| 253 | 253 | |
| 254 | 254 | /* |
| 255 | 255 | ** Number of elements in an array |
| 256 | 256 | */ |
| 257 | 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 | + | |
| 258 | 268 | |
| 259 | 269 | #endif /* _RC_COMPILE_ */ |
| 260 | 270 |
| --- 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 @@ | ||
| 36 | 36 | #define CONFIGSET_SHUN 0x000010 /* Shun settings */ |
| 37 | 37 | #define CONFIGSET_USER 0x000020 /* The USER table */ |
| 38 | 38 | #define CONFIGSET_ADDR 0x000040 /* The CONCEALED table */ |
| 39 | 39 | #define CONFIGSET_XFER 0x000080 /* Transfer configuration */ |
| 40 | 40 | #define CONFIGSET_ALIAS 0x000100 /* URL Aliases */ |
| 41 | +#define CONFIGSET_SCRIBER 0x000200 /* Email subscribers */ | |
| 42 | +#define CONFIGSET_FORUM 0x000400 /* Forum posts */ | |
| 41 | 43 | |
| 42 | -#define CONFIGSET_ALL 0x0001ff /* Everything */ | |
| 44 | +#define CONFIGSET_ALL 0x0007ff /* Everything */ | |
| 43 | 45 | |
| 44 | 46 | #define CONFIGSET_OVERWRITE 0x100000 /* Causes overwrite instead of merge */ |
| 45 | 47 | |
| 46 | 48 | /* |
| 47 | 49 | ** This mask is used for the common TH1 configuration settings (i.e. those |
| @@ -58,21 +60,23 @@ | ||
| 58 | 60 | static struct { |
| 59 | 61 | const char *zName; /* Name of the configuration set */ |
| 60 | 62 | int groupMask; /* Mask for that configuration set */ |
| 61 | 63 | const char *zHelp; /* What it does */ |
| 62 | 64 | } 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" }, | |
| 74 | 78 | }; |
| 75 | 79 | |
| 76 | 80 | |
| 77 | 81 | /* |
| 78 | 82 | ** The following is a list of settings that we are willing to |
| @@ -157,10 +161,12 @@ | ||
| 157 | 161 | |
| 158 | 162 | { "@shun", CONFIGSET_SHUN }, |
| 159 | 163 | |
| 160 | 164 | { "@alias", CONFIGSET_ALIAS }, |
| 161 | 165 | |
| 166 | + { "@subscriber", CONFIGSET_SCRIBER }, | |
| 167 | + | |
| 162 | 168 | { "xfer-common-script", CONFIGSET_XFER }, |
| 163 | 169 | { "xfer-push-script", CONFIGSET_XFER }, |
| 164 | 170 | { "xfer-commit-script", CONFIGSET_XFER }, |
| 165 | 171 | { "xfer-ticket-script", CONFIGSET_XFER }, |
| 166 | 172 | |
| @@ -213,11 +219,11 @@ | ||
| 213 | 219 | |
| 214 | 220 | /* |
| 215 | 221 | ** Return the mask for the named configuration parameter if it can be |
| 216 | 222 | ** safely exported. Return 0 if the parameter is not safe to export. |
| 217 | 223 | ** |
| 218 | -** "Safe" in the previous paragraph means the permission is created to | |
| 224 | +** "Safe" in the previous paragraph means the permission is granted to | |
| 219 | 225 | ** export the property. In other words, the requesting side has presented |
| 220 | 226 | ** login credentials and has sufficient capabilities to access the requested |
| 221 | 227 | ** information. |
| 222 | 228 | */ |
| 223 | 229 | int configure_is_exportable(const char *zName){ |
| @@ -229,11 +235,14 @@ | ||
| 229 | 235 | } |
| 230 | 236 | for(i=0; i<count(aConfig); i++){ |
| 231 | 237 | if( strncmp(zName, aConfig[i].zName, n)==0 && aConfig[i].zName[n]==0 ){ |
| 232 | 238 | int m = aConfig[i].groupMask; |
| 233 | 239 | if( !g.perm.Admin ){ |
| 234 | - m &= ~CONFIGSET_USER; | |
| 240 | + m &= ~(CONFIGSET_USER|CONFIGSET_SCRIBER); | |
| 241 | + } | |
| 242 | + if( !g.perm.RdForum ){ | |
| 243 | + m &= ~(CONFIGSET_FORUM); | |
| 235 | 244 | } |
| 236 | 245 | if( !g.perm.RdAddr ){ |
| 237 | 246 | m &= ~CONFIGSET_ADDR; |
| 238 | 247 | } |
| 239 | 248 | return m; |
| @@ -312,11 +321,15 @@ | ||
| 312 | 321 | ** Mask consists of one or more CONFIGSET_* values ORed together, to |
| 313 | 322 | ** designate what types of configuration we are allowed to receive. |
| 314 | 323 | ** |
| 315 | 324 | ** NEW FORMAT: |
| 316 | 325 | ** |
| 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 | +** | |
| 318 | 331 | ** zName indicates the table that holds the configuration information being |
| 319 | 332 | ** transferred. pContent is a string that consist of alternating Fossil |
| 320 | 333 | ** and SQL tokens. The First token is a timestamp in seconds since 1970. |
| 321 | 334 | ** The second token is a primary key for the table identified by zName. If |
| 322 | 335 | ** The entry with the corresponding primary key exists and has a more recent |
| @@ -332,53 +345,44 @@ | ||
| 332 | 345 | ** /config $MTIME $NAME value $VALUE |
| 333 | 346 | ** /user $MTIME $LOGIN pw $VALUE cap $VALUE info $VALUE photo $VALUE |
| 334 | 347 | ** /shun $MTIME $UUID scom $VALUE |
| 335 | 348 | ** /reportfmt $MTIME $TITLE owner $VALUE cols $VALUE sqlcode $VALUE |
| 336 | 349 | ** /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 ... | |
| 355 | 351 | */ |
| 356 | 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; | |
| 357 | 356 | if( zName[0]=='/' ){ |
| 358 | 357 | /* The new format */ |
| 359 | - char *azToken[12]; | |
| 358 | + char *azToken[24]; | |
| 360 | 359 | int nToken = 0; |
| 361 | 360 | int ii, jj; |
| 362 | 361 | int thisMask; |
| 363 | 362 | Blob name, value, sql; |
| 364 | 363 | 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 */ | |
| 369 | 368 | } 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"} }, | |
| 375 | 376 | }; |
| 377 | + | |
| 378 | + /* Locate the receiveType in aType[ii] */ | |
| 376 | 379 | for(ii=0; ii<count(aType); ii++){ |
| 377 | 380 | if( fossil_strcmp(&aType[ii].zName[1],&zName[1])==0 ) break; |
| 378 | 381 | } |
| 379 | 382 | if( ii>=count(aType) ) return; |
| 383 | + | |
| 380 | 384 | while( blob_token(pContent, &name) && blob_sqltoken(pContent, &value) ){ |
| 381 | 385 | char *z = blob_terminate(&name); |
| 382 | 386 | if( !safeSql(z) ) return; |
| 383 | 387 | if( nToken>0 ){ |
| 384 | 388 | for(jj=0; jj<aType[ii].nField; jj++){ |
| @@ -389,19 +393,25 @@ | ||
| 389 | 393 | if( !safeInt(z) ) return; |
| 390 | 394 | } |
| 391 | 395 | azToken[nToken++] = z; |
| 392 | 396 | azToken[nToken++] = z = blob_terminate(&value); |
| 393 | 397 | if( !safeSql(z) ) return; |
| 394 | - if( nToken>=count(azToken) ) break; | |
| 398 | + if( nToken>=count(azToken)-1 ) break; | |
| 395 | 399 | } |
| 396 | 400 | if( nToken<2 ) return; |
| 397 | 401 | if( aType[ii].zName[0]=='/' ){ |
| 398 | 402 | thisMask = configure_is_exportable(azToken[1]); |
| 399 | 403 | }else{ |
| 400 | 404 | thisMask = configure_is_exportable(aType[ii].zName); |
| 401 | 405 | } |
| 402 | 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 | + } | |
| 403 | 413 | |
| 404 | 414 | blob_zero(&sql); |
| 405 | 415 | if( groupMask & CONFIGSET_OVERWRITE ){ |
| 406 | 416 | if( (thisMask & configHasBeenReset)==0 && aType[ii].zName[0]!='/' ){ |
| 407 | 417 | db_multi_exec("DELETE FROM \"%w\"", &aType[ii].zName[1]); |
| @@ -409,16 +419,17 @@ | ||
| 409 | 419 | } |
| 410 | 420 | blob_append_sql(&sql, "REPLACE INTO "); |
| 411 | 421 | }else{ |
| 412 | 422 | blob_append_sql(&sql, "INSERT OR IGNORE INTO "); |
| 413 | 423 | } |
| 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); | |
| 415 | 426 | for(jj=2; jj<nToken; jj+=2){ |
| 416 | 427 | blob_append_sql(&sql, ",\"%w\"", azToken[jj]); |
| 417 | 428 | } |
| 418 | 429 | 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*/); | |
| 420 | 431 | for(jj=2; jj<nToken; jj+=2){ |
| 421 | 432 | blob_append_sql(&sql, ",%s", azToken[jj+1] /*safe-for-%s*/); |
| 422 | 433 | } |
| 423 | 434 | db_multi_exec("%s)", blob_sql_text(&sql)); |
| 424 | 435 | if( db_changes()==0 ){ |
| @@ -573,10 +584,39 @@ | ||
| 573 | 584 | blob_size(&rec), blob_str(&rec)); |
| 574 | 585 | nCard++; |
| 575 | 586 | blob_reset(&rec); |
| 576 | 587 | } |
| 577 | 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); | |
| 578 | 618 | } |
| 579 | 619 | db_prepare(&q, "SELECT mtime, quote(name), quote(value) FROM config" |
| 580 | 620 | " WHERE name=:name AND mtime>=%lld", iStart); |
| 581 | 621 | for(ii=0; ii<count(aConfig); ii++){ |
| 582 | 622 | if( (aConfig[ii].groupMask & groupMask)!=0 && aConfig[ii].zName[0]!='@' ){ |
| @@ -612,11 +652,12 @@ | ||
| 612 | 652 | } |
| 613 | 653 | } |
| 614 | 654 | if( notFoundIsFatal ){ |
| 615 | 655 | fossil_print("Available configuration areas:\n"); |
| 616 | 656 | 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); | |
| 618 | 659 | } |
| 619 | 660 | fossil_fatal("no such configuration area: \"%s\"", z); |
| 620 | 661 | } |
| 621 | 662 | return 0; |
| 622 | 663 | } |
| @@ -792,10 +833,19 @@ | ||
| 792 | 833 | db_create_default_users(0, 0); |
| 793 | 834 | }else if( fossil_strcmp(zName,"@concealed")==0 ){ |
| 794 | 835 | db_multi_exec("DELETE FROM concealed"); |
| 795 | 836 | }else if( fossil_strcmp(zName,"@shun")==0 ){ |
| 796 | 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 | + } | |
| 797 | 847 | }else if( fossil_strcmp(zName,"@reportfmt")==0 ){ |
| 798 | 848 | db_multi_exec("DELETE FROM reportfmt"); |
| 799 | 849 | assert( strchr(zRepositorySchemaDefaultReports,'%')==0 ); |
| 800 | 850 | db_multi_exec(zRepositorySchemaDefaultReports /*works-like:""*/); |
| 801 | 851 | } |
| 802 | 852 |
| --- 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 @@ | ||
| 173 | 173 | |
| 174 | 174 | /* Update the user preferences cookie, if necessary, and shut down this |
| 175 | 175 | ** module |
| 176 | 176 | */ |
| 177 | 177 | void cookie_render(void){ |
| 178 | - if( cookies.bChanged ){ | |
| 178 | + if( cookies.bChanged && P("udc")!=0 ){ | |
| 179 | 179 | Blob new; |
| 180 | 180 | int i; |
| 181 | 181 | blob_init(&new, 0, 0); |
| 182 | 182 | for(i=0;i<cookies.nParam;i++){ |
| 183 | 183 | if( i>0 ) blob_append(&new, ",", 1); |
| 184 | 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 ){ |
| 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 |
+91
-87
| --- src/cson_amalgamation.c | ||
| +++ src/cson_amalgamation.c | ||
| @@ -19,14 +19,14 @@ | ||
| 19 | 19 | # define JSON_PARSER_DLL_API __declspec(dllexport) |
| 20 | 20 | # else |
| 21 | 21 | # define JSON_PARSER_DLL_API __declspec(dllimport) |
| 22 | 22 | # endif |
| 23 | 23 | # else |
| 24 | -# define JSON_PARSER_DLL_API | |
| 24 | +# define JSON_PARSER_DLL_API | |
| 25 | 25 | # endif |
| 26 | 26 | #else |
| 27 | -# define JSON_PARSER_DLL_API | |
| 27 | +# define JSON_PARSER_DLL_API | |
| 28 | 28 | #endif |
| 29 | 29 | |
| 30 | 30 | /* Determine the integer type use to parse non-floating point numbers */ |
| 31 | 31 | #ifdef _WIN32 |
| 32 | 32 | typedef __int64 JSON_int_t; |
| @@ -34,22 +34,22 @@ | ||
| 34 | 34 | #define JSON_PARSER_INTEGER_SPRINTF_TOKEN "%I64d" |
| 35 | 35 | #elif (__STDC_VERSION__ >= 199901L) || (HAVE_LONG_LONG == 1) |
| 36 | 36 | typedef long long JSON_int_t; |
| 37 | 37 | #define JSON_PARSER_INTEGER_SSCANF_TOKEN "%lld" |
| 38 | 38 | #define JSON_PARSER_INTEGER_SPRINTF_TOKEN "%lld" |
| 39 | -#else | |
| 39 | +#else | |
| 40 | 40 | typedef long JSON_int_t; |
| 41 | 41 | #define JSON_PARSER_INTEGER_SSCANF_TOKEN "%ld" |
| 42 | 42 | #define JSON_PARSER_INTEGER_SPRINTF_TOKEN "%ld" |
| 43 | 43 | #endif |
| 44 | 44 | |
| 45 | 45 | |
| 46 | 46 | #ifdef __cplusplus |
| 47 | 47 | extern "C" { |
| 48 | -#endif | |
| 48 | +#endif | |
| 49 | 49 | |
| 50 | -typedef enum | |
| 50 | +typedef enum | |
| 51 | 51 | { |
| 52 | 52 | JSON_E_NONE = 0, |
| 53 | 53 | JSON_E_INVALID_CHAR, |
| 54 | 54 | JSON_E_INVALID_KEYWORD, |
| 55 | 55 | JSON_E_INVALID_ESCAPE_SEQUENCE, |
| @@ -60,11 +60,11 @@ | ||
| 60 | 60 | JSON_E_EXPECTED_KEY, |
| 61 | 61 | JSON_E_EXPECTED_COLON, |
| 62 | 62 | JSON_E_OUT_OF_MEMORY |
| 63 | 63 | } JSON_error; |
| 64 | 64 | |
| 65 | -typedef enum | |
| 65 | +typedef enum | |
| 66 | 66 | { |
| 67 | 67 | JSON_T_NONE = 0, |
| 68 | 68 | JSON_T_ARRAY_BEGIN, |
| 69 | 69 | JSON_T_ARRAY_END, |
| 70 | 70 | JSON_T_OBJECT_BEGIN, |
| @@ -80,33 +80,33 @@ | ||
| 80 | 80 | } JSON_type; |
| 81 | 81 | |
| 82 | 82 | typedef struct JSON_value_struct { |
| 83 | 83 | union { |
| 84 | 84 | JSON_int_t integer_value; |
| 85 | - | |
| 85 | + | |
| 86 | 86 | double float_value; |
| 87 | - | |
| 87 | + | |
| 88 | 88 | struct { |
| 89 | 89 | const char* value; |
| 90 | 90 | size_t length; |
| 91 | 91 | } str; |
| 92 | 92 | } vu; |
| 93 | 93 | } JSON_value; |
| 94 | 94 | |
| 95 | 95 | typedef struct JSON_parser_struct* JSON_parser; |
| 96 | 96 | |
| 97 | -/*! \brief JSON parser callback | |
| 97 | +/*! \brief JSON parser callback | |
| 98 | 98 | |
| 99 | 99 | \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. | |
| 101 | 101 | \param value A representation of the parsed value. This parameter is NULL for |
| 102 | 102 | JSON_T_ARRAY_BEGIN, JSON_T_ARRAY_END, JSON_T_OBJECT_BEGIN, JSON_T_OBJECT_END, |
| 103 | 103 | JSON_T_NULL, JSON_T_TRUE, and JSON_T_FALSE. String values are always returned |
| 104 | 104 | as zero-terminated C strings. |
| 105 | 105 | |
| 106 | 106 | \return Non-zero if parsing should continue, else zero. |
| 107 | -*/ | |
| 107 | +*/ | |
| 108 | 108 | typedef int (*JSON_parser_callback)(void* ctx, int type, const JSON_value* value); |
| 109 | 109 | |
| 110 | 110 | |
| 111 | 111 | /** |
| 112 | 112 | A typedef for allocator functions semantically compatible with malloc(). |
| @@ -115,11 +115,11 @@ | ||
| 115 | 115 | /** |
| 116 | 116 | A typedef for deallocator functions semantically compatible with free(). |
| 117 | 117 | */ |
| 118 | 118 | typedef void (*JSON_free_t)(void* mem); |
| 119 | 119 | |
| 120 | -/*! \brief The structure used to configure a JSON parser object | |
| 120 | +/*! \brief The structure used to configure a JSON parser object | |
| 121 | 121 | */ |
| 122 | 122 | typedef struct { |
| 123 | 123 | /** Pointer to a callback, called when the parser has something to tell |
| 124 | 124 | the user. This parameter may be NULL. In this case the input is |
| 125 | 125 | merely checked for validity. |
| @@ -176,17 +176,17 @@ | ||
| 176 | 176 | |
| 177 | 177 | \param config. Used to configure the parser. |
| 178 | 178 | */ |
| 179 | 179 | JSON_PARSER_DLL_API void init_JSON_config(JSON_config * config); |
| 180 | 180 | |
| 181 | -/*! \brief Create a JSON parser object | |
| 181 | +/*! \brief Create a JSON parser object | |
| 182 | 182 | |
| 183 | 183 | \param config. Used to configure the parser. Set to NULL to use |
| 184 | 184 | the default configuration. See init_JSON_config. Its contents are |
| 185 | 185 | copied by this function, so it need not outlive the returned |
| 186 | 186 | object. |
| 187 | - | |
| 187 | + | |
| 188 | 188 | \return The parser object, which is owned by the caller and must eventually |
| 189 | 189 | be freed by calling delete_JSON_parser(). |
| 190 | 190 | */ |
| 191 | 191 | JSON_PARSER_DLL_API JSON_parser new_JSON_parser(JSON_config const* config); |
| 192 | 192 | |
| @@ -200,16 +200,16 @@ | ||
| 200 | 200 | JSON_PARSER_DLL_API int JSON_parser_char(JSON_parser jc, int next_char); |
| 201 | 201 | |
| 202 | 202 | /*! \brief Finalize parsing. |
| 203 | 203 | |
| 204 | 204 | Call this method once after all input characters have been consumed. |
| 205 | - | |
| 205 | + | |
| 206 | 206 | \return Non-zero, if all parsed characters are valid JSON, zero otherwise. |
| 207 | 207 | */ |
| 208 | 208 | JSON_PARSER_DLL_API int JSON_parser_done(JSON_parser jc); |
| 209 | 209 | |
| 210 | -/*! \brief Determine if a given string is valid JSON white space | |
| 210 | +/*! \brief Determine if a given string is valid JSON white space | |
| 211 | 211 | |
| 212 | 212 | \return Non-zero if the string is valid, zero otherwise. |
| 213 | 213 | */ |
| 214 | 214 | JSON_PARSER_DLL_API int JSON_parser_is_legal_white_space_string(const char* s); |
| 215 | 215 | |
| @@ -226,12 +226,12 @@ | ||
| 226 | 226 | JSON_PARSER_DLL_API int JSON_parser_reset(JSON_parser jc); |
| 227 | 227 | |
| 228 | 228 | |
| 229 | 229 | #ifdef __cplusplus |
| 230 | 230 | } |
| 231 | -#endif | |
| 232 | - | |
| 231 | +#endif | |
| 232 | + | |
| 233 | 233 | |
| 234 | 234 | #endif /* JSON_PARSER_H */ |
| 235 | 235 | /* end file parser/JSON_parser.h */ |
| 236 | 236 | /* begin file parser/JSON_parser.c */ |
| 237 | 237 | /* |
| @@ -322,11 +322,11 @@ | ||
| 322 | 322 | #endif |
| 323 | 323 | |
| 324 | 324 | |
| 325 | 325 | #define true 1 |
| 326 | 326 | #define false 0 |
| 327 | -#define __ -1 /* the universal error code */ | |
| 327 | +#define XX -1 /* the universal error code */ | |
| 328 | 328 | |
| 329 | 329 | /* values chosen so that the object size is approx equal to one page (4K) */ |
| 330 | 330 | #ifndef JSON_PARSER_STACK_SIZE |
| 331 | 331 | # define JSON_PARSER_STACK_SIZE 128 |
| 332 | 332 | #endif |
| @@ -414,14 +414,14 @@ | ||
| 414 | 414 | /* |
| 415 | 415 | This array maps the 128 ASCII characters into character classes. |
| 416 | 416 | The remaining Unicode characters should be mapped to C_ETC. |
| 417 | 417 | Non-whitespace control characters are errors. |
| 418 | 418 | */ |
| 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, | |
| 423 | 423 | |
| 424 | 424 | C_SPACE, C_ETC, C_QUOTE, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, |
| 425 | 425 | C_ETC, C_ETC, C_STAR, C_PLUS, C_COMMA, C_MINUS, C_POINT, C_SLASH, |
| 426 | 426 | C_ZERO, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, C_DIGIT, |
| 427 | 427 | C_DIGIT, C_DIGIT, C_COLON, C_ETC, C_ETC, C_ETC, C_ETC, C_ETC, |
| @@ -448,11 +448,11 @@ | ||
| 448 | 448 | KE, /* key */ |
| 449 | 449 | CO, /* colon */ |
| 450 | 450 | VA, /* value */ |
| 451 | 451 | AR, /* array */ |
| 452 | 452 | ST, /* string */ |
| 453 | - ES, /* escape */ | |
| 453 | + ESC, /* escape */ | |
| 454 | 454 | U1, /* u1 */ |
| 455 | 455 | U2, /* u2 */ |
| 456 | 456 | U3, /* u3 */ |
| 457 | 457 | U4, /* u4 */ |
| 458 | 458 | MI, /* minus */ |
| @@ -506,46 +506,46 @@ | ||
| 506 | 506 | negative number. A JSON text is accepted if at the end of the text the |
| 507 | 507 | state is OK and if the mode is MODE_DONE. |
| 508 | 508 | |
| 509 | 509 | white 1-9 ABCDF etc |
| 510 | 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}, | |
| 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 | 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 | 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,__,__,__,__}, | |
| 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 | 547 | }; |
| 548 | 548 | |
| 549 | 549 | |
| 550 | 550 | /* |
| 551 | 551 | These modes can be pushed on the stack. |
| @@ -1050,11 +1050,11 @@ | ||
| 1050 | 1050 | } |
| 1051 | 1051 | if (next_char >= 128) { |
| 1052 | 1052 | next_class = C_ETC; |
| 1053 | 1053 | } else { |
| 1054 | 1054 | next_class = ascii_class[next_char]; |
| 1055 | - if (next_class <= __) { | |
| 1055 | + if (next_class <= XX) { | |
| 1056 | 1056 | set_error(jc); |
| 1057 | 1057 | return false; |
| 1058 | 1058 | } |
| 1059 | 1059 | } |
| 1060 | 1060 | |
| @@ -1090,11 +1090,11 @@ | ||
| 1090 | 1090 | } |
| 1091 | 1091 | break; |
| 1092 | 1092 | /* escaped char */ |
| 1093 | 1093 | case EX: |
| 1094 | 1094 | jc->escaped = 1; |
| 1095 | - jc->state = ES; | |
| 1095 | + jc->state = ESC; | |
| 1096 | 1096 | break; |
| 1097 | 1097 | /* integer detected by minus */ |
| 1098 | 1098 | case MX: |
| 1099 | 1099 | jc->type = JSON_T_INTEGER; |
| 1100 | 1100 | jc->state = MI; |
| @@ -1401,10 +1401,14 @@ | ||
| 1401 | 1401 | config->malloc = malloc; |
| 1402 | 1402 | config->free = free; |
| 1403 | 1403 | } |
| 1404 | 1404 | } |
| 1405 | 1405 | |
| 1406 | +#undef XX | |
| 1407 | +#undef COUNTOF | |
| 1408 | +#undef parse_buffer_clear | |
| 1409 | +#undef parse_buffer_pop_back_char | |
| 1406 | 1410 | /* end file parser/JSON_parser.c */ |
| 1407 | 1411 | /* begin file ./cson.c */ |
| 1408 | 1412 | #include <assert.h> |
| 1409 | 1413 | #include <stdlib.h> /* malloc()/free() */ |
| 1410 | 1414 | #include <string.h> |
| @@ -1431,11 +1435,11 @@ | ||
| 1431 | 1435 | #if defined(__cplusplus) |
| 1432 | 1436 | extern "C" { |
| 1433 | 1437 | #endif |
| 1434 | 1438 | |
| 1435 | 1439 | |
| 1436 | - | |
| 1440 | + | |
| 1437 | 1441 | /** |
| 1438 | 1442 | This type holds the "vtbl" for type-specific operations when |
| 1439 | 1443 | working with cson_value objects. |
| 1440 | 1444 | |
| 1441 | 1445 | All cson_values of a given logical type share a pointer to a single |
| @@ -1583,11 +1587,11 @@ | ||
| 1583 | 1587 | */ |
| 1584 | 1588 | #define CSON_CAST(T,V) ((T*)((V)->value)) |
| 1585 | 1589 | /** |
| 1586 | 1590 | Assumes V is a pointer to memory which is allocated as part of a |
| 1587 | 1591 | 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) | |
| 1589 | 1593 | from that address and casting it to a (cson_value*) |
| 1590 | 1594 | */ |
| 1591 | 1595 | #define CSON_VCAST(V) ((cson_value *)(((unsigned char *)(V))-sizeof(cson_value))) |
| 1592 | 1596 | |
| 1593 | 1597 | /** |
| @@ -1607,11 +1611,11 @@ | ||
| 1607 | 1611 | #define CSON_OBJ(V) CSON_CAST(cson_object,(V)) |
| 1608 | 1612 | #define CSON_ARRAY(V) CSON_CAST(cson_array,(V)) |
| 1609 | 1613 | |
| 1610 | 1614 | /** |
| 1611 | 1615 | Holds special shared "constant" (though they are non-const) |
| 1612 | - values. | |
| 1616 | + values. | |
| 1613 | 1617 | */ |
| 1614 | 1618 | static struct CSON_EMPTY_HOLDER_ |
| 1615 | 1619 | { |
| 1616 | 1620 | char trueValue; |
| 1617 | 1621 | cson_string stringValue; |
| @@ -1620,17 +1624,17 @@ | ||
| 1620 | 1624 | cson_string_empty_m |
| 1621 | 1625 | }; |
| 1622 | 1626 | |
| 1623 | 1627 | /** |
| 1624 | 1628 | Indexes into the CSON_SPECIAL_VALUES array. |
| 1625 | - | |
| 1629 | + | |
| 1626 | 1630 | If this enum changes in any way, |
| 1627 | 1631 | makes damned sure that CSON_SPECIAL_VALUES is updated |
| 1628 | 1632 | to match!!! |
| 1629 | 1633 | */ |
| 1630 | 1634 | enum CSON_INTERNAL_VALUES { |
| 1631 | - | |
| 1635 | + | |
| 1632 | 1636 | CSON_VAL_UNDEF = 0, |
| 1633 | 1637 | CSON_VAL_NULL = 1, |
| 1634 | 1638 | CSON_VAL_TRUE = 2, |
| 1635 | 1639 | CSON_VAL_FALSE = 3, |
| 1636 | 1640 | CSON_VAL_INT_0 = 4, |
| @@ -1642,11 +1646,11 @@ | ||
| 1642 | 1646 | /** |
| 1643 | 1647 | Some "special" shared cson_value instances. |
| 1644 | 1648 | |
| 1645 | 1649 | These values MUST be initialized in the order specified |
| 1646 | 1650 | by the CSON_INTERNAL_VALUES enum. |
| 1647 | - | |
| 1651 | + | |
| 1648 | 1652 | Note that they are not const because they are used as |
| 1649 | 1653 | shared-allocation objects in non-const contexts. However, the |
| 1650 | 1654 | public API provides no way to modifying them, and clients who |
| 1651 | 1655 | modify values directly are subject to The Wrath of Undefined |
| 1652 | 1656 | Behaviour. |
| @@ -1665,11 +1669,11 @@ | ||
| 1665 | 1669 | |
| 1666 | 1670 | /** |
| 1667 | 1671 | Returns non-0 (true) if m is one of our special |
| 1668 | 1672 | "built-in" values, e.g. from CSON_SPECIAL_VALUES and some |
| 1669 | 1673 | "empty" values. |
| 1670 | - | |
| 1674 | + | |
| 1671 | 1675 | If this returns true, m MUST NOT be free()d! |
| 1672 | 1676 | */ |
| 1673 | 1677 | static char cson_value_is_builtin( void const * m ) |
| 1674 | 1678 | { |
| 1675 | 1679 | if((m >= (void const *)&CSON_EMPTY_HOLDER) |
| @@ -2183,11 +2187,11 @@ | ||
| 2183 | 2187 | static int cson_value_list_visit( cson_value_list * self, |
| 2184 | 2188 | int (*visitor)(cson_value * obj, void * visitorState ), |
| 2185 | 2189 | void * visitorState ); |
| 2186 | 2190 | #endif |
| 2187 | 2191 | #endif |
| 2188 | - | |
| 2192 | + | |
| 2189 | 2193 | #if 0 |
| 2190 | 2194 | # define LIST_T cson_value_list |
| 2191 | 2195 | # define VALUE_T cson_value * |
| 2192 | 2196 | # define VALUE_T_IS_PTR 1 |
| 2193 | 2197 | # define LIST_T cson_kvp_list |
| @@ -2362,11 +2366,11 @@ | ||
| 2362 | 2366 | return cson_value_object_alloc(); |
| 2363 | 2367 | } |
| 2364 | 2368 | |
| 2365 | 2369 | cson_object * cson_new_object() |
| 2366 | 2370 | { |
| 2367 | - | |
| 2371 | + | |
| 2368 | 2372 | return cson_value_get_object( cson_value_new_object() ); |
| 2369 | 2373 | } |
| 2370 | 2374 | |
| 2371 | 2375 | cson_value * cson_value_new_array() |
| 2372 | 2376 | { |
| @@ -2608,11 +2612,11 @@ | ||
| 2608 | 2612 | { |
| 2609 | 2613 | cson_int_t i = 0; |
| 2610 | 2614 | int rc = 0; |
| 2611 | 2615 | switch(val->api->typeID) |
| 2612 | 2616 | { |
| 2613 | - case CSON_TYPE_UNDEF: | |
| 2617 | + case CSON_TYPE_UNDEF: | |
| 2614 | 2618 | case CSON_TYPE_NULL: |
| 2615 | 2619 | i = 0; |
| 2616 | 2620 | break; |
| 2617 | 2621 | case CSON_TYPE_BOOL: { |
| 2618 | 2622 | char b = 0; |
| @@ -2661,11 +2665,11 @@ | ||
| 2661 | 2665 | { |
| 2662 | 2666 | cson_double_t d = 0.0; |
| 2663 | 2667 | int rc = 0; |
| 2664 | 2668 | switch(val->api->typeID) |
| 2665 | 2669 | { |
| 2666 | - case CSON_TYPE_UNDEF: | |
| 2670 | + case CSON_TYPE_UNDEF: | |
| 2667 | 2671 | case CSON_TYPE_NULL: |
| 2668 | 2672 | d = 0; |
| 2669 | 2673 | break; |
| 2670 | 2674 | case CSON_TYPE_BOOL: { |
| 2671 | 2675 | char b = 0; |
| @@ -2791,11 +2795,11 @@ | ||
| 2791 | 2795 | #if 0 |
| 2792 | 2796 | /** |
| 2793 | 2797 | Removes and returns the last value from the given array, |
| 2794 | 2798 | shrinking its size by 1. Returns NULL if ar is NULL, |
| 2795 | 2799 | ar->list.count is 0, or the element at that index is NULL. |
| 2796 | - | |
| 2800 | + | |
| 2797 | 2801 | |
| 2798 | 2802 | If removeRef is true then cson_value_free() is called to remove |
| 2799 | 2803 | ar's reference count for the value. In that case NULL is returned, |
| 2800 | 2804 | even if the object still has live references. If removeRef is false |
| 2801 | 2805 | then the caller takes over ownership of that reference count point. |
| @@ -2858,11 +2862,11 @@ | ||
| 2858 | 2862 | #if !defined(NDEBUG) && CSON_VOID_PTR_IS_BIG |
| 2859 | 2863 | assert( sizeof(cson_int_t) <= sizeof(void *) ); |
| 2860 | 2864 | #endif |
| 2861 | 2865 | if( c ) |
| 2862 | 2866 | { |
| 2863 | - memcpy(CSON_INT(c), &v, sizeof(v)); | |
| 2867 | + memcpy( CSON_INT(c), &v, sizeof(v) ); | |
| 2864 | 2868 | } |
| 2865 | 2869 | return c; |
| 2866 | 2870 | } |
| 2867 | 2871 | } |
| 2868 | 2872 | |
| @@ -2877,11 +2881,11 @@ | ||
| 2877 | 2881 | else |
| 2878 | 2882 | { |
| 2879 | 2883 | cson_value * c = cson_value_new(CSON_TYPE_DOUBLE,0); |
| 2880 | 2884 | if( c ) |
| 2881 | 2885 | { |
| 2882 | - memcpy(CSON_DBL(c), &v, sizeof(v)); | |
| 2886 | + memcpy( CSON_DBL(c), &v, sizeof(v) ); | |
| 2883 | 2887 | } |
| 2884 | 2888 | return c; |
| 2885 | 2889 | } |
| 2886 | 2890 | } |
| 2887 | 2891 | |
| @@ -3068,11 +3072,11 @@ | ||
| 3068 | 3072 | qsort( obj->kvp.list, obj->kvp.count, sizeof(cson_kvp*), |
| 3069 | 3073 | cson_kvp_cmp ); |
| 3070 | 3074 | } |
| 3071 | 3075 | |
| 3072 | 3076 | } |
| 3073 | -#endif | |
| 3077 | +#endif | |
| 3074 | 3078 | |
| 3075 | 3079 | int cson_object_unset( cson_object * obj, char const * key ) |
| 3076 | 3080 | { |
| 3077 | 3081 | if( ! obj || !key || !*key ) return cson_rc.ArgError; |
| 3078 | 3082 | else |
| @@ -3238,11 +3242,11 @@ | ||
| 3238 | 3242 | using p->key. In any other case cson_rc.InternalError is returned. |
| 3239 | 3243 | |
| 3240 | 3244 | Returns cson_rc.AllocError if an allocation fails. |
| 3241 | 3245 | |
| 3242 | 3246 | Returns 0 on success. On error, parsing must be ceased immediately. |
| 3243 | - | |
| 3247 | + | |
| 3244 | 3248 | Ownership of val is ALWAYS TRANSFERED to this function. If this |
| 3245 | 3249 | function fails, val will be cleaned up and destroyed. (This |
| 3246 | 3250 | simplifies error handling in the core parser.) |
| 3247 | 3251 | */ |
| 3248 | 3252 | static int cson_parser_set_key( cson_parser * p, cson_value * val ) |
| @@ -3485,11 +3489,11 @@ | ||
| 3485 | 3489 | ++p->totalKeyCount; |
| 3486 | 3490 | break; |
| 3487 | 3491 | } |
| 3488 | 3492 | case JSON_T_STRING: { |
| 3489 | 3493 | cson_value * v = cson_value_new_string( value->vu.str.value, value->vu.str.length ); |
| 3490 | - rc = ( NULL == v ) | |
| 3494 | + rc = ( NULL == v ) | |
| 3491 | 3495 | ? cson_rc.AllocError |
| 3492 | 3496 | : cson_parser_push_value( p, v ); |
| 3493 | 3497 | break; |
| 3494 | 3498 | } |
| 3495 | 3499 | default: |
| @@ -3532,11 +3536,11 @@ | ||
| 3532 | 3536 | To properly take over ownership of the parser's root node on a |
| 3533 | 3537 | successful parse: |
| 3534 | 3538 | |
| 3535 | 3539 | - Copy p->root's pointer and set p->root to NULL. |
| 3536 | 3540 | - Eventually free up p->root with cson_value_free(). |
| 3537 | - | |
| 3541 | + | |
| 3538 | 3542 | If you do not set p->root to NULL, p->root will be freed along with |
| 3539 | 3543 | any other items inserted into it (or under it) during the parsing |
| 3540 | 3544 | process. |
| 3541 | 3545 | */ |
| 3542 | 3546 | static int cson_parser_clean( cson_parser * p ) |
| @@ -3571,11 +3575,11 @@ | ||
| 3571 | 3575 | int rc = 0; |
| 3572 | 3576 | unsigned int len = 1; |
| 3573 | 3577 | cson_parse_info info = info_ ? *info_ : cson_parse_info_empty; |
| 3574 | 3578 | cson_parser p = cson_parser_empty; |
| 3575 | 3579 | if( ! tgt || ! src ) return cson_rc.ArgError; |
| 3576 | - | |
| 3580 | + | |
| 3577 | 3581 | { |
| 3578 | 3582 | JSON_config jopt = {0}; |
| 3579 | 3583 | init_JSON_config( &jopt ); |
| 3580 | 3584 | jopt.allow_comments = opt.allowComments; |
| 3581 | 3585 | jopt.depth = opt.maxDepth; |
| @@ -3780,11 +3784,11 @@ | ||
| 3780 | 3784 | unsigned char const * end = (unsigned char const *)(str ? (str + len) : NULL); |
| 3781 | 3785 | unsigned char const * next = NULL; |
| 3782 | 3786 | int ch; |
| 3783 | 3787 | unsigned char clen = 0; |
| 3784 | 3788 | char escChar[3] = {'\\',0,0}; |
| 3785 | - enum { UBLen = 13 }; | |
| 3789 | + enum { UBLen = 20 }; | |
| 3786 | 3790 | char ubuf[UBLen]; |
| 3787 | 3791 | int rc = 0; |
| 3788 | 3792 | rc = f(state, "\"", 1 ); |
| 3789 | 3793 | for( ; (pos < end) && (0 == rc); pos += clen ) |
| 3790 | 3794 | { |
| @@ -4640,11 +4644,11 @@ | ||
| 4640 | 4644 | #endif |
| 4641 | 4645 | #undef TRY_SHARING |
| 4642 | 4646 | cson_value_add_reference(rc); |
| 4643 | 4647 | return rc; |
| 4644 | 4648 | } |
| 4645 | - | |
| 4649 | + | |
| 4646 | 4650 | static cson_value * cson_value_clone_array( cson_value const * orig ) |
| 4647 | 4651 | { |
| 4648 | 4652 | unsigned int i = 0; |
| 4649 | 4653 | cson_array const * asrc = cson_value_get_array( orig ); |
| 4650 | 4654 | unsigned int alen = cson_array_length_get( asrc ); |
| @@ -4680,11 +4684,11 @@ | ||
| 4680 | 4684 | cson_value_free(cl)/*remove our artificial reference */; |
| 4681 | 4685 | } |
| 4682 | 4686 | } |
| 4683 | 4687 | return destV; |
| 4684 | 4688 | } |
| 4685 | - | |
| 4689 | + | |
| 4686 | 4690 | static cson_value * cson_value_clone_object( cson_value const * orig ) |
| 4687 | 4691 | { |
| 4688 | 4692 | cson_object const * src = cson_value_get_object( orig ); |
| 4689 | 4693 | cson_value * destV = NULL; |
| 4690 | 4694 | cson_object * dest = NULL; |
| @@ -4834,11 +4838,11 @@ | ||
| 4834 | 4838 | } |
| 4835 | 4839 | case CSON_TYPE_STRING: { |
| 4836 | 4840 | cson_string const * jstr = cson_value_get_string(orig); |
| 4837 | 4841 | unsigned const int slen = cson_string_length_bytes( jstr ); |
| 4838 | 4842 | assert( NULL != jstr ); |
| 4839 | - v = cson_strdup( cson_string_cstr( jstr ), slen ); | |
| 4843 | + v = cson_strdup( cson_string_cstr( jstr ), slen ); | |
| 4840 | 4844 | break; |
| 4841 | 4845 | } |
| 4842 | 4846 | case CSON_TYPE_INTEGER: { |
| 4843 | 4847 | char buf[BufSize] = {0}; |
| 4844 | 4848 | if( 0 < sprintf( v, "%"CSON_INT_T_PFMT, cson_value_get_integer(orig)) ) |
| @@ -4887,11 +4891,11 @@ | ||
| 4887 | 4891 | } |
| 4888 | 4892 | case CSON_TYPE_STRING: { |
| 4889 | 4893 | cson_string const * jstr = cson_value_get_string(orig); |
| 4890 | 4894 | unsigned const int slen = cson_string_length_bytes( jstr ); |
| 4891 | 4895 | assert( NULL != jstr ); |
| 4892 | - v = cson_strdup( cson_string_cstr( jstr ), slen ); | |
| 4896 | + v = cson_strdup( cson_string_cstr( jstr ), slen ); | |
| 4893 | 4897 | break; |
| 4894 | 4898 | } |
| 4895 | 4899 | case CSON_TYPE_INTEGER: { |
| 4896 | 4900 | char buf[BufSize] = {0}; |
| 4897 | 4901 | if( 0 < sprintf( v, "%"CSON_INT_T_PFMT, cson_value_get_integer(orig)) ) |
| @@ -5351,11 +5355,11 @@ | ||
| 5351 | 5355 | int rc = 0; |
| 5352 | 5356 | int colCount = 0; |
| 5353 | 5357 | assert(st); |
| 5354 | 5358 | colCount = sqlite3_column_count(st); |
| 5355 | 5359 | if( colCount <= 0 ) return NULL; |
| 5356 | - | |
| 5360 | + | |
| 5357 | 5361 | aryV = cson_value_new_array(); |
| 5358 | 5362 | if( ! aryV ) return NULL; |
| 5359 | 5363 | ary = cson_value_get_array(aryV); |
| 5360 | 5364 | assert(ary); |
| 5361 | 5365 | for( i = 0; (0 ==rc) && (i < colCount); ++i ) |
| @@ -5491,11 +5495,11 @@ | ||
| 5491 | 5495 | aryV = NULL; |
| 5492 | 5496 | end: |
| 5493 | 5497 | return aryV; |
| 5494 | 5498 | } |
| 5495 | 5499 | |
| 5496 | - | |
| 5500 | + | |
| 5497 | 5501 | /** |
| 5498 | 5502 | Internal impl of cson_sqlite3_stmt_to_json() when the 'fat' |
| 5499 | 5503 | parameter is non-0. |
| 5500 | 5504 | */ |
| 5501 | 5505 | static int cson_sqlite3_stmt_to_json_fat( sqlite3_stmt * st, cson_value ** tgt ) |
| @@ -5636,11 +5640,11 @@ | ||
| 5636 | 5640 | int rc = sqlite3_prepare_v2( db, sql, -1, &st, NULL ); |
| 5637 | 5641 | if( 0 != rc ) return cson_rc.IOError /* FIXME: Better error code? */; |
| 5638 | 5642 | rc = cson_sqlite3_stmt_to_json( st, tgt, fat ); |
| 5639 | 5643 | sqlite3_finalize( st ); |
| 5640 | 5644 | return rc; |
| 5641 | - } | |
| 5645 | + } | |
| 5642 | 5646 | } |
| 5643 | 5647 | |
| 5644 | 5648 | int cson_sqlite3_bind_value( sqlite3_stmt * st, int ndx, cson_value const * v ) |
| 5645 | 5649 | { |
| 5646 | 5650 | int rc = 0; |
| 5647 | 5651 |
| --- 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 |
+81
-81
| --- src/cson_amalgamation.h | ||
| +++ src/cson_amalgamation.h | ||
| @@ -59,11 +59,11 @@ | ||
| 59 | 59 | #define CSON_INT_T_PFMT "I64d" |
| 60 | 60 | #elif (__STDC_VERSION__ >= 199901L) || (HAVE_LONG_LONG == 1) |
| 61 | 61 | typedef long long cson_int_t; |
| 62 | 62 | #define CSON_INT_T_SFMT "lld" |
| 63 | 63 | #define CSON_INT_T_PFMT "lld" |
| 64 | -#else | |
| 64 | +#else | |
| 65 | 65 | typedef long cson_int_t; |
| 66 | 66 | #define CSON_INT_T_SFMT "ld" |
| 67 | 67 | #define CSON_INT_T_PFMT "ld" |
| 68 | 68 | #endif |
| 69 | 69 | |
| @@ -215,11 +215,11 @@ | ||
| 215 | 215 | Convenience typedef. |
| 216 | 216 | */ |
| 217 | 217 | typedef struct cson_value cson_value; |
| 218 | 218 | |
| 219 | 219 | /** @struct cson_value |
| 220 | - | |
| 220 | + | |
| 221 | 221 | The core value type of this API. It is opaque to clients, and |
| 222 | 222 | only the cson public API should be used for setting or |
| 223 | 223 | inspecting their values. |
| 224 | 224 | |
| 225 | 225 | This class is opaque because stack-based usage can easily cause |
| @@ -231,11 +231,11 @@ | ||
| 231 | 231 | counting) as long as those insertions do not cause cycles. However, |
| 232 | 232 | be very aware that such value re-use uses a reference to the |
| 233 | 233 | original copy, meaning that if its value is changed once, it is |
| 234 | 234 | changed everywhere. Also beware that multi-threaded write |
| 235 | 235 | operations on such references leads to undefined behaviour. |
| 236 | - | |
| 236 | + | |
| 237 | 237 | PLEASE read the ACHTUNGEN below... |
| 238 | 238 | |
| 239 | 239 | ACHTUNG #1: |
| 240 | 240 | |
| 241 | 241 | cson_values MUST NOT form cycles (e.g. via object or array |
| @@ -244,33 +244,33 @@ | ||
| 244 | 244 | Not abiding th Holy Law Of No Cycles will lead to double-frees and |
| 245 | 245 | the like (i.e. undefined behaviour, likely crashes due to infinite |
| 246 | 246 | recursion or stepping on invalid (freed) pointers). |
| 247 | 247 | |
| 248 | 248 | ACHTUNG #2: |
| 249 | - | |
| 249 | + | |
| 250 | 250 | ALL cson_values returned as non-const cson_value pointers from any |
| 251 | 251 | public functions in the cson API are to be treated as if they are |
| 252 | 252 | heap-allocated, and MUST be freed by client by doing ONE of: |
| 253 | - | |
| 253 | + | |
| 254 | 254 | - Passing it to cson_value_free(). |
| 255 | - | |
| 255 | + | |
| 256 | 256 | - Adding it to an Object or Array, in which case the object/array |
| 257 | 257 | takes over ownership. As of 20110323, a value may be inserted into |
| 258 | 258 | a single container multiple times, or into multiple containers, |
| 259 | 259 | in which case they all share ownership (via reference counting) |
| 260 | 260 | of the original value (meaning any changes to it are visible in |
| 261 | 261 | all references to it). |
| 262 | - | |
| 262 | + | |
| 263 | 263 | Each call to cson_value_new_xxx() MUST eventually be followed up |
| 264 | 264 | by one of those options. |
| 265 | - | |
| 265 | + | |
| 266 | 266 | Some cson_value_new_XXX() implementations do not actually allocate |
| 267 | 267 | memory, but this is an internal implementation detail. Client code |
| 268 | 268 | MUST NOT rely on this behaviour and MUST treat each object |
| 269 | 269 | returned by such a function as if it was a freshly-allocated copy |
| 270 | 270 | (even if their pointer addresses are the same). |
| 271 | - | |
| 271 | + | |
| 272 | 272 | ACHTUNG #3: |
| 273 | 273 | |
| 274 | 274 | Note that ACHTUNG #2 tells us that we must always free (or transfer |
| 275 | 275 | ownership of) all pointers returned bycson_value_new_xxx(), but |
| 276 | 276 | that two calls to (e.g.) cson_value_new_bool(1) will (or might) |
| @@ -315,11 +315,11 @@ | ||
| 315 | 315 | if( 0 == rc ) {...success...} |
| 316 | 316 | else if( cson_rc.ArgError == rc ) { ... some argument was wrong ... } |
| 317 | 317 | else if( cson_rc.AllocError == rc ) { ... allocation error ... } |
| 318 | 318 | ... |
| 319 | 319 | @endcode |
| 320 | - | |
| 320 | + | |
| 321 | 321 | The entries named Parse_XXX are generally only returned by |
| 322 | 322 | cson_parse() and friends. |
| 323 | 323 | */ |
| 324 | 324 | |
| 325 | 325 | /** @struct cson_rc_ |
| @@ -472,11 +472,11 @@ | ||
| 472 | 472 | |
| 473 | 473 | /** |
| 474 | 474 | Length, in bytes. |
| 475 | 475 | */ |
| 476 | 476 | unsigned int length; |
| 477 | - | |
| 477 | + | |
| 478 | 478 | /** |
| 479 | 479 | Error code of the parse run (0 for no error). |
| 480 | 480 | */ |
| 481 | 481 | int errorCode; |
| 482 | 482 | |
| @@ -523,11 +523,11 @@ | ||
| 523 | 523 | /** |
| 524 | 524 | Specifies how to indent (or not) output. The values |
| 525 | 525 | are: |
| 526 | 526 | |
| 527 | 527 | (0) == no extra indentation. |
| 528 | - | |
| 528 | + | |
| 529 | 529 | (1) == 1 TAB character for each level. |
| 530 | 530 | |
| 531 | 531 | (>1) == that number of SPACES for each level. |
| 532 | 532 | */ |
| 533 | 533 | unsigned char indentation; |
| @@ -536,11 +536,11 @@ | ||
| 536 | 536 | Maximum object/array depth to traverse. Traversing deeply can |
| 537 | 537 | be indicative of cycles in the object/array tree, and this |
| 538 | 538 | value is used to figure out when to abort the traversal. |
| 539 | 539 | */ |
| 540 | 540 | unsigned short maxDepth; |
| 541 | - | |
| 541 | + | |
| 542 | 542 | /** |
| 543 | 543 | If true, a newline will be added to generated output, |
| 544 | 544 | else not. |
| 545 | 545 | */ |
| 546 | 546 | char addNewline; |
| @@ -636,11 +636,11 @@ | ||
| 636 | 636 | |
| 637 | 637 | Must return 0 on success, non-0 on error (preferably a value from |
| 638 | 638 | cson_rc). |
| 639 | 639 | |
| 640 | 640 | 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. | |
| 642 | 642 | */ |
| 643 | 643 | typedef int (*cson_data_dest_f)( void * state, void const * src, unsigned int n ); |
| 644 | 644 | |
| 645 | 645 | /** |
| 646 | 646 | Reads JSON-formatted string data (in ASCII, UTF8, or UTF16), using the |
| @@ -664,15 +664,15 @@ | ||
| 664 | 664 | used. |
| 665 | 665 | |
| 666 | 666 | The info argument may be NULL. If it is not NULL then the parser |
| 667 | 667 | populates it with information which is useful in error |
| 668 | 668 | reporting. Namely, it contains the line/column of parse errors. |
| 669 | - | |
| 669 | + | |
| 670 | 670 | The srcState argument is ignored by this function but is passed on to src, |
| 671 | 671 | so any output-destination-specific state can be stored there and accessed |
| 672 | 672 | via the src callback. |
| 673 | - | |
| 673 | + | |
| 674 | 674 | Non-parse error conditions include: |
| 675 | 675 | |
| 676 | 676 | - (!tgt) or !src: cson_rc.ArgError |
| 677 | 677 | - cson_rc.AllocError can happen at any time during the input phase |
| 678 | 678 | |
| @@ -725,11 +725,11 @@ | ||
| 725 | 725 | TODOs: |
| 726 | 726 | |
| 727 | 727 | - Buffer the input in larger chunks. We currently read |
| 728 | 728 | byte-by-byte, but i'm too tired to write/test the looping code for |
| 729 | 729 | the buffering. |
| 730 | - | |
| 730 | + | |
| 731 | 731 | @see cson_parse_FILE() |
| 732 | 732 | @see cson_parse_string() |
| 733 | 733 | */ |
| 734 | 734 | int cson_parse( cson_value ** tgt, cson_data_source_f src, void * srcState, |
| 735 | 735 | cson_parse_opt const * opt, cson_parse_info * info ); |
| @@ -786,11 +786,11 @@ | ||
| 786 | 786 | The destState parameter is ignored by this function and is passed |
| 787 | 787 | on to the dest function. |
| 788 | 788 | |
| 789 | 789 | Returns 0 on success. On error, any amount of output might have been |
| 790 | 790 | generated before the error was triggered. |
| 791 | - | |
| 791 | + | |
| 792 | 792 | Example: |
| 793 | 793 | |
| 794 | 794 | @code |
| 795 | 795 | int rc = cson_output( myValue, cson_data_dest_FILE, stdout, NULL ); |
| 796 | 796 | // basically equivalent to: cson_output_FILE( myValue, stdout, NULL ); |
| @@ -925,15 +925,15 @@ | ||
| 925 | 925 | /** |
| 926 | 926 | Converts the given value to a boolean, using JavaScript semantics depending |
| 927 | 927 | on the concrete type of val: |
| 928 | 928 | |
| 929 | 929 | undef or null: false |
| 930 | - | |
| 930 | + | |
| 931 | 931 | boolean: same |
| 932 | - | |
| 932 | + | |
| 933 | 933 | integer, double: 0 or 0.0 == false, else true |
| 934 | - | |
| 934 | + | |
| 935 | 935 | object, array: true |
| 936 | 936 | |
| 937 | 937 | string: length-0 string is false, else true. |
| 938 | 938 | |
| 939 | 939 | Returns 0 on success and assigns *v (if v is not NULL) to either 0 or 1. |
| @@ -945,18 +945,18 @@ | ||
| 945 | 945 | Similar to cson_value_fetch_bool(), but fetches an integer value. |
| 946 | 946 | |
| 947 | 947 | The conversion, if any, depends on the concrete type of val: |
| 948 | 948 | |
| 949 | 949 | NULL, null, undefined: *v is set to 0 and 0 is returned. |
| 950 | - | |
| 950 | + | |
| 951 | 951 | string, object, array: *v is set to 0 and |
| 952 | 952 | cson_rc.TypeError is returned. The error may normally be safely |
| 953 | 953 | ignored, but it is provided for those wanted to know whether a direct |
| 954 | 954 | conversion was possible. |
| 955 | 955 | |
| 956 | 956 | integer: *v is set to the int value and 0 is returned. |
| 957 | - | |
| 957 | + | |
| 958 | 958 | double: *v is set to the value truncated to int and 0 is returned. |
| 959 | 959 | */ |
| 960 | 960 | int cson_value_fetch_integer( cson_value const * val, cson_int_t * v ); |
| 961 | 961 | |
| 962 | 962 | /** |
| @@ -1084,11 +1084,11 @@ | ||
| 1084 | 1084 | |
| 1085 | 1085 | - The maximum number of bytes compared is the lesser of rhsLen and |
| 1086 | 1086 | the length of lhs. If the strings do not match, but compare equal |
| 1087 | 1087 | up to the just-described comparison length, the shorter string is |
| 1088 | 1088 | considered to be less-than the longer one. |
| 1089 | - | |
| 1089 | + | |
| 1090 | 1090 | - If lhs and rhs are both NULL, or both have a length of 0 then they will |
| 1091 | 1091 | compare equal. |
| 1092 | 1092 | |
| 1093 | 1093 | - If lhs is null/length-0 but rhs is not then lhs is considered to be less-than |
| 1094 | 1094 | rhs. |
| @@ -1110,11 +1110,11 @@ | ||
| 1110 | 1110 | Returns the length, in bytes, of str, or 0 if str is NULL. This is |
| 1111 | 1111 | an O(1) operation. |
| 1112 | 1112 | |
| 1113 | 1113 | TODO: add cson_string_length_chars() (is O(N) unless we add another |
| 1114 | 1114 | member to store the char length). |
| 1115 | - | |
| 1115 | + | |
| 1116 | 1116 | @see cson_string_cstr() |
| 1117 | 1117 | */ |
| 1118 | 1118 | unsigned int cson_string_length_bytes( cson_string const * str ); |
| 1119 | 1119 | |
| 1120 | 1120 | /** |
| @@ -1207,11 +1207,11 @@ | ||
| 1207 | 1207 | ar is expanded, if needed, to be able to hold at least (ndx+1) |
| 1208 | 1208 | items, and any new entries created by that expansion are empty |
| 1209 | 1209 | (NULL values). |
| 1210 | 1210 | |
| 1211 | 1211 | On success, 0 is returned and ownership of v is transfered to ar. |
| 1212 | - | |
| 1212 | + | |
| 1213 | 1213 | On error ownership of v is NOT modified, and the caller may still |
| 1214 | 1214 | need to clean it up. For example, the following code will introduce |
| 1215 | 1215 | a leak if this function fails: |
| 1216 | 1216 | |
| 1217 | 1217 | @code |
| @@ -1240,11 +1240,11 @@ | ||
| 1240 | 1240 | |
| 1241 | 1241 | This is functionally equivalent to |
| 1242 | 1242 | cson_array_set(ar,cson_array_length_get(ar),v), but this |
| 1243 | 1243 | implementation has slightly different array-preallocation policy |
| 1244 | 1244 | (it grows more eagerly). |
| 1245 | - | |
| 1245 | + | |
| 1246 | 1246 | Returns 0 on success, non-zero on error. Error cases include: |
| 1247 | 1247 | |
| 1248 | 1248 | - ar or v are NULL: cson_rc.ArgError |
| 1249 | 1249 | |
| 1250 | 1250 | - Array cannot be expanded to hold enough elements: cson_rc.AllocError. |
| @@ -1283,25 +1283,25 @@ | ||
| 1283 | 1283 | cson_value * cson_new_bool(char v); |
| 1284 | 1284 | |
| 1285 | 1285 | /** |
| 1286 | 1286 | Returns the special JSON "null" value. When outputing JSON, |
| 1287 | 1287 | its string representation is "null" (without the quotes). |
| 1288 | - | |
| 1288 | + | |
| 1289 | 1289 | See cson_value_new_bool() for notes regarding the returned |
| 1290 | 1290 | value's memory. |
| 1291 | 1291 | */ |
| 1292 | -cson_value * cson_value_null(); | |
| 1292 | +cson_value * cson_value_null( void ); | |
| 1293 | 1293 | |
| 1294 | 1294 | /** |
| 1295 | 1295 | Equivalent to cson_value_new_bool(1). |
| 1296 | 1296 | */ |
| 1297 | -cson_value * cson_value_true(); | |
| 1297 | +cson_value * cson_value_true( void ); | |
| 1298 | 1298 | |
| 1299 | 1299 | /** |
| 1300 | 1300 | Equivalent to cson_value_new_bool(0). |
| 1301 | 1301 | */ |
| 1302 | -cson_value * cson_value_false(); | |
| 1302 | +cson_value * cson_value_false( void ); | |
| 1303 | 1303 | |
| 1304 | 1304 | /** |
| 1305 | 1305 | Semantically the same as cson_value_new_bool(), but for integers. |
| 1306 | 1306 | */ |
| 1307 | 1307 | cson_value * cson_value_new_integer( cson_int_t v ); |
| @@ -1323,16 +1323,16 @@ | ||
| 1323 | 1323 | |
| 1324 | 1324 | /** |
| 1325 | 1325 | Semantically the same as cson_value_new_bool(), but for strings. |
| 1326 | 1326 | This creates a JSON value which copies the first n bytes of str. |
| 1327 | 1327 | The string will automatically be NUL-terminated. |
| 1328 | - | |
| 1328 | + | |
| 1329 | 1329 | Note that if str is NULL or n is 0, this function still |
| 1330 | 1330 | returns non-NULL value representing that empty string. |
| 1331 | - | |
| 1331 | + | |
| 1332 | 1332 | Returns NULL on allocation error. |
| 1333 | - | |
| 1333 | + | |
| 1334 | 1334 | See cson_value_new_bool() for important information about the |
| 1335 | 1335 | returned memory. |
| 1336 | 1336 | */ |
| 1337 | 1337 | cson_value * cson_value_new_string( char const * str, unsigned int n ); |
| 1338 | 1338 | |
| @@ -1346,32 +1346,32 @@ | ||
| 1346 | 1346 | Post-conditions: cson_value_is_object(value) will return true. |
| 1347 | 1347 | |
| 1348 | 1348 | @see cson_value_new_array() |
| 1349 | 1349 | @see cson_value_free() |
| 1350 | 1350 | */ |
| 1351 | -cson_value * cson_value_new_object(); | |
| 1351 | +cson_value * cson_value_new_object( void ); | |
| 1352 | 1352 | |
| 1353 | 1353 | /** |
| 1354 | 1354 | This works like cson_value_new_object() but returns an Object |
| 1355 | 1355 | handle directly. |
| 1356 | 1356 | |
| 1357 | 1357 | The value handle for the returned object can be fetched with |
| 1358 | 1358 | cson_object_value(theObject). |
| 1359 | - | |
| 1359 | + | |
| 1360 | 1360 | Ownership is transfered to the caller, who must eventually free it |
| 1361 | 1361 | by passing the Value handle (NOT the Object handle) to |
| 1362 | 1362 | cson_value_free() or passing ownership to a parent container. |
| 1363 | 1363 | |
| 1364 | 1364 | Returns NULL on error (out of memory). |
| 1365 | 1365 | */ |
| 1366 | -cson_object * cson_new_object(); | |
| 1366 | +cson_object * cson_new_object( void ); | |
| 1367 | 1367 | |
| 1368 | 1368 | /** |
| 1369 | 1369 | Identical to cson_new_object() except that it creates |
| 1370 | 1370 | an Array. |
| 1371 | 1371 | */ |
| 1372 | -cson_array * cson_new_array(); | |
| 1372 | +cson_array * cson_new_array( void ); | |
| 1373 | 1373 | |
| 1374 | 1374 | /** |
| 1375 | 1375 | Identical to cson_new_object() except that it creates |
| 1376 | 1376 | a String. |
| 1377 | 1377 | */ |
| @@ -1403,11 +1403,11 @@ | ||
| 1403 | 1403 | Post-conditions: cson_value_is_array(value) will return true. |
| 1404 | 1404 | |
| 1405 | 1405 | @see cson_value_new_object() |
| 1406 | 1406 | @see cson_value_free() |
| 1407 | 1407 | */ |
| 1408 | -cson_value * cson_value_new_array(); | |
| 1408 | +cson_value * cson_value_new_array( void ); | |
| 1409 | 1409 | |
| 1410 | 1410 | /** |
| 1411 | 1411 | Frees any resources owned by v, then frees v. If v is a container |
| 1412 | 1412 | type (object or array) its children are also freed (recursively). |
| 1413 | 1413 | |
| @@ -1419,11 +1419,11 @@ | ||
| 1419 | 1419 | cson_value_add_reference(). Even if this function does not |
| 1420 | 1420 | immediately destroy the value, the value must be considered, from |
| 1421 | 1421 | the perspective of that client code, to have been |
| 1422 | 1422 | destroyed/invalidated by this call. |
| 1423 | 1423 | |
| 1424 | - | |
| 1424 | + | |
| 1425 | 1425 | @see cson_value_new_object() |
| 1426 | 1426 | @see cson_value_new_array() |
| 1427 | 1427 | @see cson_value_add_reference() |
| 1428 | 1428 | */ |
| 1429 | 1429 | void cson_value_free(cson_value * v); |
| @@ -1445,11 +1445,11 @@ | ||
| 1445 | 1445 | case, the key is set to the special null value. |
| 1446 | 1446 | |
| 1447 | 1447 | The key may be encoded as ASCII or UTF8. Results are undefined |
| 1448 | 1448 | with other encodings, and the errors won't show up here, but may |
| 1449 | 1449 | show up later, e.g. during output. |
| 1450 | - | |
| 1450 | + | |
| 1451 | 1451 | Returns 0 on success, non-0 on error. It has the following error |
| 1452 | 1452 | cases: |
| 1453 | 1453 | |
| 1454 | 1454 | - cson_rc.ArgError: obj or key are NULL or strlen(key) is 0. |
| 1455 | 1455 | |
| @@ -1498,11 +1498,11 @@ | ||
| 1498 | 1498 | */ |
| 1499 | 1499 | int cson_object_set_s( cson_object * obj, cson_string * key, cson_value * v ); |
| 1500 | 1500 | |
| 1501 | 1501 | /** |
| 1502 | 1502 | Removes a property from an object. |
| 1503 | - | |
| 1503 | + | |
| 1504 | 1504 | If obj contains the given key, it is removed and 0 is returned. If |
| 1505 | 1505 | it is not found, cson_rc.NotFoundError is returned (which can |
| 1506 | 1506 | normally be ignored by client code). |
| 1507 | 1507 | |
| 1508 | 1508 | cson_rc.ArgError is returned if obj or key are NULL or key has |
| @@ -1569,33 +1569,33 @@ | ||
| 1569 | 1569 | |
| 1570 | 1570 | If it finds the given path, it returns the value by assiging *tgt |
| 1571 | 1571 | to it. If tgt is NULL then this function has no side-effects but |
| 1572 | 1572 | will return 0 if the given path is found within the object, so it can be used |
| 1573 | 1573 | to test for existence without fetching it. |
| 1574 | - | |
| 1574 | + | |
| 1575 | 1575 | Returns 0 if it finds an entry, cson_rc.NotFoundError if it finds |
| 1576 | 1576 | no item, and any other non-zero error code on a "real" error. Errors include: |
| 1577 | 1577 | |
| 1578 | 1578 | - obj or path are NULL: cson_rc.ArgError |
| 1579 | - | |
| 1579 | + | |
| 1580 | 1580 | - separator is 0, or path is an empty string or contains only |
| 1581 | 1581 | separator characters: cson_rc.RangeError |
| 1582 | 1582 | |
| 1583 | 1583 | - There is an upper limit on how long a single path component may |
| 1584 | 1584 | be (some "reasonable" internal size), and cson_rc.RangeError is |
| 1585 | 1585 | returned if that length is violated. |
| 1586 | 1586 | |
| 1587 | - | |
| 1587 | + | |
| 1588 | 1588 | Limitations: |
| 1589 | 1589 | |
| 1590 | 1590 | - It has no way to fetch data from arrays this way. i could |
| 1591 | 1591 | imagine, e.g., a path of "subobj.subArray.0" for |
| 1592 | 1592 | subobj.subArray[0], or "0.3.1" for [0][3][1]. But i'm too |
| 1593 | 1593 | lazy/tired to add this. |
| 1594 | 1594 | |
| 1595 | 1595 | Example usage: |
| 1596 | - | |
| 1596 | + | |
| 1597 | 1597 | |
| 1598 | 1598 | Assume we have a JSON structure which abstractly looks like: |
| 1599 | 1599 | |
| 1600 | 1600 | @code |
| 1601 | 1601 | {"subobj":{"subsubobj":{"myValue":[1,2,3]}}} |
| @@ -1613,11 +1613,11 @@ | ||
| 1613 | 1613 | "subobj/subsubobj/myValue" with separator='/' is equivalent the |
| 1614 | 1614 | path "subobj.subsubobj.myValue" with separator='.'. The value of 0 |
| 1615 | 1615 | is not legal as a separator character because we cannot |
| 1616 | 1616 | distinguish that use from the real end-of-string without requiring |
| 1617 | 1617 | the caller to also pass in the length of the string. |
| 1618 | - | |
| 1618 | + | |
| 1619 | 1619 | Multiple successive separators in the list are collapsed into a |
| 1620 | 1620 | single separator for parsing purposes. e.g. the path "a...b...c" |
| 1621 | 1621 | (separator='.') is equivalent to "a.b.c". |
| 1622 | 1622 | |
| 1623 | 1623 | @see cson_object_get_sub() |
| @@ -1700,11 +1700,11 @@ | ||
| 1700 | 1700 | @see cson_object_iter_init() |
| 1701 | 1701 | @see cson_object_iter_next() |
| 1702 | 1702 | */ |
| 1703 | 1703 | struct cson_object_iterator |
| 1704 | 1704 | { |
| 1705 | - | |
| 1705 | + | |
| 1706 | 1706 | /** @internal |
| 1707 | 1707 | The underlying object. |
| 1708 | 1708 | */ |
| 1709 | 1709 | cson_object const * obj; |
| 1710 | 1710 | /** @internal |
| @@ -1771,11 +1771,11 @@ | ||
| 1771 | 1771 | ... |
| 1772 | 1772 | } |
| 1773 | 1773 | @endcode |
| 1774 | 1774 | |
| 1775 | 1775 | There is no need to clean up an iterator, as it holds no dynamic resources. |
| 1776 | - | |
| 1776 | + | |
| 1777 | 1777 | @see cson_kvp_key() |
| 1778 | 1778 | @see cson_kvp_value() |
| 1779 | 1779 | */ |
| 1780 | 1780 | cson_kvp * cson_object_iter_next( cson_object_iterator * iter ); |
| 1781 | 1781 | |
| @@ -1863,11 +1863,11 @@ | ||
| 1863 | 1863 | buf = cson_buffer_empty; |
| 1864 | 1864 | @endcode |
| 1865 | 1865 | |
| 1866 | 1866 | (You might also need to store buf.used and buf.capacity, |
| 1867 | 1867 | depending on what you want to do with the memory.) |
| 1868 | - | |
| 1868 | + | |
| 1869 | 1869 | When doing so, the memory must eventually be passed to free() |
| 1870 | 1870 | to deallocate it. |
| 1871 | 1871 | */ |
| 1872 | 1872 | unsigned char * mem; |
| 1873 | 1873 | }; |
| @@ -1894,11 +1894,11 @@ | ||
| 1894 | 1894 | On error non-zero is returned. Errors include: |
| 1895 | 1895 | |
| 1896 | 1896 | - Invalid arguments: cson_rc.ArgError |
| 1897 | 1897 | |
| 1898 | 1898 | - Buffer cannot be expanded (runs out of memory): cson_rc.AllocError |
| 1899 | - | |
| 1899 | + | |
| 1900 | 1900 | Example usage: |
| 1901 | 1901 | |
| 1902 | 1902 | @code |
| 1903 | 1903 | cson_buffer buf = cson_buffer_empty; |
| 1904 | 1904 | // optional: cson_buffer_reserve(&buf, 1024 * 10); |
| @@ -1918,13 +1918,13 @@ | ||
| 1918 | 1918 | buf = cson_buffer_empty; |
| 1919 | 1919 | ... |
| 1920 | 1920 | free(mem); |
| 1921 | 1921 | } |
| 1922 | 1922 | @endcode |
| 1923 | - | |
| 1923 | + | |
| 1924 | 1924 | @see cson_output() |
| 1925 | - | |
| 1925 | + | |
| 1926 | 1926 | */ |
| 1927 | 1927 | int cson_output_buffer( cson_value const * v, cson_buffer * buf, |
| 1928 | 1928 | cson_output_opt const * opt ); |
| 1929 | 1929 | |
| 1930 | 1930 | /** |
| @@ -1993,11 +1993,11 @@ | ||
| 1993 | 1993 | calling cson_buffer_reserve(dest,0). |
| 1994 | 1994 | |
| 1995 | 1995 | dest->mem might (and possibly will) be (re)allocated by this |
| 1996 | 1996 | function, so any pointers to it held from before this call might be |
| 1997 | 1997 | invalidated by this call. |
| 1998 | - | |
| 1998 | + | |
| 1999 | 1999 | On error non-0 is returned and dest has almost certainly been |
| 2000 | 2000 | modified but its state must be considered incomplete. |
| 2001 | 2001 | |
| 2002 | 2002 | Errors include: |
| 2003 | 2003 | |
| @@ -2042,11 +2042,11 @@ | ||
| 2042 | 2042 | void * mem = buf.mem; |
| 2043 | 2043 | buf = cson_buffer_empty; |
| 2044 | 2044 | @endcode |
| 2045 | 2045 | |
| 2046 | 2046 | In which case the memory must eventually be passed to free() to |
| 2047 | - free it. | |
| 2047 | + free it. | |
| 2048 | 2048 | */ |
| 2049 | 2049 | int cson_buffer_fill_from( cson_buffer * dest, cson_data_source_f src, void * state ); |
| 2050 | 2050 | |
| 2051 | 2051 | |
| 2052 | 2052 | /** |
| @@ -2072,11 +2072,11 @@ | ||
| 2072 | 2072 | for other sharing points which added a reference. |
| 2073 | 2073 | |
| 2074 | 2074 | Normally any such value handles would be invalidated when the |
| 2075 | 2075 | parent container(s) is/are cleaned up, but this function can be |
| 2076 | 2076 | used to effectively delay the cleanup. |
| 2077 | - | |
| 2077 | + | |
| 2078 | 2078 | This function, at its lowest level, increments the value's |
| 2079 | 2079 | reference count by 1. |
| 2080 | 2080 | |
| 2081 | 2081 | To decrement the reference count, pass the value to |
| 2082 | 2082 | cson_value_free(), after which the value must be considered, from |
| @@ -2091,11 +2091,11 @@ | ||
| 2091 | 2091 | errors long before the reference count could overflow (assuming |
| 2092 | 2092 | those reference counts come from container insertions, as opposed |
| 2093 | 2093 | to via this function). |
| 2094 | 2094 | |
| 2095 | 2095 | Insider notes which clients really need to know: |
| 2096 | - | |
| 2096 | + | |
| 2097 | 2097 | For shared/constant value instances, such as those returned by |
| 2098 | 2098 | cson_value_true() and cson_value_null(), this function has no side |
| 2099 | 2099 | effects - it does not actually modify the reference count because |
| 2100 | 2100 | (A) those instances are shared across all client code and (B) those |
| 2101 | 2101 | objects are static and never get cleaned up. However, that is an |
| @@ -2173,11 +2173,11 @@ | ||
| 2173 | 2173 | returned object will be of the same logical type as orig. |
| 2174 | 2174 | |
| 2175 | 2175 | ACHTUNG: if orig contains any cyclic references at any depth level |
| 2176 | 2176 | this function will endlessly recurse. (Having _any_ cyclic |
| 2177 | 2177 | references violates this library's requirements.) |
| 2178 | - | |
| 2178 | + | |
| 2179 | 2179 | Returns NULL if orig is NULL or if cloning fails. Assuming that |
| 2180 | 2180 | orig is in a valid state, the only "likely" error case is that an |
| 2181 | 2181 | allocation fails while constructing the clone. In other words, if |
| 2182 | 2182 | cloning fails due to something other than an allocation error then |
| 2183 | 2183 | either orig is in an invalid state or there is a bug. |
| @@ -2278,11 +2278,11 @@ | ||
| 2278 | 2278 | --key=VAL : Treats VAL as either a double, integer, or string. |
| 2279 | 2279 | |
| 2280 | 2280 | --key= : Treats key as a JSON null (not literal NULL) value. |
| 2281 | 2281 | |
| 2282 | 2282 | Arguments not starting with a dash are skipped. |
| 2283 | - | |
| 2283 | + | |
| 2284 | 2284 | Each key/value pair is inserted into an object. If a given key |
| 2285 | 2285 | appears more than once then only the final entry is actually |
| 2286 | 2286 | stored. |
| 2287 | 2287 | |
| 2288 | 2288 | argc and argv are expected to be values from main() (or similar, |
| @@ -2434,22 +2434,22 @@ | ||
| 2434 | 2434 | Integer, double, null, or string (TEXT and BLOB data, though not |
| 2435 | 2435 | all blob data is legal for a JSON string). |
| 2436 | 2436 | |
| 2437 | 2437 | st must be a sqlite3_step()'d row and col must be a 0-based column |
| 2438 | 2438 | index within that result row. |
| 2439 | - */ | |
| 2439 | + */ | |
| 2440 | 2440 | cson_value * cson_sqlite3_column_to_value( sqlite3_stmt * st, int col ); |
| 2441 | 2441 | |
| 2442 | 2442 | /** |
| 2443 | 2443 | 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 | + | |
| 2446 | 2446 | Returns a new array value on success, which the caller owns. Its elements |
| 2447 | 2447 | are in the same order as in the underlying query. |
| 2448 | 2448 | |
| 2449 | 2449 | On error NULL is returned. |
| 2450 | - | |
| 2450 | + | |
| 2451 | 2451 | st is not traversed or freed by this function - only the column |
| 2452 | 2452 | count and names are read. |
| 2453 | 2453 | */ |
| 2454 | 2454 | cson_value * cson_sqlite3_column_names( sqlite3_stmt * st ); |
| 2455 | 2455 | |
| @@ -2494,22 +2494,22 @@ | ||
| 2494 | 2494 | */ |
| 2495 | 2495 | cson_value * cson_sqlite3_row_to_array( sqlite3_stmt * st ); |
| 2496 | 2496 | /** |
| 2497 | 2497 | Converts the results of an sqlite3 SELECT statement to JSON, |
| 2498 | 2498 | in the form of a cson_value object tree. |
| 2499 | - | |
| 2499 | + | |
| 2500 | 2500 | st must be a prepared, but not yet traversed, SELECT query. |
| 2501 | 2501 | tgt must be a pointer to NULL (see the example below). If |
| 2502 | 2502 | either of those arguments are NULL, cson_rc.ArgError is returned. |
| 2503 | - | |
| 2503 | + | |
| 2504 | 2504 | This walks the query results and returns a JSON object which |
| 2505 | 2505 | has a different structure depending on the value of the 'fat' |
| 2506 | 2506 | argument. |
| 2507 | - | |
| 2508 | - | |
| 2507 | + | |
| 2508 | + | |
| 2509 | 2509 | If 'fat' is 0 then the structure is: |
| 2510 | - | |
| 2510 | + | |
| 2511 | 2511 | @code |
| 2512 | 2512 | { |
| 2513 | 2513 | "columns":["colName1",..."colNameN"], |
| 2514 | 2514 | "rows":[ |
| 2515 | 2515 | [colVal0, ... colValN], |
| @@ -2516,16 +2516,16 @@ | ||
| 2516 | 2516 | [colVal0, ... colValN], |
| 2517 | 2517 | ... |
| 2518 | 2518 | ] |
| 2519 | 2519 | } |
| 2520 | 2520 | @endcode |
| 2521 | - | |
| 2521 | + | |
| 2522 | 2522 | In the "non-fat" format the order of the columns and row values is |
| 2523 | 2523 | guaranteed to be the same as that of the underlying query. |
| 2524 | - | |
| 2524 | + | |
| 2525 | 2525 | If 'fat' is not 0 then the structure is: |
| 2526 | - | |
| 2526 | + | |
| 2527 | 2527 | @code |
| 2528 | 2528 | { |
| 2529 | 2529 | "columns":["colName1",..."colNameN"], |
| 2530 | 2530 | "rows":[ |
| 2531 | 2531 | {"colName1":value1,..."colNameN":valueN}, |
| @@ -2543,33 +2543,33 @@ | ||
| 2543 | 2543 | |
| 2544 | 2544 | On success it returns 0 and assigns *tgt to a newly-allocated |
| 2545 | 2545 | JSON object tree (using the above structure), which the caller owns. |
| 2546 | 2546 | If the query returns no rows, the "rows" value will be an empty |
| 2547 | 2547 | array, as opposed to null. |
| 2548 | - | |
| 2548 | + | |
| 2549 | 2549 | On error non-0 is returned and *tgt is not modified. |
| 2550 | - | |
| 2550 | + | |
| 2551 | 2551 | The error code cson_rc.IOError is used to indicate a db-level |
| 2552 | 2552 | error, and cson_rc.TypeError is returned if sqlite3_column_count(st) |
| 2553 | 2553 | returns 0 or less (indicating an invalid or non-SELECT statement). |
| 2554 | - | |
| 2554 | + | |
| 2555 | 2555 | The JSON data types are determined by the column type as reported |
| 2556 | 2556 | by sqlite3_column_type(): |
| 2557 | - | |
| 2557 | + | |
| 2558 | 2558 | SQLITE_INTEGER: integer |
| 2559 | - | |
| 2559 | + | |
| 2560 | 2560 | SQLITE_FLOAT: double |
| 2561 | - | |
| 2561 | + | |
| 2562 | 2562 | SQLITE_TEXT or SQLITE_BLOB: string, and this will only work if |
| 2563 | 2563 | the data is UTF8 compatible. |
| 2564 | - | |
| 2564 | + | |
| 2565 | 2565 | If the db returns a literal or SQL NULL for a value it is converted |
| 2566 | 2566 | to a JSON null. If it somehow finds a column type it cannot handle, |
| 2567 | 2567 | the value is also converted to a NULL in the output. |
| 2568 | 2568 | |
| 2569 | 2569 | Example |
| 2570 | - | |
| 2570 | + | |
| 2571 | 2571 | @code |
| 2572 | 2572 | cson_value * json = NULL; |
| 2573 | 2573 | int rc = cson_sqlite3_stmt_to_json( myStatement, &json, 1 ); |
| 2574 | 2574 | if( 0 != rc ) { ... error ... } |
| 2575 | 2575 | else { |
| @@ -2603,14 +2603,14 @@ | ||
| 2603 | 2603 | TODO: add Object support for named parameters. |
| 2604 | 2604 | |
| 2605 | 2605 | Returns 0 on success, non-0 on error. |
| 2606 | 2606 | */ |
| 2607 | 2607 | int cson_sqlite3_bind_value( sqlite3_stmt * st, int ndx, cson_value const * v ); |
| 2608 | - | |
| 2608 | + | |
| 2609 | 2609 | #if defined(__cplusplus) |
| 2610 | 2610 | } /*extern "C"*/ |
| 2611 | 2611 | #endif |
| 2612 | - | |
| 2612 | + | |
| 2613 | 2613 | #endif /* CSON_ENABLE_SQLITE3 */ |
| 2614 | 2614 | #endif /* WANDERINGHORSE_NET_CSON_SQLITE3_H_INCLUDED */ |
| 2615 | 2615 | /* end file include/wh/cson/cson_sqlite3.h */ |
| 2616 | 2616 | #endif /* FOSSIL_ENABLE_JSON */ |
| 2617 | 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(); |
| 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 |
M
src/db.c
+186
-38
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -124,19 +124,37 @@ | ||
| 124 | 124 | } aHook[5]; |
| 125 | 125 | char *azDeleteOnFail[3]; /* Files to delete on a failure */ |
| 126 | 126 | char *azBeforeCommit[5]; /* Commands to run prior to COMMIT */ |
| 127 | 127 | int nBeforeCommit; /* Number of entries in azBeforeCommit */ |
| 128 | 128 | 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 */ | |
| 129 | 131 | } db = {0, 0, 0, 0, 0, 0, }; |
| 130 | 132 | |
| 131 | 133 | /* |
| 132 | 134 | ** Arrange for the given file to be deleted on a failure. |
| 133 | 135 | */ |
| 134 | 136 | void db_delete_on_failure(const char *zFilename){ |
| 135 | 137 | assert( db.nDeleteOnFail<count(db.azDeleteOnFail) ); |
| 136 | 138 | db.azDeleteOnFail[db.nDeleteOnFail++] = fossil_strdup(zFilename); |
| 137 | 139 | } |
| 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 | +} | |
| 138 | 156 | |
| 139 | 157 | /* |
| 140 | 158 | ** This routine is called by the SQLite commit-hook mechanism |
| 141 | 159 | ** just prior to each commit. All this routine does is verify |
| 142 | 160 | ** that nBegin really is zero. That insures that transactions |
| @@ -152,24 +170,66 @@ | ||
| 152 | 170 | } |
| 153 | 171 | return 0; |
| 154 | 172 | } |
| 155 | 173 | |
| 156 | 174 | /* |
| 157 | -** Begin and end a nested transaction | |
| 175 | +** Silently add the filename and line number as parameter to each | |
| 176 | +** db_begin_transaction call. | |
| 158 | 177 | */ |
| 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){ | |
| 160 | 189 | if( db.nBegin==0 ){ |
| 161 | 190 | db_multi_exec("BEGIN"); |
| 162 | 191 | sqlite3_commit_hook(g.db, db_verify_at_commit, 0); |
| 163 | 192 | 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); | |
| 164 | 214 | } |
| 165 | 215 | db.nBegin++; |
| 166 | 216 | } |
| 217 | + | |
| 218 | +/* End a transaction previously started using db_begin_transaction() | |
| 219 | +** or db_begin_write(). | |
| 220 | +*/ | |
| 167 | 221 | void db_end_transaction(int rollbackFlag){ |
| 168 | 222 | 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 | + } | |
| 171 | 231 | db.nBegin--; |
| 172 | 232 | if( db.nBegin==0 ){ |
| 173 | 233 | int i; |
| 174 | 234 | if( db.doRollback==0 && db.nPriorChanges<sqlite3_total_changes(g.db) ){ |
| 175 | 235 | i = 0; |
| @@ -180,11 +240,15 @@ | ||
| 180 | 240 | i++; |
| 181 | 241 | } |
| 182 | 242 | leaf_do_pending_checks(); |
| 183 | 243 | } |
| 184 | 244 | 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 | + } | |
| 186 | 250 | } |
| 187 | 251 | while( db.pAllStmt ){ |
| 188 | 252 | db_finalize(db.pAllStmt); |
| 189 | 253 | } |
| 190 | 254 | db_multi_exec("%s", db.doRollback ? "ROLLBACK" : "COMMIT"); |
| @@ -272,11 +336,11 @@ | ||
| 272 | 336 | db.nPrepare++; |
| 273 | 337 | if( flags & DB_PREPARE_PERSISTENT ){ |
| 274 | 338 | prepFlags = SQLITE_PREPARE_PERSISTENT; |
| 275 | 339 | } |
| 276 | 340 | 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 ){ | |
| 278 | 342 | db_err("%s\n%s", sqlite3_errmsg(g.db), zSql); |
| 279 | 343 | } |
| 280 | 344 | pStmt->pNext = pStmt->pPrev = 0; |
| 281 | 345 | pStmt->nStep = 0; |
| 282 | 346 | pStmt->rc = rc; |
| @@ -310,10 +374,31 @@ | ||
| 310 | 374 | db.pAllStmt = pStmt; |
| 311 | 375 | va_end(ap); |
| 312 | 376 | } |
| 313 | 377 | return rc; |
| 314 | 378 | } |
| 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 | + | |
| 315 | 400 | |
| 316 | 401 | /* |
| 317 | 402 | ** Return the index of a bind parameter |
| 318 | 403 | */ |
| 319 | 404 | static int paramIdx(Stmt *pStmt, const char *zParamName){ |
| @@ -428,11 +513,11 @@ | ||
| 428 | 513 | ** Return the rowid of the most recent insert |
| 429 | 514 | */ |
| 430 | 515 | int db_last_insert_rowid(void){ |
| 431 | 516 | i64 x = sqlite3_last_insert_rowid(g.db); |
| 432 | 517 | if( x<0 || x>(i64)2147483647 ){ |
| 433 | - fossil_fatal("rowid out of range (0..2147483647)"); | |
| 518 | + fossil_panic("rowid out of range (0..2147483647)"); | |
| 434 | 519 | } |
| 435 | 520 | return (int)x; |
| 436 | 521 | } |
| 437 | 522 | |
| 438 | 523 | /* |
| @@ -479,10 +564,16 @@ | ||
| 479 | 564 | return mprintf("%s", db_column_text(pStmt, N)); |
| 480 | 565 | } |
| 481 | 566 | void db_column_blob(Stmt *pStmt, int N, Blob *pBlob){ |
| 482 | 567 | blob_append(pBlob, sqlite3_column_blob(pStmt->pStmt, N), |
| 483 | 568 | 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; | |
| 484 | 575 | } |
| 485 | 576 | |
| 486 | 577 | /* |
| 487 | 578 | ** Initialize a blob to an ephemeral copy of the content of a |
| 488 | 579 | ** column in the current row. The data in the blob will become |
| @@ -875,10 +966,33 @@ | ||
| 875 | 966 | }else{ |
| 876 | 967 | sqlite3_result_text(context, "utc", -1, SQLITE_STATIC); |
| 877 | 968 | } |
| 878 | 969 | } |
| 879 | 970 | |
| 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 | +} | |
| 880 | 994 | |
| 881 | 995 | /* |
| 882 | 996 | ** Register the SQL functions that are useful both to the internal |
| 883 | 997 | ** representation and to the "fossil sql" command. |
| 884 | 998 | */ |
| @@ -893,10 +1007,12 @@ | ||
| 893 | 1007 | db_now_function, 0, 0); |
| 894 | 1008 | sqlite3_create_function(db, "toLocal", 0, SQLITE_UTF8, 0, |
| 895 | 1009 | db_tolocal_function, 0, 0); |
| 896 | 1010 | sqlite3_create_function(db, "fromLocal", 0, SQLITE_UTF8, 0, |
| 897 | 1011 | db_fromlocal_function, 0, 0); |
| 1012 | + sqlite3_create_function(db, "hextoblob", 1, SQLITE_UTF8, 0, | |
| 1013 | + db_hextoblob, 0, 0); | |
| 898 | 1014 | } |
| 899 | 1015 | |
| 900 | 1016 | #if USE_SEE |
| 901 | 1017 | /* |
| 902 | 1018 | ** This is a pointer to the saved database encryption key string. |
| @@ -939,11 +1055,11 @@ | ||
| 939 | 1055 | blobSize = blob_size(pKey); |
| 940 | 1056 | if( blobSize==0 ) return; |
| 941 | 1057 | fossil_get_page_size(&pageSize); |
| 942 | 1058 | assert( pageSize>0 ); |
| 943 | 1059 | 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); | |
| 945 | 1061 | } |
| 946 | 1062 | p = fossil_secure_alloc_page(&n); |
| 947 | 1063 | assert( p!=NULL ); |
| 948 | 1064 | assert( n==pageSize ); |
| 949 | 1065 | assert( n>=blobSize ); |
| @@ -973,11 +1089,11 @@ | ||
| 973 | 1089 | size_t blobSize = blob_size(pKey); |
| 974 | 1090 | if( blobSize==0 ){ |
| 975 | 1091 | db_unsave_encryption_key(); |
| 976 | 1092 | }else{ |
| 977 | 1093 | if( blobSize>savedKeySize ){ |
| 978 | - fossil_fatal("key blob too large: %u versus %u", | |
| 1094 | + fossil_panic("key blob too large: %u versus %u", | |
| 979 | 1095 | blobSize, savedKeySize); |
| 980 | 1096 | } |
| 981 | 1097 | fossil_secure_zero(zSavedKey, savedKeySize); |
| 982 | 1098 | memcpy(zSavedKey, blob_str(pKey), blobSize); |
| 983 | 1099 | } |
| @@ -1003,11 +1119,11 @@ | ||
| 1003 | 1119 | HANDLE hProcess = NULL; |
| 1004 | 1120 | |
| 1005 | 1121 | fossil_get_page_size(&pageSize); |
| 1006 | 1122 | assert( pageSize>0 ); |
| 1007 | 1123 | 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); | |
| 1009 | 1125 | } |
| 1010 | 1126 | p = fossil_secure_alloc_page(&n); |
| 1011 | 1127 | assert( p!=NULL ); |
| 1012 | 1128 | assert( n==pageSize ); |
| 1013 | 1129 | assert( n>=nSize ); |
| @@ -1019,20 +1135,20 @@ | ||
| 1019 | 1135 | if( nRead==nSize ){ |
| 1020 | 1136 | db_unsave_encryption_key(); |
| 1021 | 1137 | zSavedKey = p; |
| 1022 | 1138 | savedKeySize = n; |
| 1023 | 1139 | }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", | |
| 1025 | 1141 | nRead, nSize, pAddress, processId); |
| 1026 | 1142 | } |
| 1027 | 1143 | }else{ |
| 1028 | 1144 | 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, | |
| 1030 | 1146 | pAddress, processId, GetLastError()); |
| 1031 | 1147 | } |
| 1032 | 1148 | }else{ |
| 1033 | - fossil_fatal("failed to open pid %lu: %lu", processId, GetLastError()); | |
| 1149 | + fossil_panic("failed to open pid %lu: %lu", processId, GetLastError()); | |
| 1034 | 1150 | } |
| 1035 | 1151 | } |
| 1036 | 1152 | #endif /* defined(_WIN32) */ |
| 1037 | 1153 | #endif /* USE_SEE */ |
| 1038 | 1154 | |
| @@ -1092,10 +1208,17 @@ | ||
| 1092 | 1208 | LOCAL sqlite3 *db_open(const char *zDbName){ |
| 1093 | 1209 | int rc; |
| 1094 | 1210 | sqlite3 *db; |
| 1095 | 1211 | |
| 1096 | 1212 | 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 | + } | |
| 1097 | 1220 | rc = sqlite3_open_v2( |
| 1098 | 1221 | zDbName, &db, |
| 1099 | 1222 | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, |
| 1100 | 1223 | g.zVfsName |
| 1101 | 1224 | ); |
| @@ -1170,11 +1293,11 @@ | ||
| 1170 | 1293 | ** After calling this routine, db_database_slot(zLabel) should |
| 1171 | 1294 | ** return 0. |
| 1172 | 1295 | */ |
| 1173 | 1296 | void db_set_main_schemaname(sqlite3 *db, const char *zLabel){ |
| 1174 | 1297 | 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 " | |
| 1176 | 1299 | "SQLITE_DBCONFIG_MAINDBNAME interface."); |
| 1177 | 1300 | } |
| 1178 | 1301 | } |
| 1179 | 1302 | |
| 1180 | 1303 | /* |
| @@ -1227,12 +1350,14 @@ | ||
| 1227 | 1350 | sqlite3_wal_checkpoint(g.dbConfig, 0); |
| 1228 | 1351 | sqlite3_close(g.dbConfig); |
| 1229 | 1352 | g.dbConfig = 0; |
| 1230 | 1353 | g.zConfigDbName = 0; |
| 1231 | 1354 | }else if( g.db && 0==iSlot ){ |
| 1355 | + int rc; | |
| 1232 | 1356 | 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); | |
| 1234 | 1359 | g.db = 0; |
| 1235 | 1360 | g.zConfigDbName = 0; |
| 1236 | 1361 | } |
| 1237 | 1362 | } |
| 1238 | 1363 | |
| @@ -1269,27 +1394,27 @@ | ||
| 1269 | 1394 | } |
| 1270 | 1395 | } |
| 1271 | 1396 | } |
| 1272 | 1397 | if( zHome==0 ){ |
| 1273 | 1398 | if( isOptional ) return 0; |
| 1274 | - fossil_fatal("cannot locate home directory - please set the " | |
| 1399 | + fossil_panic("cannot locate home directory - please set the " | |
| 1275 | 1400 | "FOSSIL_HOME, LOCALAPPDATA, APPDATA, or HOMEPATH " |
| 1276 | 1401 | "environment variables"); |
| 1277 | 1402 | } |
| 1278 | 1403 | #else |
| 1279 | 1404 | if( zHome==0 ){ |
| 1280 | 1405 | zHome = fossil_getenv("HOME"); |
| 1281 | 1406 | } |
| 1282 | 1407 | if( zHome==0 ){ |
| 1283 | 1408 | if( isOptional ) return 0; |
| 1284 | - fossil_fatal("cannot locate home directory - please set the " | |
| 1409 | + fossil_panic("cannot locate home directory - please set the " | |
| 1285 | 1410 | "FOSSIL_HOME or HOME environment variables"); |
| 1286 | 1411 | } |
| 1287 | 1412 | #endif |
| 1288 | 1413 | if( file_isdir(zHome, ExtFILE)!=1 ){ |
| 1289 | 1414 | if( isOptional ) return 0; |
| 1290 | - fossil_fatal("invalid home directory: %s", zHome); | |
| 1415 | + fossil_panic("invalid home directory: %s", zHome); | |
| 1291 | 1416 | } |
| 1292 | 1417 | #if defined(_WIN32) || defined(__CYGWIN__) |
| 1293 | 1418 | /* . filenames give some window systems problems and many apps problems */ |
| 1294 | 1419 | zDbName = mprintf("%//_fossil", zHome); |
| 1295 | 1420 | #else |
| @@ -1296,17 +1421,17 @@ | ||
| 1296 | 1421 | zDbName = mprintf("%s/.fossil", zHome); |
| 1297 | 1422 | #endif |
| 1298 | 1423 | if( file_size(zDbName, ExtFILE)<1024*3 ){ |
| 1299 | 1424 | if( file_access(zHome, W_OK) ){ |
| 1300 | 1425 | if( isOptional ) return 0; |
| 1301 | - fossil_fatal("home directory %s must be writeable", zHome); | |
| 1426 | + fossil_panic("home directory %s must be writeable", zHome); | |
| 1302 | 1427 | } |
| 1303 | 1428 | db_init_database(zDbName, zConfigSchema, (char*)0); |
| 1304 | 1429 | } |
| 1305 | 1430 | if( file_access(zDbName, W_OK) ){ |
| 1306 | 1431 | if( isOptional ) return 0; |
| 1307 | - fossil_fatal("configuration file %s must be writeable", zDbName); | |
| 1432 | + fossil_panic("configuration file %s must be writeable", zDbName); | |
| 1308 | 1433 | } |
| 1309 | 1434 | if( useAttach ){ |
| 1310 | 1435 | db_open_or_attach(zDbName, "configdb"); |
| 1311 | 1436 | g.dbConfig = 0; |
| 1312 | 1437 | }else{ |
| @@ -1524,16 +1649,19 @@ | ||
| 1524 | 1649 | } |
| 1525 | 1650 | } |
| 1526 | 1651 | g.zRepositoryName = mprintf("%s", zDbName); |
| 1527 | 1652 | db_open_or_attach(g.zRepositoryName, "repository"); |
| 1528 | 1653 | 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); | |
| 1532 | 1656 | /* Cache "allow-symlinks" option, because we'll need it on every stat call */ |
| 1533 | 1657 | g.allowSymlinks = db_get_boolean("allow-symlinks", |
| 1534 | 1658 | 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 | |
| 1535 | 1663 | g.zAuxSchema = db_get("aux-schema",""); |
| 1536 | 1664 | g.eHashPolicy = db_get_int("hash-policy",-1); |
| 1537 | 1665 | if( g.eHashPolicy<0 ){ |
| 1538 | 1666 | g.eHashPolicy = hname_default_policy(); |
| 1539 | 1667 | db_set_int("hash-policy", g.eHashPolicy, 0); |
| @@ -1542,10 +1670,25 @@ | ||
| 1542 | 1670 | /* Make a change to the CHECK constraint on the BLOB table for |
| 1543 | 1671 | ** version 2.0 and later. |
| 1544 | 1672 | */ |
| 1545 | 1673 | rebuild_schema_update_2_0(); /* Do the Fossil-2.0 schema updates */ |
| 1546 | 1674 | } |
| 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 | +} | |
| 1547 | 1690 | |
| 1548 | 1691 | /* |
| 1549 | 1692 | ** Flags for the db_find_and_open_repository() function. |
| 1550 | 1693 | */ |
| 1551 | 1694 | #if INTERFACE |
| @@ -1 |